fix(mcp-server): 3 A2A pipeline bug fixes from audit

- nomgap-client: add electrolyte_reminder + extended_fast_warning to PushTriggerType; rename scheduledAt → scheduledFor to match backend schema; fix startedAt type string → number (backend stores epoch ms)
- nomgap-tools: rename scheduledAt → scheduledFor in nomgap.push.fire tool schema + execute call
- safety-monitor-pipeline: fix FastStateResult.startedAt type number; use epoch ms directly instead of new Date(string)
- sync-diagnostics-pipeline: remove peakpulseGetStats (returns aggregate stats not sync queue); derive queueDepth from recentFailureCount telemetry; add lastSuccessfulSync derivation from events
This commit is contained in:
saravanakumardb1 2026-03-05 14:54:21 -08:00
parent a7d8a58348
commit 7e47151918
6 changed files with 577 additions and 30 deletions

View File

@ -41,18 +41,18 @@
### 2.3 API & Clients
| Package | Purpose | Exports | Consumers |
| ------------------------------- | ------------------------- | --------------------------------------- | ------------- |
| `@bytelyst/api-client` | Typed fetch wrapper | `createApiClient()` with auth injection | Web + RN |
| `@bytelyst/platform-client` | Platform service client | Typed fetch with 401 retry | Web + RN |
| `@bytelyst/broadcast-client` | Broadcast/survey client | Poll and broadcast APIs | iOS + Android |
| `@bytelyst/survey-client` | Survey response client | Submit survey responses | iOS + Android |
| `@bytelyst/telemetry-client` | Telemetry ingestion | `createTelemetryClient()` with batching | All platforms |
| `@bytelyst/feature-flag-client` | Feature flag polling | `createFeatureFlagClient()` | All platforms |
| `@bytelyst/kill-switch-client` | Kill switch check | `createKillSwitchClient()` (fail-open) | All platforms |
| `@bytelyst/offline-queue` | Persistent retry queue | Configurable storage adapter | Web + RN |
| `@bytelyst/diagnostics-client` | Remote diagnostics client | Error reporting, log upload | All platforms |
| `@bytelyst/feedback-client` | In-app feedback | Submit feedback + attachments | iOS + Android |
| Package | Purpose | Exports | Consumers |
| ------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------- |
| `@bytelyst/api-client` | Typed fetch wrapper | `createApiClient()` with auth injection | Web + RN |
| `@bytelyst/platform-client` | Platform service client | Typed fetch with 401 retry | Web + RN |
| `@bytelyst/broadcast-client` | Broadcast/survey client | Poll and broadcast APIs | iOS + Android |
| `@bytelyst/survey-client` | Survey response client | Submit survey responses | iOS + Android |
| `@bytelyst/telemetry-client` | Telemetry ingestion | `createTelemetryClient()` with batching | All platforms |
| `@bytelyst/feature-flag-client` | Feature flag polling | `createFeatureFlagClient()` | All platforms |
| `@bytelyst/kill-switch-client` | Kill switch check | `createKillSwitchClient()` (fail-open) | All platforms |
| `@bytelyst/offline-queue` | Persistent retry queue | Configurable storage adapter | Web + RN |
| `@bytelyst/diagnostics-client` | Admin-triggered remote debug sessions | Session polling, trace spans, breadcrumbs, network/console/error capture, batch flush | ✅ All 6 products (web + iOS + RN) |
| `@bytelyst/feedback-client` | In-app feedback | Submit feedback + attachments | iOS + Android |
### 2.4 Storage & Data

547
scripts/update-agent-docs.sh Executable file
View File

