@aitne/daemon 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/adapter-watchdog.d.ts +70 -0
- package/dist/adapters/adapter-watchdog.js +115 -0
- package/dist/adapters/discord.d.ts +17 -1
- package/dist/adapters/discord.js +33 -0
- package/dist/adapters/notification-manager.d.ts +27 -1
- package/dist/adapters/notification-manager.js +54 -39
- package/dist/adapters/slack-adapter.d.ts +26 -1
- package/dist/adapters/slack-adapter.js +41 -0
- package/dist/adapters/telegram-adapter.d.ts +18 -1
- package/dist/adapters/telegram-adapter.js +41 -2
- package/dist/adapters/types.d.ts +20 -0
- package/dist/adapters/whatsapp-adapter.d.ts +26 -7
- package/dist/adapters/whatsapp-adapter.js +74 -21
- package/dist/api/env-writer.js +8 -5
- package/dist/api/helpers/agent-errors-registry.d.ts +5 -5
- package/dist/api/helpers/agent-errors-registry.js +5 -5
- package/dist/api/routes/agent.js +33 -12
- package/dist/api/routes/agents/index.js +75 -16
- package/dist/api/routes/agents/views.d.ts +37 -2
- package/dist/api/routes/agents/views.js +64 -2
- package/dist/api/routes/background-task.d.ts +22 -0
- package/dist/api/routes/background-task.js +338 -0
- package/dist/api/routes/browser-history.js +9 -1
- package/dist/api/routes/context/permissions.js +3 -2
- package/dist/api/routes/context/snapshots.js +0 -3
- package/dist/api/routes/context/write.js +3 -17
- package/dist/api/routes/dashboard/config.js +48 -12
- package/dist/api/routes/dashboard/cost-approvals.js +66 -0
- package/dist/api/routes/dashboard/notifications.js +9 -9
- package/dist/api/routes/integrations/crud-patch.js +5 -1
- package/dist/api/routes/integrations-reconcile.js +2 -2
- package/dist/api/routes/notion.d.ts +1 -1
- package/dist/api/routes/observations.js +7 -7
- package/dist/api/routes/obsidian.d.ts +1 -1
- package/dist/api/routes/receipts.js +5 -1
- package/dist/api/routes/setup-migrate.js +1 -1
- package/dist/api/routes/setup.js +1 -1
- package/dist/api/routes/task-flows.d.ts +1 -1
- package/dist/api/routes/task-flows.js +1 -1
- package/dist/api/routes/tuning.d.ts +29 -0
- package/dist/api/routes/tuning.js +304 -0
- package/dist/api/server.d.ts +44 -16
- package/dist/api/server.js +9 -0
- package/dist/bootstrap/adapters.d.ts +19 -0
- package/dist/bootstrap/adapters.js +61 -0
- package/dist/bootstrap/api.d.ts +5 -3
- package/dist/bootstrap/api.js +45 -13
- package/dist/bootstrap/catchup.d.ts +1 -1
- package/dist/bootstrap/catchup.js +11 -11
- package/dist/bootstrap/event-pipeline.d.ts +11 -0
- package/dist/bootstrap/event-pipeline.js +245 -7
- package/dist/bootstrap/observers.js +9 -6
- package/dist/bootstrap/schedule-helpers.d.ts +104 -6
- package/dist/bootstrap/schedule-helpers.js +172 -19
- package/dist/config.js +26 -12
- package/dist/core/agent-core.d.ts +33 -1
- package/dist/core/agent-core.js +36 -1
- package/dist/core/agents/activity-scan-cadence.d.ts +103 -0
- package/dist/core/agents/activity-scan-cadence.js +127 -0
- package/dist/core/agents/agent-route-override.d.ts +53 -0
- package/dist/core/agents/agent-route-override.js +69 -0
- package/dist/core/agents/builtin-registry.d.ts +51 -14
- package/dist/core/agents/builtin-registry.js +92 -15
- package/dist/core/agents/config-gate-reconcile.d.ts +38 -0
- package/dist/core/agents/config-gate-reconcile.js +51 -0
- package/dist/core/agents/cron-substitute.d.ts +1 -1
- package/dist/core/agents/cron-substitute.js +1 -1
- package/dist/core/agents/custom-routine-migration.d.ts +60 -0
- package/dist/core/agents/custom-routine-migration.js +149 -0
- package/dist/core/agents/firing-blocked.d.ts +1 -1
- package/dist/core/agents/hourly-cadence.d.ts +102 -0
- package/dist/core/agents/hourly-cadence.js +126 -0
- package/dist/core/agents/loader-boot.js +23 -0
- package/dist/core/agents/loader.d.ts +19 -0
- package/dist/core/agents/loader.js +34 -2
- package/dist/core/agents/override-merge.d.ts +1 -1
- package/dist/core/agents/override-merge.js +9 -1
- package/dist/core/agents/recurrence-convert.d.ts +1 -1
- package/dist/core/agents/recurrence-convert.js +1 -1
- package/dist/core/agents/recurring-schedule-adapter.js +8 -0
- package/dist/core/alerts.js +6 -6
- package/dist/core/backends/auth-health-monitor.d.ts +2 -2
- package/dist/core/backends/auth-health-monitor.js +1 -1
- package/dist/core/backends/backend-router.d.ts +27 -1
- package/dist/core/backends/backend-router.js +165 -1
- package/dist/core/backends/claude-code-core.d.ts +71 -31
- package/dist/core/backends/claude-code-core.js +282 -54
- package/dist/core/backends/cli-quota-guards.d.ts +29 -1
- package/dist/core/backends/cli-quota-guards.js +40 -5
- package/dist/core/backends/codex-core.d.ts +6 -0
- package/dist/core/backends/codex-core.js +22 -6
- package/dist/core/backends/failure-spend.d.ts +58 -0
- package/dist/core/backends/failure-spend.js +137 -0
- package/dist/core/backends/gemini-cli-core.d.ts +6 -0
- package/dist/core/backends/gemini-cli-core.js +25 -6
- package/dist/core/backends/model-registry.d.ts +1 -1
- package/dist/core/backends/model-registry.js +4 -4
- package/dist/core/backends/opencode-core.d.ts +1 -1
- package/dist/core/backends/opencode-core.js +5 -5
- package/dist/core/backends/plan-presets.js +39 -15
- package/dist/core/bang-commands/commands-cost.js +3 -1
- package/dist/core/bang-commands/commands-report.js +4 -3
- package/dist/core/bang-commands/commands-research.js +4 -1
- package/dist/core/bang-commands/commands-revert-tuning.d.ts +18 -0
- package/dist/core/bang-commands/commands-revert-tuning.js +63 -0
- package/dist/core/bang-commands/commands-stop-start.js +3 -3
- package/dist/core/bang-commands/commands-task-control.d.ts +19 -0
- package/dist/core/bang-commands/commands-task-control.js +147 -0
- package/dist/core/bang-commands/commands-wiki.js +5 -5
- package/dist/core/bang-commands/index.d.ts +2 -0
- package/dist/core/bang-commands/index.js +12 -0
- package/dist/core/bang-commands/registry.d.ts +12 -0
- package/dist/core/browser-history/research-cluster-fanout.d.ts +28 -14
- package/dist/core/browser-history/research-cluster-fanout.js +39 -16
- package/dist/core/channel-timeline.d.ts +5 -1
- package/dist/core/channel-timeline.js +13 -0
- package/dist/core/context/index-reconciler.js +5 -2
- package/dist/core/context/policy-index-reconciler.d.ts +6 -4
- package/dist/core/context/policy-index-runner.js +25 -6
- package/dist/core/context-builder-calendar.js +10 -2
- package/dist/core/context-builder-conversation.d.ts +8 -1
- package/dist/core/context-builder-conversation.js +41 -7
- package/dist/core/context-builder-yesterday.js +4 -3
- package/dist/core/context-builder.d.ts +7 -2
- package/dist/core/context-builder.js +62 -20
- package/dist/core/context-file-serializer.d.ts +1 -1
- package/dist/core/context-file-serializer.js +1 -1
- package/dist/core/context-health.js +2 -2
- package/dist/core/context-paths.d.ts +1 -1
- package/dist/core/context-paths.js +1 -1
- package/dist/core/context-validation/prepare-write.js +1 -1
- package/dist/core/context-validation/routine-rulebook.d.ts +1 -1
- package/dist/core/context-vault-aliases.d.ts +0 -13
- package/dist/core/context-vault-aliases.js +37 -0
- package/dist/core/custom-routines.d.ts +99 -0
- package/dist/core/custom-routines.js +187 -0
- package/dist/core/daemon-api-cli.js +49 -0
- package/dist/core/day-boundary.d.ts +46 -0
- package/dist/core/day-boundary.js +40 -0
- package/dist/core/dispatcher-activity-scan.d.ts +221 -0
- package/dist/core/dispatcher-activity-scan.js +775 -0
- package/dist/core/dispatcher-error-handling.d.ts +6 -11
- package/dist/core/dispatcher-error-handling.js +38 -62
- package/dist/core/dispatcher-hourly-check.js +6 -1
- package/dist/core/dispatcher-message-handler.d.ts +10 -0
- package/dist/core/dispatcher-message-handler.js +17 -0
- package/dist/core/dispatcher-morning-routine.d.ts +6 -6
- package/dist/core/dispatcher-morning-routine.js +13 -13
- package/dist/core/dispatcher-result-processor.d.ts +33 -0
- package/dist/core/dispatcher-result-processor.js +167 -11
- package/dist/core/dispatcher-scheduled-background-task.d.ts +42 -0
- package/dist/core/dispatcher-scheduled-background-task.js +89 -0
- package/dist/core/dispatcher-scheduled-tasks.d.ts +63 -1
- package/dist/core/dispatcher-scheduled-tasks.js +213 -6
- package/dist/core/dispatcher-task-delivery.d.ts +105 -0
- package/dist/core/dispatcher-task-delivery.js +555 -0
- package/dist/core/dispatcher-types.d.ts +48 -9
- package/dist/core/dispatcher-types.js +3 -3
- package/dist/core/dispatcher.d.ts +112 -31
- package/dist/core/dispatcher.js +284 -59
- package/dist/core/dm-freshness-metrics.d.ts +1 -1
- package/dist/core/drift-effects.js +2 -2
- package/dist/core/feedback/consolidation-prep.js +17 -5
- package/dist/core/feedback/eviction-scorer.js +6 -2
- package/dist/core/feedback/lesson-format.js +9 -4
- package/dist/core/feedback/lesson-injection.d.ts +1 -1
- package/dist/core/feedback/lesson-injection.js +17 -2
- package/dist/core/feedback/lesson-store-overview.d.ts +8 -4
- package/dist/core/feedback/lesson-store-overview.js +8 -4
- package/dist/core/feedback/regeneralization-prep.js +29 -16
- package/dist/core/feedback/self-performance-prep.d.ts +186 -0
- package/dist/core/feedback/self-performance-prep.js +541 -0
- package/dist/core/feedback/tuning-actuator.d.ts +198 -0
- package/dist/core/feedback/tuning-actuator.js +432 -0
- package/dist/core/feedback/tuning-recommender.d.ts +247 -0
- package/dist/core/feedback/tuning-recommender.js +580 -0
- package/dist/core/feedback/tuning-revert-monitor.d.ts +90 -0
- package/dist/core/feedback/tuning-revert-monitor.js +213 -0
- package/dist/core/health-monitor.d.ts +6 -0
- package/dist/core/health-monitor.js +1 -1
- package/dist/core/injection-policy.d.ts +4 -4
- package/dist/core/injection-policy.js +4 -4
- package/dist/core/integration-main-backend.js +4 -0
- package/dist/core/management-md.d.ts +2 -2
- package/dist/core/management-md.js +51 -13
- package/dist/core/morning/orchestrator.d.ts +2 -2
- package/dist/core/morning/orchestrator.js +2 -2
- package/dist/core/notification-gate.d.ts +64 -0
- package/dist/core/notification-gate.js +51 -0
- package/dist/core/notification-rate-limit.d.ts +40 -0
- package/dist/core/notification-rate-limit.js +50 -0
- package/dist/core/policy-files.d.ts +1 -1
- package/dist/core/policy-files.js +2 -2
- package/dist/core/pre-pass-freshness.d.ts +4 -4
- package/dist/core/retention.d.ts +5 -0
- package/dist/core/retention.js +20 -4
- package/dist/core/review-context.d.ts +1 -1
- package/dist/core/review-context.js +10 -5
- package/dist/core/roadmap-write-lock.d.ts +2 -1
- package/dist/core/roadmap-write-lock.js +15 -10
- package/dist/core/routine-acquisition-plan.d.ts +47 -1
- package/dist/core/routine-acquisition-plan.js +78 -20
- package/dist/core/routine-fetch-window-retry.js +7 -4
- package/dist/core/routine-fetch-window-runner.d.ts +39 -3
- package/dist/core/routine-fetch-window-runner.js +264 -13
- package/dist/core/routine-windows.d.ts +2 -2
- package/dist/core/routine-windows.js +8 -5
- package/dist/core/scheduler.d.ts +175 -16
- package/dist/core/scheduler.js +559 -102
- package/dist/core/signal-detector.d.ts +12 -0
- package/dist/core/signal-detector.js +53 -9
- package/dist/core/skills-compiler-denied-tools.js +2 -2
- package/dist/core/skills-compiler-skill-index.d.ts +2 -2
- package/dist/core/skills-compiler-skill-index.js +2 -2
- package/dist/core/skills-compiler-variants.d.ts +1 -1
- package/dist/core/skills-compiler-variants.js +8 -0
- package/dist/core/skills-compiler.d.ts +29 -26
- package/dist/core/skills-compiler.js +117 -81
- package/dist/core/skills-manifest.d.ts +37 -0
- package/dist/core/skills-manifest.js +73 -2
- package/dist/core/sleep-inhibitor.d.ts +79 -0
- package/dist/core/sleep-inhibitor.js +132 -0
- package/dist/core/slim-system-prompt-loader.d.ts +77 -0
- package/dist/core/slim-system-prompt-loader.js +141 -0
- package/dist/core/spawn-gates.d.ts +126 -0
- package/dist/core/spawn-gates.js +180 -0
- package/dist/core/today-direct-writer.d.ts +2 -2
- package/dist/core/today-direct-writer.js +1 -1
- package/dist/core/today-write-lock.d.ts +4 -2
- package/dist/core/today-write-lock.js +30 -20
- package/dist/core/wake-detector.d.ts +55 -0
- package/dist/core/wake-detector.js +80 -0
- package/dist/core/wiki/compile-lock.d.ts +1 -1
- package/dist/core/wiki/compile-lock.js +1 -1
- package/dist/core/workdir.js +15 -6
- package/dist/db/activity-scan-signals.d.ts +77 -0
- package/dist/db/activity-scan-signals.js +378 -0
- package/dist/db/agents-store.d.ts +28 -0
- package/dist/db/agents-store.js +62 -0
- package/dist/db/background-task-clarifications-store.d.ts +81 -0
- package/dist/db/background-task-clarifications-store.js +152 -0
- package/dist/db/background-task-store.d.ts +207 -0
- package/dist/db/background-task-store.js +380 -0
- package/dist/db/browser-history-store.d.ts +39 -6
- package/dist/db/browser-history-store.js +51 -7
- package/dist/db/browser-task-clarifications-store.d.ts +12 -0
- package/dist/db/browser-task-clarifications-store.js +35 -5
- package/dist/db/browser-task-store.d.ts +3 -0
- package/dist/db/browser-task-store.js +29 -4
- package/dist/db/deferred-dm.d.ts +86 -0
- package/dist/db/deferred-dm.js +199 -0
- package/dist/db/migrations.js +330 -0
- package/dist/db/observations.d.ts +2 -2
- package/dist/db/observations.js +3 -3
- package/dist/db/schema.js +217 -16
- package/dist/db/voice-transcripts-store.d.ts +1 -1
- package/dist/index.js +86 -29
- package/dist/messaging/browser-task-mcp-notifier.d.ts +12 -70
- package/dist/messaging/browser-task-mcp-notifier.js +30 -151
- package/dist/messaging/browser-task-screenshot-attachment.d.ts +15 -0
- package/dist/messaging/browser-task-screenshot-attachment.js +63 -0
- package/dist/observers/delegated-sync-worker.d.ts +6 -6
- package/dist/observers/delegated-sync-worker.js +10 -10
- package/dist/observers/git-delegated-cron.d.ts +1 -1
- package/dist/observers/git-delegated-cron.js +2 -2
- package/dist/observers/github-poller-classifier.d.ts +3 -3
- package/dist/observers/github-poller-classifier.js +3 -3
- package/dist/observers/imminent-event-scheduler.d.ts +1 -1
- package/dist/observers/imminent-event-scheduler.js +1 -1
- package/dist/observers/mail-poller.d.ts +1 -0
- package/dist/observers/mail-poller.js +42 -3
- package/dist/observers/observation-summarizer/summarizer-client.d.ts +2 -2
- package/dist/observers/observation-summarizer/summarizer-client.js +2 -2
- package/dist/observers/observation-summarizer/worker.d.ts +2 -2
- package/dist/observers/observation-summarizer/worker.js +4 -4
- package/dist/observers/obsidian-watcher.d.ts +1 -1
- package/dist/observers/obsidian-watcher.js +1 -1
- package/dist/safety/agent-write-tracker.d.ts +4 -4
- package/dist/safety/agent-write-tracker.js +4 -4
- package/dist/safety/audit.d.ts +43 -5
- package/dist/safety/audit.js +86 -18
- package/dist/safety/risk-classifier.d.ts +6 -0
- package/dist/safety/risk-classifier.js +75 -11
- package/dist/scheduler/activity-scan-gate.d.ts +86 -0
- package/dist/scheduler/activity-scan-gate.js +132 -0
- package/dist/services/background-task/background-task-budget.d.ts +80 -0
- package/dist/services/background-task/background-task-budget.js +91 -0
- package/dist/services/background-task/background-task-driver.d.ts +105 -0
- package/dist/services/background-task/background-task-driver.js +416 -0
- package/dist/services/background-task/background-task-runner.d.ts +96 -0
- package/dist/services/background-task/background-task-runner.js +673 -0
- package/dist/services/background-task/background-task-tools.d.ts +84 -0
- package/dist/services/background-task/background-task-tools.js +247 -0
- package/dist/services/background-task/background-task-transition-events.d.ts +43 -0
- package/dist/services/background-task/background-task-transition-events.js +54 -0
- package/dist/services/browser-history/automation/egress-denylist.d.ts +1 -1
- package/dist/services/browser-history/automation/egress-denylist.js +16 -6
- package/dist/services/browser-history/managed-chromium/sandbox-launcher.js +0 -1
- package/dist/services/browser-task/browser-task-runner.js +53 -8
- package/dist/services/observations-batch.d.ts +1 -1
- package/dist/services/observations-batch.js +2 -2
- package/dist/settings/runtime-settings.d.ts +38 -11
- package/dist/settings/runtime-settings.js +203 -40
- package/dist/settings/settings-store.js +11 -3
- package/package.json +4 -4
|
@@ -37,6 +37,15 @@ export class TelegramAdapter {
|
|
|
37
37
|
attachmentStore;
|
|
38
38
|
bot = null;
|
|
39
39
|
botInfo = null;
|
|
40
|
+
/**
|
|
41
|
+
* Non-null when the long-poll loop has died. telegraf's `launch()`
|
|
42
|
+
* promise stays pending while polling runs and settles when the loop
|
|
43
|
+
* exits: it self-retries network errors / 429 / 5xx internally, but any
|
|
44
|
+
* other failure (401 invalid token, 409 conflicting consumer, an
|
|
45
|
+
* unexpected throw) kills the loop for good. Without observing that
|
|
46
|
+
* settlement the daemon would sit "connected" while receiving nothing.
|
|
47
|
+
*/
|
|
48
|
+
pollingDeadError = null;
|
|
40
49
|
/** Active pairing challenge (null when pairing isn't in progress). */
|
|
41
50
|
pairingChallenge = null;
|
|
42
51
|
/** Buffers for Telegram media albums (media_group_id → accumulated items). */
|
|
@@ -134,8 +143,26 @@ export class TelegramAdapter {
|
|
|
134
143
|
logger.error({ err }, "telegram message handler threw");
|
|
135
144
|
});
|
|
136
145
|
});
|
|
137
|
-
// Start long polling (non-blocking)
|
|
138
|
-
|
|
146
|
+
// Start long polling (non-blocking). The launch() promise settles only
|
|
147
|
+
// when the poll loop exits — track that settlement so a dead loop is
|
|
148
|
+
// observable instead of becoming an unhandled rejection with the
|
|
149
|
+
// adapter silently deaf until daemon restart.
|
|
150
|
+
this.pollingDeadError = null;
|
|
151
|
+
const bot = this.bot;
|
|
152
|
+
bot.launch({ dropPendingUpdates: true }).then(() => {
|
|
153
|
+
// Resolves on stop() (AbortError path). If we did NOT initiate the
|
|
154
|
+
// stop, the loop ended on its own — mark the adapter down.
|
|
155
|
+
if (this.bot === bot) {
|
|
156
|
+
this.pollingDeadError = "Telegram polling loop exited unexpectedly";
|
|
157
|
+
logger.error("Telegram polling loop exited unexpectedly");
|
|
158
|
+
}
|
|
159
|
+
}, (err) => {
|
|
160
|
+
if (this.bot === bot) {
|
|
161
|
+
this.pollingDeadError =
|
|
162
|
+
err instanceof Error ? err.message : String(err);
|
|
163
|
+
logger.error({ err }, "Telegram polling loop died");
|
|
164
|
+
}
|
|
165
|
+
});
|
|
139
166
|
logger.info({ botUsername: this.botInfo?.username }, "Telegram adapter connected (Long Polling)");
|
|
140
167
|
}
|
|
141
168
|
async stop() {
|
|
@@ -153,6 +180,18 @@ export class TelegramAdapter {
|
|
|
153
180
|
async resolveUserChannel() {
|
|
154
181
|
return this.mutableOwnerId;
|
|
155
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Live long-poll liveness for the adapter watchdog. "ok" means the
|
|
185
|
+
* polling loop is still running (telegraf retries transient network /
|
|
186
|
+
* 429 / 5xx failures internally, so an offline laptop stays "ok" and
|
|
187
|
+
* recovers on its own); "down" means the loop has exited and only a
|
|
188
|
+
* full stop→start cycle brings messages back.
|
|
189
|
+
*/
|
|
190
|
+
getConnectionState() {
|
|
191
|
+
if (!this.bot)
|
|
192
|
+
return "unknown";
|
|
193
|
+
return this.pollingDeadError === null ? "ok" : "down";
|
|
194
|
+
}
|
|
156
195
|
async sendMessage(params) {
|
|
157
196
|
if (!this.bot) {
|
|
158
197
|
throw new Error("Telegram bot not started");
|
package/dist/adapters/types.d.ts
CHANGED
|
@@ -24,6 +24,20 @@ export interface NotificationRuntimeStatus {
|
|
|
24
24
|
runtimeState: "ok" | "error" | "connecting";
|
|
25
25
|
error: string | null;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Live transport-level connection state, read by the adapter watchdog
|
|
29
|
+
* (`adapter-watchdog.ts`).
|
|
30
|
+
*
|
|
31
|
+
* - `"ok"` — the underlying socket / poll loop is alive right now.
|
|
32
|
+
* - `"down"` — the library reports the connection dead (socket closed,
|
|
33
|
+
* poll loop exited, gateway session invalidated). The
|
|
34
|
+
* watchdog restarts the adapter after consecutive `"down"`
|
|
35
|
+
* observations.
|
|
36
|
+
* - `"unknown"` — liveness cannot be determined (adapter not started, or
|
|
37
|
+
* the library's internals are not introspectable). The
|
|
38
|
+
* watchdog takes no action on `"unknown"`.
|
|
39
|
+
*/
|
|
40
|
+
export type AdapterConnectionState = "ok" | "down" | "unknown";
|
|
27
41
|
/**
|
|
28
42
|
* MessageAdapter — platform-specific messaging integration.
|
|
29
43
|
*
|
|
@@ -50,6 +64,12 @@ export interface MessageAdapter {
|
|
|
50
64
|
* generic registered/error state tracked by MessageHub.
|
|
51
65
|
*/
|
|
52
66
|
getNotificationRuntimeStatus?(): NotificationRuntimeStatus;
|
|
67
|
+
/**
|
|
68
|
+
* Cheap synchronous probe of the live transport state, for the adapter
|
|
69
|
+
* watchdog. Adapters that cannot introspect their library (or manage
|
|
70
|
+
* their own reconnection, like WhatsApp) omit this.
|
|
71
|
+
*/
|
|
72
|
+
getConnectionState?(): AdapterConnectionState;
|
|
53
73
|
/** Start receiving messages (connects to platform) */
|
|
54
74
|
start(): Promise<void>;
|
|
55
75
|
/** Graceful shutdown */
|
|
@@ -110,9 +110,17 @@ export declare class WhatsAppAdapter implements MessageAdapter {
|
|
|
110
110
|
* watch's async connectivity probe) is in flight, and we have NOT
|
|
111
111
|
* permanently given up. Lets the status accessors report "connecting"
|
|
112
112
|
* during the brief timer-less window of a sustained probe instead of
|
|
113
|
-
* flashing a red error. Cleared on open /
|
|
113
|
+
* flashing a red error. Cleared on open / logout / stop.
|
|
114
114
|
*/
|
|
115
115
|
private reconnecting;
|
|
116
|
+
/**
|
|
117
|
+
* True while the slow app-layer watch is armed (fast phase exhausted on a
|
|
118
|
+
* non-network close). Unlike the network-class sustained watch, this state
|
|
119
|
+
* IS surfaced as an error by {@link getStatusError} — WhatsApp is actively
|
|
120
|
+
* rejecting us and the user may want to act (re-pair, check the dashboard)
|
|
121
|
+
* rather than wait out the ~30-min retry cadence.
|
|
122
|
+
*/
|
|
123
|
+
private appLayerWatch;
|
|
116
124
|
/**
|
|
117
125
|
* Connectivity probe used by the sustained watch. Injectable so tests can
|
|
118
126
|
* drive the offline / back-online transitions without real DNS; production
|
|
@@ -141,9 +149,10 @@ export declare class WhatsAppAdapter implements MessageAdapter {
|
|
|
141
149
|
* - `logged_out` → terminal, surface the error.
|
|
142
150
|
* - `disconnected` → recovery is in flight when a reconnect timer is
|
|
143
151
|
* pending OR a sustained-watch attempt is mid-probe ({@link reconnecting}
|
|
144
|
-
* covers the brief timer-less DNS window)
|
|
145
|
-
*
|
|
146
|
-
*
|
|
152
|
+
* covers the brief timer-less DNS window) — EXCEPT the slow app-layer
|
|
153
|
+
* watch ({@link appLayerWatch}), which is surfaced even though a timer
|
|
154
|
+
* is pending: WhatsApp is actively rejecting us and the ~30-min retry
|
|
155
|
+
* cadence is slow enough that the user may want to act first.
|
|
147
156
|
*/
|
|
148
157
|
getStatusError(): string | null;
|
|
149
158
|
getNotificationRuntimeStatus(): NotificationRuntimeStatus;
|
|
@@ -233,9 +242,13 @@ export declare class WhatsAppAdapter implements MessageAdapter {
|
|
|
233
242
|
* on its own instead of wedging in an error state.
|
|
234
243
|
*
|
|
235
244
|
* If the fast phase exhausts on a *non*-network close (WhatsApp rejecting us
|
|
236
|
-
* at the app layer — bad version, bad session, throttle) we
|
|
237
|
-
*
|
|
238
|
-
*
|
|
245
|
+
* at the app layer — bad version, bad session, throttle) we drop into the
|
|
246
|
+
* slow **app-layer watch**: same timer machinery as the sustained watch but
|
|
247
|
+
* on a much longer cadence ({@link RECONNECT_APP_LAYER_WATCH_DELAY_MS}) and
|
|
248
|
+
* with the error surfaced to the dashboard. Hammering a relay that keeps
|
|
249
|
+
* closing us is the fast path to an IP-level block, but a permanent stop
|
|
250
|
+
* would wedge the adapter until daemon restart over conditions that
|
|
251
|
+
* routinely clear on their own (throttles, server restarts, version churn).
|
|
239
252
|
*/
|
|
240
253
|
private scheduleReconnect;
|
|
241
254
|
/**
|
|
@@ -252,6 +265,12 @@ export declare class WhatsAppAdapter implements MessageAdapter {
|
|
|
252
265
|
* `exhausted && !network` in {@link scheduleReconnect} and falsely give up.
|
|
253
266
|
* A fresh fast phase gives those handshake closes their proper retry budget
|
|
254
267
|
* (a clean `open` doesn't fire before a 515, so we can't rely on it here).
|
|
268
|
+
*
|
|
269
|
+
* The app-layer watch (`appLayerWatch=true`) reuses this path but grants
|
|
270
|
+
* only {@link RECONNECT_APP_LAYER_RETRY_BUDGET} fast attempts instead of a
|
|
271
|
+
* full fresh phase: the network never went away, so a still-broken app
|
|
272
|
+
* layer should fall back into the slow watch after a short burst rather
|
|
273
|
+
* than re-earning ten rapid connects every watch tick.
|
|
255
274
|
*/
|
|
256
275
|
private runReconnectAttempt;
|
|
257
276
|
/**
|
|
@@ -39,14 +39,28 @@ const WA_VERSION_TTL_MS = 12 * 60 * 60 * 1000;
|
|
|
39
39
|
* networks) drops into the sustained connectivity watch below, which never
|
|
40
40
|
* gives up and recovers on its own once the network returns;
|
|
41
41
|
* - any other close (WhatsApp rejected us at the app layer — bad version,
|
|
42
|
-
* bad session, throttle) surfaces an error and
|
|
43
|
-
* relay that keeps closing us is the
|
|
42
|
+
* bad session, throttle) surfaces an error and drops into the slow
|
|
43
|
+
* app-layer watch below — hammering a relay that keeps closing us is the
|
|
44
|
+
* fast path to an IP-level ban, but stopping forever turns a transient
|
|
45
|
+
* server-side condition into a wedge only a daemon restart clears.
|
|
44
46
|
*/
|
|
45
47
|
const RECONNECT_INITIAL_DELAY_MS = 1_000;
|
|
46
48
|
const RECONNECT_MAX_DELAY_MS = 60_000;
|
|
47
49
|
const RECONNECT_BACKOFF_FACTOR = 2;
|
|
48
50
|
const RECONNECT_JITTER_MS = 500;
|
|
49
51
|
const RECONNECT_MAX_ATTEMPTS = 10;
|
|
52
|
+
/**
|
|
53
|
+
* App-layer watch tunables. Once the fast phase exhausts on a *non*-network
|
|
54
|
+
* close (WhatsApp rejecting us at the app layer), we retry on this long
|
|
55
|
+
* cadence instead of giving up: throttles lift, server-side restarts settle,
|
|
56
|
+
* and version rejections clear after the cache refetch. Each watch tick
|
|
57
|
+
* grants only {@link RECONNECT_APP_LAYER_RETRY_BUDGET} fast attempts (enough
|
|
58
|
+
* for the 515 → version-refetch → connect handshake chain) rather than a full
|
|
59
|
+
* fresh fast phase, keeping the worst-case connect rate low enough to pose no
|
|
60
|
+
* ban risk while still recovering unattended.
|
|
61
|
+
*/
|
|
62
|
+
const RECONNECT_APP_LAYER_WATCH_DELAY_MS = 30 * 60_000;
|
|
63
|
+
const RECONNECT_APP_LAYER_RETRY_BUDGET = 3;
|
|
50
64
|
/**
|
|
51
65
|
* Sustained-watch tunables. Once the fast phase exhausts on a *network-class*
|
|
52
66
|
* close we keep watching for connectivity indefinitely on this long, fixed
|
|
@@ -282,9 +296,17 @@ export class WhatsAppAdapter {
|
|
|
282
296
|
* watch's async connectivity probe) is in flight, and we have NOT
|
|
283
297
|
* permanently given up. Lets the status accessors report "connecting"
|
|
284
298
|
* during the brief timer-less window of a sustained probe instead of
|
|
285
|
-
* flashing a red error. Cleared on open /
|
|
299
|
+
* flashing a red error. Cleared on open / logout / stop.
|
|
286
300
|
*/
|
|
287
301
|
reconnecting = false;
|
|
302
|
+
/**
|
|
303
|
+
* True while the slow app-layer watch is armed (fast phase exhausted on a
|
|
304
|
+
* non-network close). Unlike the network-class sustained watch, this state
|
|
305
|
+
* IS surfaced as an error by {@link getStatusError} — WhatsApp is actively
|
|
306
|
+
* rejecting us and the user may want to act (re-pair, check the dashboard)
|
|
307
|
+
* rather than wait out the ~30-min retry cadence.
|
|
308
|
+
*/
|
|
309
|
+
appLayerWatch = false;
|
|
288
310
|
/**
|
|
289
311
|
* Connectivity probe used by the sustained watch. Injectable so tests can
|
|
290
312
|
* drive the offline / back-online transitions without real DNS; production
|
|
@@ -323,9 +345,10 @@ export class WhatsAppAdapter {
|
|
|
323
345
|
* - `logged_out` → terminal, surface the error.
|
|
324
346
|
* - `disconnected` → recovery is in flight when a reconnect timer is
|
|
325
347
|
* pending OR a sustained-watch attempt is mid-probe ({@link reconnecting}
|
|
326
|
-
* covers the brief timer-less DNS window)
|
|
327
|
-
*
|
|
328
|
-
*
|
|
348
|
+
* covers the brief timer-less DNS window) — EXCEPT the slow app-layer
|
|
349
|
+
* watch ({@link appLayerWatch}), which is surfaced even though a timer
|
|
350
|
+
* is pending: WhatsApp is actively rejecting us and the ~30-min retry
|
|
351
|
+
* cadence is slow enough that the user may want to act first.
|
|
329
352
|
*/
|
|
330
353
|
getStatusError() {
|
|
331
354
|
switch (this.connectionState) {
|
|
@@ -337,6 +360,8 @@ export class WhatsAppAdapter {
|
|
|
337
360
|
case "logged_out":
|
|
338
361
|
return this.lastError ?? "WhatsApp logged out";
|
|
339
362
|
case "disconnected":
|
|
363
|
+
if (this.appLayerWatch)
|
|
364
|
+
return this.lastError ?? "WhatsApp disconnected";
|
|
340
365
|
if (this.reconnectTimer !== null || this.reconnecting)
|
|
341
366
|
return null;
|
|
342
367
|
return this.lastError ?? "WhatsApp disconnected";
|
|
@@ -392,6 +417,7 @@ export class WhatsAppAdapter {
|
|
|
392
417
|
this.lastError = null;
|
|
393
418
|
this.reconnectAttempts = 0;
|
|
394
419
|
this.reconnecting = false;
|
|
420
|
+
this.appLayerWatch = false;
|
|
395
421
|
this.lastCloseWasNetwork = false;
|
|
396
422
|
if (this.reconnectTimer) {
|
|
397
423
|
clearTimeout(this.reconnectTimer);
|
|
@@ -484,6 +510,7 @@ export class WhatsAppAdapter {
|
|
|
484
510
|
}
|
|
485
511
|
this.reconnectAttempts = 0;
|
|
486
512
|
this.reconnecting = false;
|
|
513
|
+
this.appLayerWatch = false;
|
|
487
514
|
this.lastCloseWasNetwork = false;
|
|
488
515
|
this.sentMessageIds.clear();
|
|
489
516
|
this.clearQrSnapshot();
|
|
@@ -923,6 +950,7 @@ export class WhatsAppAdapter {
|
|
|
923
950
|
this.lastError = null;
|
|
924
951
|
this.reconnectAttempts = 0;
|
|
925
952
|
this.reconnecting = false;
|
|
953
|
+
this.appLayerWatch = false;
|
|
926
954
|
this.lastCloseWasNetwork = false;
|
|
927
955
|
this.clearQrSnapshot();
|
|
928
956
|
this.clearQrFile();
|
|
@@ -956,6 +984,7 @@ export class WhatsAppAdapter {
|
|
|
956
984
|
this.lastError = `WhatsApp logged out (status ${statusCode ?? "unknown"}) — re-pair required`;
|
|
957
985
|
this.reconnectAttempts = 0;
|
|
958
986
|
this.reconnecting = false;
|
|
987
|
+
this.appLayerWatch = false;
|
|
959
988
|
logger.error({ statusCode }, "whatsapp connection closed: logged out");
|
|
960
989
|
if (this.onLoggedOut) {
|
|
961
990
|
try {
|
|
@@ -971,6 +1000,7 @@ export class WhatsAppAdapter {
|
|
|
971
1000
|
this.connectionState = "disabled";
|
|
972
1001
|
this.reconnectAttempts = 0;
|
|
973
1002
|
this.reconnecting = false;
|
|
1003
|
+
this.appLayerWatch = false;
|
|
974
1004
|
return;
|
|
975
1005
|
}
|
|
976
1006
|
// Classify the close: a transport/network failure (or a raw socket error
|
|
@@ -1013,9 +1043,13 @@ export class WhatsAppAdapter {
|
|
|
1013
1043
|
* on its own instead of wedging in an error state.
|
|
1014
1044
|
*
|
|
1015
1045
|
* If the fast phase exhausts on a *non*-network close (WhatsApp rejecting us
|
|
1016
|
-
* at the app layer — bad version, bad session, throttle) we
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1046
|
+
* at the app layer — bad version, bad session, throttle) we drop into the
|
|
1047
|
+
* slow **app-layer watch**: same timer machinery as the sustained watch but
|
|
1048
|
+
* on a much longer cadence ({@link RECONNECT_APP_LAYER_WATCH_DELAY_MS}) and
|
|
1049
|
+
* with the error surfaced to the dashboard. Hammering a relay that keeps
|
|
1050
|
+
* closing us is the fast path to an IP-level block, but a permanent stop
|
|
1051
|
+
* would wedge the adapter until daemon restart over conditions that
|
|
1052
|
+
* routinely clear on their own (throttles, server restarts, version churn).
|
|
1019
1053
|
*/
|
|
1020
1054
|
scheduleReconnect() {
|
|
1021
1055
|
if (this.reconnectTimer || this.shuttingDown)
|
|
@@ -1023,15 +1057,25 @@ export class WhatsAppAdapter {
|
|
|
1023
1057
|
if (this.connectionState === "logged_out")
|
|
1024
1058
|
return;
|
|
1025
1059
|
const exhaustedFastPhase = this.reconnectAttempts >= RECONNECT_MAX_ATTEMPTS;
|
|
1026
|
-
|
|
1027
|
-
const previousError = this.lastError ?? "unknown error";
|
|
1028
|
-
this.lastError = `WhatsApp reconnect gave up after ${RECONNECT_MAX_ATTEMPTS} attempts (${previousError})`;
|
|
1029
|
-
this.reconnecting = false;
|
|
1030
|
-
logger.error({ attempts: this.reconnectAttempts, lastError: previousError }, "whatsapp reconnect: max attempts exceeded");
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1060
|
+
const appLayerWatch = exhaustedFastPhase && !this.lastCloseWasNetwork;
|
|
1033
1061
|
let delayMs;
|
|
1034
|
-
if (
|
|
1062
|
+
if (appLayerWatch) {
|
|
1063
|
+
// Wrap lastError and log only on ENTRY into the watch. Re-scheduling
|
|
1064
|
+
// from within it (the probe-unreachable path of runReconnectAttempt
|
|
1065
|
+
// calls scheduleReconnect on every watch tick) must not re-wrap —
|
|
1066
|
+
// each pass would nest the previous wrapped message inside the new
|
|
1067
|
+
// one, growing lastError without bound, and would re-emit the
|
|
1068
|
+
// error-level log every ~30 min for one underlying condition.
|
|
1069
|
+
if (!this.appLayerWatch) {
|
|
1070
|
+
const previousError = this.lastError ?? "unknown error";
|
|
1071
|
+
this.lastError = `WhatsApp reconnect: ${RECONNECT_MAX_ATTEMPTS} fast attempts failed at the app layer (${previousError}); retrying every ~${Math.round(RECONNECT_APP_LAYER_WATCH_DELAY_MS / 60_000)} min`;
|
|
1072
|
+
logger.error({ attempts: this.reconnectAttempts, lastError: previousError }, "whatsapp reconnect: fast phase exhausted at app layer; entering slow watch");
|
|
1073
|
+
}
|
|
1074
|
+
delayMs =
|
|
1075
|
+
RECONNECT_APP_LAYER_WATCH_DELAY_MS
|
|
1076
|
+
+ Math.floor(Math.random() * RECONNECT_SUSTAINED_JITTER_MS);
|
|
1077
|
+
}
|
|
1078
|
+
else if (exhaustedFastPhase) {
|
|
1035
1079
|
// Sustained network watch — fixed cadence, counter left pinned at the
|
|
1036
1080
|
// cap so we stay in this branch without growing reconnectAttempts.
|
|
1037
1081
|
delayMs =
|
|
@@ -1044,14 +1088,15 @@ export class WhatsAppAdapter {
|
|
|
1044
1088
|
this.reconnectAttempts += 1;
|
|
1045
1089
|
}
|
|
1046
1090
|
this.reconnecting = true;
|
|
1047
|
-
|
|
1091
|
+
this.appLayerWatch = appLayerWatch;
|
|
1092
|
+
logger.info({ attempt: this.reconnectAttempts, delayMs, sustained: exhaustedFastPhase, appLayerWatch }, "whatsapp reconnect scheduled");
|
|
1048
1093
|
this.reconnectTimer = setTimeout(() => {
|
|
1049
1094
|
this.reconnectTimer = null;
|
|
1050
1095
|
if (this.shuttingDown || this.connectionState === "logged_out") {
|
|
1051
1096
|
this.reconnecting = false;
|
|
1052
1097
|
return;
|
|
1053
1098
|
}
|
|
1054
|
-
void this.runReconnectAttempt(exhaustedFastPhase);
|
|
1099
|
+
void this.runReconnectAttempt(exhaustedFastPhase, appLayerWatch);
|
|
1055
1100
|
}, delayMs);
|
|
1056
1101
|
this.reconnectTimer.unref?.();
|
|
1057
1102
|
}
|
|
@@ -1069,8 +1114,14 @@ export class WhatsAppAdapter {
|
|
|
1069
1114
|
* `exhausted && !network` in {@link scheduleReconnect} and falsely give up.
|
|
1070
1115
|
* A fresh fast phase gives those handshake closes their proper retry budget
|
|
1071
1116
|
* (a clean `open` doesn't fire before a 515, so we can't rely on it here).
|
|
1117
|
+
*
|
|
1118
|
+
* The app-layer watch (`appLayerWatch=true`) reuses this path but grants
|
|
1119
|
+
* only {@link RECONNECT_APP_LAYER_RETRY_BUDGET} fast attempts instead of a
|
|
1120
|
+
* full fresh phase: the network never went away, so a still-broken app
|
|
1121
|
+
* layer should fall back into the slow watch after a short burst rather
|
|
1122
|
+
* than re-earning ten rapid connects every watch tick.
|
|
1072
1123
|
*/
|
|
1073
|
-
async runReconnectAttempt(sustained) {
|
|
1124
|
+
async runReconnectAttempt(sustained, appLayerWatch = false) {
|
|
1074
1125
|
if (sustained) {
|
|
1075
1126
|
const reachable = await this.isNetworkReachable();
|
|
1076
1127
|
// shuttingDown / logged_out may have flipped during the async probe.
|
|
@@ -1084,7 +1135,9 @@ export class WhatsAppAdapter {
|
|
|
1084
1135
|
return;
|
|
1085
1136
|
}
|
|
1086
1137
|
logger.info("whatsapp reconnect: network reachable, reconnecting");
|
|
1087
|
-
this.reconnectAttempts =
|
|
1138
|
+
this.reconnectAttempts = appLayerWatch
|
|
1139
|
+
? RECONNECT_MAX_ATTEMPTS - RECONNECT_APP_LAYER_RETRY_BUDGET
|
|
1140
|
+
: 0;
|
|
1088
1141
|
}
|
|
1089
1142
|
try {
|
|
1090
1143
|
await this.connect();
|
package/dist/api/env-writer.js
CHANGED
|
@@ -67,14 +67,17 @@ function isRestartRequiredKey(key) {
|
|
|
67
67
|
const NUMERIC_RANGE = {
|
|
68
68
|
dayBoundaryHour: { min: 0, max: 9, label: "0–9 (before 10:00)" },
|
|
69
69
|
executeTimeoutMinutes: { min: 1, max: 1440, label: "1–1440 minutes" },
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
activityScanActiveStartHour: { min: 0, max: 23, label: "0–23" },
|
|
71
|
+
activityScanActiveEndHour: { min: 1, max: 24, label: "1–24" },
|
|
72
72
|
// Any positive integer up to one day. Intervals that evenly divide 60
|
|
73
73
|
// produce a tight cron; arbitrary intervals run a minute-tick cron with an
|
|
74
74
|
// in-callback modulo gate anchored at activeStartHour. See
|
|
75
|
-
// scheduler.ts:
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
// scheduler.ts:buildActivityScanCronExpr / shouldFireActivityScanTickAt.
|
|
76
|
+
activityScanIntervalMinutes: { min: 1, max: 1440, label: "1–1440 minutes" },
|
|
77
|
+
activityScanMinObservations: { min: 0, max: 1000, label: "0–1000" },
|
|
78
|
+
// Twin of the runtimeSettingsSchema bound; cap 480 = the self-tuning R1
|
|
79
|
+
// freshness ladder's top notch (SELF_TUNING_REVIEW_CYCLE_DESIGN.md D2).
|
|
80
|
+
activityScanPrePassFreshnessMinutes: { min: 0, max: 480, label: "0–480 minutes" },
|
|
78
81
|
gitPollIntervalSeconds: { min: 60, max: 86400, label: "60–86400 seconds" },
|
|
79
82
|
githubPollIntervalSeconds: { min: 60, max: 86400, label: "60–86400 seconds" },
|
|
80
83
|
// 0 disables the observer; 20160 min = 14 days which is already well past
|
|
@@ -1261,7 +1261,7 @@ export declare const AGENT_ERROR_REGISTRY: {
|
|
|
1261
1261
|
};
|
|
1262
1262
|
readonly "observations.invalid_actor": {
|
|
1263
1263
|
readonly expected: "actor = 'user' | 'agent' | 'system'";
|
|
1264
|
-
readonly hint: "The `?actor=` filter accepts only `user`, `agent`, or `system`. `user` is the
|
|
1264
|
+
readonly hint: "The `?actor=` filter accepts only `user`, `agent`, or `system`. `user` is the activity-scan default (excludes agent's own writes). Omit to get all observations.";
|
|
1265
1265
|
readonly skillAnchor: "observations#actor-filter";
|
|
1266
1266
|
readonly legacyErrorCode: "invalid_actor";
|
|
1267
1267
|
readonly constraint: {
|
|
@@ -1868,11 +1868,11 @@ export declare const AGENT_ERROR_REGISTRY: {
|
|
|
1868
1868
|
readonly legacyErrorCode: "daemon_starting";
|
|
1869
1869
|
readonly retryable: true;
|
|
1870
1870
|
};
|
|
1871
|
-
readonly "agent.
|
|
1872
|
-
readonly expected: "
|
|
1873
|
-
readonly hint: "POST /agent/run-now
|
|
1871
|
+
readonly "agent.activity_scan_unavailable": {
|
|
1872
|
+
readonly expected: "triggerActivityScan wired into the API server";
|
|
1873
|
+
readonly hint: "POST /api/agent/run-now was called before the activity-scan engine was wired (typically only the first ~1s of boot). Wait ~3s and retry.";
|
|
1874
1874
|
readonly skillAnchor: "agent#run-now";
|
|
1875
|
-
readonly legacyErrorCode: "
|
|
1875
|
+
readonly legacyErrorCode: "activity_scan_unavailable";
|
|
1876
1876
|
readonly retryable: true;
|
|
1877
1877
|
};
|
|
1878
1878
|
readonly "agent.roadmap_maintenance_unavailable": {
|
|
@@ -1194,7 +1194,7 @@ export const AGENT_ERROR_REGISTRY = {
|
|
|
1194
1194
|
// ── /api/observations/* — Phase 9 pending/consume.
|
|
1195
1195
|
"observations.invalid_actor": {
|
|
1196
1196
|
expected: "actor = 'user' | 'agent' | 'system'",
|
|
1197
|
-
hint: "The `?actor=` filter accepts only `user`, `agent`, or `system`. `user` is the
|
|
1197
|
+
hint: "The `?actor=` filter accepts only `user`, `agent`, or `system`. `user` is the activity-scan default (excludes agent's own writes). Omit to get all observations.",
|
|
1198
1198
|
skillAnchor: "observations#actor-filter",
|
|
1199
1199
|
legacyErrorCode: "invalid_actor",
|
|
1200
1200
|
constraint: { type: "enum", enum: ["user", "agent", "system"] },
|
|
@@ -1794,11 +1794,11 @@ export const AGENT_ERROR_REGISTRY = {
|
|
|
1794
1794
|
legacyErrorCode: "daemon_starting",
|
|
1795
1795
|
retryable: true,
|
|
1796
1796
|
},
|
|
1797
|
-
"agent.
|
|
1798
|
-
expected: "
|
|
1799
|
-
hint: "POST /agent/run-now
|
|
1797
|
+
"agent.activity_scan_unavailable": {
|
|
1798
|
+
expected: "triggerActivityScan wired into the API server",
|
|
1799
|
+
hint: "POST /api/agent/run-now was called before the activity-scan engine was wired (typically only the first ~1s of boot). Wait ~3s and retry.",
|
|
1800
1800
|
skillAnchor: "agent#run-now",
|
|
1801
|
-
legacyErrorCode: "
|
|
1801
|
+
legacyErrorCode: "activity_scan_unavailable",
|
|
1802
1802
|
retryable: true,
|
|
1803
1803
|
},
|
|
1804
1804
|
"agent.roadmap_maintenance_unavailable": {
|
package/dist/api/routes/agent.js
CHANGED
|
@@ -24,10 +24,10 @@ export function createAgentRoutes(deps) {
|
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
if (!deps.
|
|
27
|
+
if (!deps.triggerActivityScan) {
|
|
28
28
|
return respondWithAgentError(c, 503, [
|
|
29
|
-
composeIssue("agent.
|
|
30
|
-
field: "
|
|
29
|
+
composeIssue("agent.activity_scan_unavailable", {
|
|
30
|
+
field: "triggerActivityScan",
|
|
31
31
|
received: "<unavailable>",
|
|
32
32
|
}),
|
|
33
33
|
]);
|
|
@@ -37,7 +37,7 @@ export function createAgentRoutes(deps) {
|
|
|
37
37
|
? body.reason.trim()
|
|
38
38
|
: "api";
|
|
39
39
|
// Default to `force: true` for manual runs — the user explicitly asked
|
|
40
|
-
// us to check now, so bypass the `
|
|
40
|
+
// us to check now, so bypass the `activityScanMinObservations` threshold.
|
|
41
41
|
// Callers may pass `{ force: false }` to respect the threshold (e.g.,
|
|
42
42
|
// for a gentle cron-style "check if anything is pending" ping).
|
|
43
43
|
const force = body.force === undefined ? true : body.force === true;
|
|
@@ -61,7 +61,7 @@ export function createAgentRoutes(deps) {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
const result = await deps.
|
|
64
|
+
const result = await deps.triggerActivityScan(`manual:${reason}`, {
|
|
65
65
|
force,
|
|
66
66
|
...(requestedModel ? { requestedModel } : {}),
|
|
67
67
|
});
|
|
@@ -251,6 +251,7 @@ export function createAgentRoutes(deps) {
|
|
|
251
251
|
const normalizedPlatforms = platforms ?? (platform ? [platform] : undefined);
|
|
252
252
|
const messageSummary = message.slice(0, 200);
|
|
253
253
|
const originSessionId = parsePositiveIntegerHeader(c.req.header("x-pa-session-id"));
|
|
254
|
+
const correlationId = c.req.header("x-pa-event-correlation-id");
|
|
254
255
|
let dispatchId = randomUUID();
|
|
255
256
|
let notificationId = dispatchId;
|
|
256
257
|
if (sendNotification) {
|
|
@@ -260,7 +261,29 @@ export function createAgentRoutes(deps) {
|
|
|
260
261
|
priority: priority ?? "normal",
|
|
261
262
|
notificationType: "agent",
|
|
262
263
|
...(originSessionId !== null ? { originSessionId } : {}),
|
|
264
|
+
...(correlationId ? { correlationId } : {}),
|
|
263
265
|
});
|
|
266
|
+
// QUIET_HOURS_HARDENING_PLAN.md Phase 1 — quiet-hours deferral. The
|
|
267
|
+
// message WILL deliver (durable `task_type='dm'` row at the
|
|
268
|
+
// quiet-hours edge, visible on /schedule), so the event is marked
|
|
269
|
+
// notified exactly like the sent path: not marking would double-send
|
|
270
|
+
// once the dispatcher's implicit final-text forward fires.
|
|
271
|
+
if (result.status === "deferred_quiet_hours") {
|
|
272
|
+
if (deps.markEventNotified && correlationId) {
|
|
273
|
+
deps.markEventNotified(correlationId);
|
|
274
|
+
}
|
|
275
|
+
return c.json({
|
|
276
|
+
status: "deferred_quiet_hours",
|
|
277
|
+
scheduleId: result.scheduleId,
|
|
278
|
+
deliverAfter: result.deliverAfter,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
// Rate-limited: nothing was sent and nothing was queued — the live
|
|
282
|
+
// session can adapt (write to today.md, retry later). The event is
|
|
283
|
+
// deliberately NOT marked notified.
|
|
284
|
+
if (result.status === "rate_limited") {
|
|
285
|
+
return c.json({ status: "rate_limited", retryAfter: result.retryAfter }, 429);
|
|
286
|
+
}
|
|
264
287
|
dispatchId = result.dispatchId;
|
|
265
288
|
notificationId = dispatchId;
|
|
266
289
|
}
|
|
@@ -283,13 +306,11 @@ export function createAgentRoutes(deps) {
|
|
|
283
306
|
// Notify-dedup — when the agent's shim auto-attached the correlation
|
|
284
307
|
// header, mark the event so the dispatcher skips the implicit
|
|
285
308
|
// final-text DM forward in processResult. We only mark on the
|
|
286
|
-
//
|
|
287
|
-
// is daemon-misconfiguration, not user-visible
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
deps.markEventNotified(correlationId);
|
|
292
|
-
}
|
|
309
|
+
// delivery branches (sent above-the-fold here, deferred earlier); the
|
|
310
|
+
// warn-fallback branch is daemon-misconfiguration, not user-visible
|
|
311
|
+
// delivery, and the rate-limited branch never delivers.
|
|
312
|
+
if (sendNotification && deps.markEventNotified && correlationId) {
|
|
313
|
+
deps.markEventNotified(correlationId);
|
|
293
314
|
}
|
|
294
315
|
return c.json({
|
|
295
316
|
status: "sent",
|