@ -0,0 +1,547 @@
#!/usr/bin/env bash
# scripts/update-agent-docs.sh
#
# Regenerates AI agent config files across all repos listed in repos.txt.
#
# Files always regenerated (overwritten):
# .cursorrules, .windsurfrules, .clinerules, .aider.conf.yml,
# .editorconfig, .github/copilot-instructions.md
#
# Files created only if missing (never overwritten):
# CLAUDE.md — hand-crafted summary; script creates a placeholder if absent
#
# Files never touched:
# AGENTS.md — canonical source of truth; must be maintained manually
#
# Usage:
# ./scripts/update-agent-docs.sh # regenerate + commit
# ./scripts/update-agent-docs.sh --dry-run # show what would change, no writes
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPOS_TXT="${SCRIPT_DIR}/../.windsurf/workflows/repos.txt"
BASE_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
DRY_RUN=false
CHANGED_REPOS=()
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
# ── colours ────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
header(){ echo -e "\n${CYAN}══ $* ══${NC}"; }
# ── per-repo single-line metadata ──────────────────────────────────────────
set_meta() {
NAME="" ID="" TAGLINE="" STACK="" BUILD_VFY="" LINT1="" LINT2="" LINT3="" AIDER_READ2="README.md"
case "$1" in
learning_ai_common_plat)
NAME="@bytelyst Common Platform"
ID="(product-agnostic)"
TAGLINE="Shared packages + microservices for the ByteLyst ecosystem"
STACK="TypeScript, ESM, pnpm workspace, Fastify 5, Vitest, Azure Cosmos DB"
BUILD_VFY="pnpm build && pnpm test && pnpm typecheck"
LINT1="pnpm build 2>&1 | tail -10"
LINT2="pnpm test 2>&1 | tail -10"
LINT3="pnpm typecheck 2>&1 | tail -10"
AIDER_READ2="README.md"
;;
learning_voice_ai_agent)
NAME="LysnrAI"
ID="lysnrai"
TAGLINE="Cross-platform voice-to-text dictation platform"
STACK="Python 3.12 (desktop/backend) + Next.js 16 (dashboards) + Fastify 5 (microservices in sibling repo)"
BUILD_VFY="python -m pytest tests/ -v --tb=short"
LINT1="cd user-dashboard-web && npx tsc --noEmit 2>&1 | tail -10"
LINT2="python -m pytest tests/ -x --tb=short 2>&1 | tail -10"
AIDER_READ2="README_MONO_REPO.md"
;;
learning_multimodal_memory_agents)
NAME="MindLyst"
ID="mindlyst"
TAGLINE="Role-Based Life OS — AI-powered multimodal second brain"
STACK="KMP (shared) + SwiftUI (iOS) + Jetpack Compose (Android) + Next.js 16 (web)"
BUILD_VFY="./gradlew :shared:compileKotlinIosSimulatorArm64"
LINT1="cd mindlyst-native && ./gradlew :shared:compileKotlinIosSimulatorArm64 2>&1 | tail -5"
LINT2="cd mindlyst-native/web && npx next build 2>&1 | tail -10"
AIDER_READ2="ARCHITECTURE.md"
;;
learning_ai_clock)
NAME="ChronoMind"
ID="chronomind"
TAGLINE="AI-Powered Contextual Clock & Timer"
STACK="Next.js 16 (web) + SwiftUI (iOS/Watch/Mac) + Jetpack Compose (Android) + Fastify 5 (backend)"
BUILD_VFY="cd web && npm test && npm run typecheck && npm run build"
LINT1="cd web && npm test 2>&1 | tail -10"
LINT2="cd web && npm run typecheck 2>&1 | tail -10"
AIDER_READ2="docs/PRD.md"
;;
learning_ai_fastgap)
NAME="NomGap"
ID="nomgap"
TAGLINE="Fasting visualization and coaching app"
STACK="React Native (Expo SDK 55) + TypeScript + Fastify 5 (backend)"
BUILD_VFY="npm test && npm run typecheck"
LINT1="npm test 2>&1 | tail -10"
LINT2="npm run typecheck 2>&1 | tail -10"
AIDER_READ2="docs/PRD.md"
;;
learning_ai_jarvis_jr)
NAME="JarvisJr"
ID="jarvisjr"
TAGLINE="Voice-first multi-agent coaching platform"
STACK="SwiftUI (iOS/Watch/Mac) + Next.js 16 (web) + Jetpack Compose (Android) + Fastify 5 (backend)"
BUILD_VFY="cd web && npm test && npm run typecheck && npm run build"
LINT1="cd web && npm test 2>&1 | tail -10"
LINT2="cd web && npm run typecheck 2>&1 | tail -10"
AIDER_READ2="docs/ENHANCED_IDEA_v2.md"
;;
learning_ai_peakpulse)
NAME="PeakPulse"
ID="peakpulse"
TAGLINE="Sensor-driven adventure tracker for hikers and skiers"
STACK="SwiftUI (iOS 17+), SwiftData, MapKit, WeatherKit, ActivityKit, WidgetKit, App Intents"
BUILD_VFY="cd ios && xcodegen generate && xcodebuild -scheme PeakPulse -sdk iphonesimulator build"
LINT1="cd ios && xcodebuild -scheme PeakPulse -sdk iphonesimulator build 2>&1 | tail -20"
AIDER_READ2="docs/PRD.md"
;;
*)
warn "Unknown repo: $1 — skipping"
return 1
;;
esac
}
# ── per-repo multi-line content ─────────────────────────────────────────────
repo_rules() {
case "$1" in
learning_ai_common_plat)
echo "- Package manager: pnpm — NEVER use npm"
echo "- ESM everywhere: \"type\": \"module\", .js extensions in imports"
echo "- Fastify module pattern: types.ts → repository.ts → routes.ts"
echo "- Use req.log / app.log — never console.log"
echo "- Every Cosmos document MUST include a productId field"
echo "- peerDependencies for heavy deps; workspace:* for inter-package deps"
echo "- Services re-export @bytelyst/* in src/lib/ for clean internal imports"
;;
learning_voice_ai_agent)
echo "- Python: structlog (never print), ruff lint, type hints required, pydantic-settings"
echo "- TypeScript: App Router (not Pages), @bytelyst/api-client for service clients"
echo "- All Cosmos documents MUST include productId field"
echo "- Use PRODUCT_ID from @bytelyst/config — never hardcode \"lysnrai\""
echo "- Dashboards: TailwindCSS v4, shadcn/ui, Recharts"
echo "- Desktop build requires --webpack flag for Next.js"
;;
learning_multimodal_memory_agents)
echo "- ALL business logic in shared/src/commonMain/ — NEVER in platform UI code"
echo "- Android/iOS/Web are thin UI shells consuming shared repositories via StateFlow"
echo "- Koin for DI — register all new deps in SharedModule.kt"
echo "- MindLystTokens.kt is the single source of design tokens"
echo "- Web: Pages Router, vanilla CSS with --ml-* props, NO Tailwind"
echo "- Use compilerOptions {} (not deprecated kotlinOptions {})"
echo "- Use StateFlow for observable state, never LiveData"
;;
learning_ai_clock)
echo "- Web engine logic in web/src/lib/ — pure TS, no React imports"
echo "- Components in web/src/components/ — React UI only"
echo "- iOS shared logic in ios/ChronoMind/Shared/ — consumed by all Apple targets"
echo "- Android engine in android/.../engine/ — pure Kotlin, no Android framework deps"
echo "- Web build: must use --webpack flag (Serwist incompatible with Turbopack)"
echo "- Every Cosmos doc: productId: \"chronomind\""
echo "- Theme: --cm-* CSS custom properties (web), ChronoMindTheme (native)"
;;
learning_ai_fastgap)
echo "- All engine logic in src/lib/ — zero React Native imports"
echo "- Screens are thin composites wiring store + engines + components"
echo "- Theme tokens from src/theme/ — never hardcode colors, spacing, or fonts"
echo "- Expo native modules mocked in __mocks__/ for Vitest"
echo "- Engine modules are pure functions — testable without mocking RN"
echo "- Every Cosmos doc: productId: \"nomgap\""
;;
learning_ai_jarvis_jr)
echo "- iOS shared logic in ios/JarvisJr/Shared/ — consumed by iOS, Watch, Mac"
echo "- Web engine in web/src/lib/ — pure TS, no React imports"
echo "- Web components in web/src/components/ — React UI only"
echo "- Android engine in android/.../engine/ — pure Kotlin, no Android framework deps"
echo "- Web build: must use --webpack flag (Serwist incompatible with Turbopack)"
echo "- Never use print() in Swift — use os.Logger"
echo "- Theme: --jj-* CSS custom properties (web), JarvisJrTheme (native)"
echo "- Every Cosmos doc: productId: \"jarvisjr\""
;;
learning_ai_peakpulse)
echo "- Models in ios/PeakPulse/Models/ — SwiftData @Model + Codable structs"
echo "- Services in ios/PeakPulse/Services/ — pure Swift engine logic"
echo "- ViewModels in ios/PeakPulse/ViewModels/ — @Observable MVVM"
echo "- Platform/ files are thin wrappers over ByteLystPlatformSDK — never reimplement"
echo "- All logging via os.Logger — never use print()"
echo "- All colors from PeakPulseTheme — never hardcode"
echo "- Every Cosmos doc: productId: \"peakpulse\""
;;
esac
}
repo_paths() {
case "$1" in
learning_ai_common_plat)
echo "- packages/ — @bytelyst/* shared libraries (errors, cosmos, config, auth, api-client, fastify-core, react-auth, logger, testing, blob, extraction, monitoring, design-tokens)"
echo "- services/platform-service/ — consolidated platform service (port 4003)"
echo "- services/extraction-service/ — text extraction + Python sidecar (port 4005)"
echo "- dashboards/admin-web/ — admin console (port 3001)"
echo "- dashboards/tracker-web/ — issue tracker (port 3003)"
;;
learning_voice_ai_agent)
echo "- src/ — Desktop app (Python 3.12, tkinter, Azure Speech SDK)"
echo "- backend/ — Fastify 5 + TypeScript backend (port 4015)"
echo "- user-dashboard-web/ — Next.js 16 user portal (port 3002)"
echo "- ../learning_ai_common_plat/services/ — platform-service (4003), extraction-service (4005)"
echo "- ../learning_ai_common_plat/dashboards/ — admin-web (3001), tracker-web (3003)"
echo "- mobile_app/ios/ — Swift + SwiftUI"
echo "- mobile_app/android/ — Kotlin + Jetpack Compose"
;;
learning_multimodal_memory_agents)
echo "- mindlyst-native/shared/src/commonMain/ — KMP shared logic (ALL business logic)"
echo "- mindlyst-native/androidApp/ — Jetpack Compose Android app"
echo "- mindlyst-native/iosApp/ — SwiftUI iOS app"
echo "- mindlyst-native/web/ — Next.js 16 web dashboard"
echo "- backend/ — Fastify 5 + TypeScript backend (port 4014)"
echo "- design-system/ — design tokens source"
;;
learning_ai_clock)
echo "- web/src/lib/ — Pure TS engine modules (timer, cascade, urgency, nl-parser, etc.)"
echo "- web/src/components/ — React UI components"
echo "- ios/ChronoMind/Shared/ — Shared Swift code (iOS + Watch + Mac + Widgets)"
echo "- android/app/.../engine/ — Pure Kotlin engine"
echo "- backend/ — Fastify 5 + TypeScript backend (port 4011)"
;;
learning_ai_fastgap)
echo "- src/lib/ — Pure engine modules (NO React Native deps)"
echo "- src/screens/ — Screen components (thin UI composites)"
echo "- src/components/ — Reusable UI components"
echo "- src/store/ — Zustand store"
echo "- src/theme/ — Design tokens (colors, typography, spacing)"
echo "- backend/ — Fastify 5 + TypeScript backend (port 4013)"
;;
learning_ai_jarvis_jr)
echo "- ios/JarvisJr/Shared/ — Cross-platform Swift code (iOS + Watch + Mac)"
echo "- web/src/lib/ — Pure TS engine + API clients"
echo "- web/src/app/ — Next.js App Router pages"
echo "- android/app/.../engine/ — Pure Kotlin engine"
echo "- backend/ — Fastify 5 + TypeScript backend (port 4012)"
;;
learning_ai_peakpulse)
echo "- ios/PeakPulse/Models/ — SwiftData models"
echo "- ios/PeakPulse/Services/ — Pure Swift engine logic"
echo "- ios/PeakPulse/ViewModels/ — @Observable MVVM"
echo "- ios/PeakPulse/Views/ — SwiftUI screens + components"
echo "- ios/PeakPulse/Platform/ — ByteLystPlatformSDK thin wrappers"
echo "- backend/ — Fastify 5 + TypeScript backend (port 4010)"
;;
esac
}
# ── file generators ─────────────────────────────────────────────────────────
write_editorconfig() {
cat > "$1" << 'EOF'
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
EOF
}
write_cursorrules() {
local dest="$1" repo="$2"
local RULES PATHS
RULES=$(repo_rules "$repo")
PATHS=$(repo_paths "$repo")
cat > "$dest" << EOF
# ${NAME} — Cursor Rules
# Read AGENTS.md for full context.
Project: ${NAME}${TAGLINE}
Product ID: ${ID}
Stack: ${STACK}
## Architecture
${PATHS}
## Rules
${RULES}
- Commits: feat(scope): description / fix(scope): description
- Never delete existing comments/documentation unless asked
- Never add emojis unless asked
- Never hardcode secrets, colors, or API URLs
## Build Verification
- ${BUILD_VFY}
## Key Documents
- AGENTS.md — Full AI agent instructions (read this first)
EOF
}
write_windsurfrules() {
local dest="$1" repo="$2"
local RULES PATHS
RULES=$(repo_rules "$repo")
PATHS=$(repo_paths "$repo")
cat > "$dest" << EOF
# ${NAME} — Windsurf / Codeium Rules
# Read AGENTS.md for full context. These are the critical rules.
Project: ${NAME}${TAGLINE}
Stack: ${STACK}
## Architecture Rules
${PATHS}
## Conventions
${RULES}
- Commits: feat(scope): description / fix(scope): description
## Build Verification
- ${BUILD_VFY}
EOF
}
write_clinerules() {
local dest="$1" repo="$2"
local RULES PATHS
RULES=$(repo_rules "$repo")
PATHS=$(repo_paths "$repo")
cat > "$dest" << EOF
# .clinerules — Cline / Roo Code Rules for ${NAME}
# Read AGENTS.md for the complete onboarding guide.
## Project: ${NAME} — ${TAGLINE}
Architecture: ${STACK}
## Mandatory Rules
${RULES}
- Commits: \`type(scope): description\`
- After changes, verify: \`${BUILD_VFY}\`
## Key File Locations
${PATHS}
- Full guide: \`AGENTS.md\`
EOF
}
write_aider_conf() {
local dest="$1"
# Build lint-cmd list (skip empty entries)
local lint_block=""
for cmd in "${LINT1:-}" "${LINT2:-}" "${LINT3:-}"; do
[[ -z "$cmd" ]] && continue
lint_block+=" - '${cmd}'"$'\n'
done
# Build read list
local read_block=" - AGENTS.md"$'\n'
[[ -n "${AIDER_READ2:-}" ]] && read_block+=" - ${AIDER_READ2}"$'\n'
cat > "$dest" << EOF
# .aider.conf.yml — Aider Configuration for ${NAME}
# Helps Aider understand the project structure and conventions.
read:
${read_block}
conventions: AGENTS.md
lint-cmd:
${lint_block}
auto-commits: false
EOF
}
write_copilot_instructions() {
local dest_dir="$1" repo="$2"
local RULES PATHS
RULES=$(repo_rules "$repo")
PATHS=$(repo_paths "$repo")
mkdir -p "$dest_dir"
cat > "${dest_dir}/copilot-instructions.md" << EOF
# GitHub Copilot Instructions — ${NAME}
> For full agent instructions, read [\`AGENTS.md\`](../AGENTS.md) at the repo root.
## Project Context
**${NAME}** — ${TAGLINE}.
Stack: ${STACK}
## Code Generation Rules
### Always
${RULES}
- Include \`productId: "${ID}"\` in every Cosmos DB document
- Use \`type(scope): description\` commit message format
- Fix source code, not tests (unless the test itself is wrong)
### Never
- \`console.log\` or \`print()\` in production code
- Hardcoded secrets, API keys, colors, or URLs
- Delete existing comments or documentation unless explicitly asked
- Add emojis unless asked
## Key Paths
${PATHS}
## Build Verification
\`\`\`bash
${BUILD_VFY}
\`\`\`
EOF
}
write_claude_placeholder() {
local dest="$1" repo="$2"
local PATHS
PATHS=$(repo_paths "$repo")
cat > "$dest" << EOF
# ${NAME} — Claude Code Instructions
> This file is read automatically by Claude Code. For full agent instructions
> shared across all AI tools, see [\`AGENTS.md\`](AGENTS.md).
## Quick Context
**Product:** ${NAME}${TAGLINE}
**Product ID:** \`${ID}\`
**Stack:** ${STACK}
## Rules
1. **Read \`AGENTS.md\` first** — it has coding conventions, file ownership, and tech stack rules.
2. **Every Cosmos document** must have a \`productId: "${ID}"\` field.
3. **Commit messages:** \`type(scope): description\` (feat, fix, docs, refactor, test, chore).
4. **Fix source, not tests** — unless the test itself is wrong.
5. **Never use** \`console.log\`, \`print()\`, or hardcoded colors/secrets/URLs.
## Architecture
${PATHS}
## Build Verification
\`\`\`bash
${BUILD_VFY}
\`\`\`
EOF
}
# ── main loop ───────────────────────────────────────────────────────────────
if [[ ! -f "$REPOS_TXT" ]]; then
echo -e "${RED}[ERROR]${NC} repos.txt not found at: ${REPOS_TXT}"
exit 1
fi
REPOS=()
while IFS= read -r line; do
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "${line// }" ]] && continue
REPOS+=("$line")
done < "$REPOS_TXT"
info "Base directory: ${BASE_DIR}"
info "Repos to process: ${#REPOS[@]}"
$DRY_RUN && warn "DRY RUN — no files will be written or committed"
for REPO in "${REPOS[@]}"; do
header "$REPO"
REPO_DIR="${BASE_DIR}/${REPO}"
if [[ ! -d "$REPO_DIR" ]]; then
warn "Directory not found at ${REPO_DIR} — skipping"
continue
fi
if ! set_meta "$REPO"; then
continue
fi
if [[ ! -f "${REPO_DIR}/AGENTS.md" ]]; then
warn "AGENTS.md not found in ${REPO} — skipping (run manually to bootstrap)"
continue
fi
if $DRY_RUN; then
info "Would generate agent docs for ${NAME} (${ID})"
info " build: ${BUILD_VFY}"
continue
fi
# .editorconfig — always overwrite (identical across all repos)
write_editorconfig "${REPO_DIR}/.editorconfig"
ok ".editorconfig"
# .cursorrules — always overwrite
write_cursorrules "${REPO_DIR}/.cursorrules" "$REPO"
ok ".cursorrules"
# .windsurfrules — always overwrite
write_windsurfrules "${REPO_DIR}/.windsurfrules" "$REPO"
ok ".windsurfrules"
# .clinerules — always overwrite
write_clinerules "${REPO_DIR}/.clinerules" "$REPO"
ok ".clinerules"
# .aider.conf.yml — always overwrite
write_aider_conf "${REPO_DIR}/.aider.conf.yml"
ok ".aider.conf.yml"
# .github/copilot-instructions.md — always overwrite
write_copilot_instructions "${REPO_DIR}/.github" "$REPO"
ok ".github/copilot-instructions.md"
# CLAUDE.md — create only if missing
if [[ ! -f "${REPO_DIR}/CLAUDE.md" ]]; then
write_claude_placeholder "${REPO_DIR}/CLAUDE.md" "$REPO"
ok "CLAUDE.md (created — was missing)"
else
info "CLAUDE.md exists — skipping (hand-crafted; delete to regenerate)"
fi
# Commit if there are any changes in this repo
if git -C "$REPO_DIR" status --porcelain 2>/dev/null | grep -q .; then
git -C "$REPO_DIR" add \
.editorconfig .cursorrules .windsurfrules .clinerules \
.aider.conf.yml CLAUDE.md .github/copilot-instructions.md \
2>/dev/null || true
git -C "$REPO_DIR" commit -m "chore(docs): regenerate AI agent config files" \
--no-verify 2>/dev/null || true
ok "Committed changes in ${REPO}"
CHANGED_REPOS+=("$REPO")
else
info "No changes in ${REPO}"
fi
done
# ── summary ──────────────────────────────────────────────────────────────────
echo ""
if [[ ${#CHANGED_REPOS[@]} -gt 0 ]]; then
ok "Updated and committed ${#CHANGED_REPOS[@]} repo(s):"
for r in "${CHANGED_REPOS[@]}"; do
echo "$r"
done
echo ""
info "Push with:"
for r in "${CHANGED_REPOS[@]}"; do
echo " git -C ${BASE_DIR}/${r} push"
done
else
ok "All repos already up-to-date — no changes."
fi

View File

@ -42,7 +42,7 @@ export interface FastingSessionDoc {
userId: string;
productId: string;
protocolId: string;
startedAt: string;
startedAt: number;
targetDurationMs: number;
status: 'active' | 'completed' | 'broken';
stages: unknown[];
@ -153,7 +153,9 @@ export type PushTriggerType =
| 'social_invite'
| 'weekly_digest'
| 'achievement_unlocked'
| 'refeeding_reminder';
| 'refeeding_reminder'
| 'electrolyte_reminder'
| 'extended_fast_warning';
export interface PushTriggerDoc {
id: string;
@ -171,7 +173,7 @@ export function nomgapPushFire(
type: PushTriggerType;
userId: string;
variables?: Record<string, string>;
scheduledAt?: string;
scheduledFor?: string;
},
opts: NomGapClientOptions
): Promise<PushTriggerDoc> {

View File

@ -30,7 +30,7 @@ type PushType = 'refeeding_reminder' | 'extended_fast_warning' | 'electrolyte_re
interface FastStateResult {
sessionId: string;
protocolId: string;
startedAt: string;
startedAt: number;
elapsedHours: number;
targetHours: number;
isActive: boolean;
@ -126,8 +126,8 @@ async function inspectFastState(
const session = result.items[0] as FastingSessionDoc | undefined;
if (!session) return null;
const startedAt = session.startedAt ?? session.createdAt;
const elapsedMs = Date.now() - new Date(startedAt).getTime();
const startedAt = session.startedAt ?? new Date(session.createdAt).getTime();
const elapsedMs = Date.now() - startedAt;
const elapsedHours = elapsedMs / (1000 * 60 * 60);
return {

View File

@ -15,7 +15,6 @@ import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import type { McpToolRequest } from '../tools/types.js';
import { peakpulseGetStats } from '../../lib/peakpulse-client.js';
import {
telemetryQuery,
diagnosticsCreateSession,
@ -78,16 +77,7 @@ async function inspectSyncFailures(
platform: string | null,
opts: { token?: string; requestId?: string }
): Promise<SyncFailureInspection> {
// Fetch session stats for queue depth
let queueDepth = 0;
let lastSuccessfulSync: string | null = null;
try {
const stats = await peakpulseGetStats(opts);
queueDepth = ((stats as Record<string, unknown>).unsyncedCount as number) ?? 0;
lastSuccessfulSync = ((stats as Record<string, unknown>).lastSyncAt as string) ?? null;
} catch {
// best-effort
}
// Query telemetry for recent sync failures (last 24h)
const now = new Date();
@ -105,10 +95,18 @@ async function inspectSyncFailures(
{ ...opts, productId: 'peakpulse' }
);
recentFailureCount = result.total;
// Derive last successful sync from most recent event timestamp (inverse: last non-failure event)
const events = result.events as Array<Record<string, unknown>>;
if (events.length > 0 && recentFailureCount === 0) {
lastSuccessfulSync = (events[0]?.['timestamp'] as string) ?? null;
}
} catch {
// best-effort
}
// queueDepth: approximate as recent failure count (each failure = one upload that didn't land)
const queueDepth = recentFailureCount;
// Classify failure pattern
let failurePattern: FailurePattern;
if (errorCode === 'auth_401' || errorCode === 'unauthorized') {

View File

@ -105,7 +105,7 @@ registerTool({
.optional()
.default({})
.describe('Template variables for the notification message'),
scheduledAt: z.string().datetime().optional().describe('ISO 8601 fire time (default: now)'),
scheduledFor: z.string().datetime().optional().describe('ISO 8601 fire time (default: now)'),
}),
async execute(args, req) {
return nomgapPushFire(
@ -113,7 +113,7 @@ registerTool({
type: args.type,
userId: args.userId,
variables: args.variables,
scheduledAt: args.scheduledAt,
scheduledFor: args.scheduledFor,
},
{ token: tokenOf(req), requestId: req.id }
);