@aitne/daemon 0.1.9 → 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.d.ts +1 -0
- package/dist/api/env-writer.js +17 -7
- 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-schedule.js +5 -1
- 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/apple-calendar.js +4 -1
- 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/calendar.js +12 -2
- package/dist/api/routes/context/path-resolve.js +6 -1
- package/dist/api/routes/context/permissions.js +12 -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 +58 -12
- package/dist/api/routes/dashboard/cost-approvals.js +66 -0
- package/dist/api/routes/dashboard/notifications.js +9 -9
- package/dist/api/routes/dashboard/oauth-google.js +5 -3
- package/dist/api/routes/feedback.d.ts +3 -0
- package/dist/api/routes/feedback.js +349 -0
- package/dist/api/routes/git.js +10 -3
- package/dist/api/routes/github.js +5 -1
- package/dist/api/routes/integrations/crud-patch.js +5 -1
- package/dist/api/routes/integrations-reconcile.js +2 -2
- package/dist/api/routes/mcp.js +65 -13
- 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 +12 -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 +246 -8
- 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 +32 -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 +38 -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 +47 -18
- 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 +193 -5
- 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 +11 -1
- package/dist/core/context-paths.js +17 -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 +50 -1
- 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 +24 -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 +104 -1
- package/dist/core/dispatcher-scheduled-tasks.js +480 -8
- 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 +297 -60
- package/dist/core/dm-freshness-metrics.d.ts +1 -1
- package/dist/core/drift-effects.js +2 -2
- package/dist/core/feedback/consolidation-prep.d.ts +94 -0
- package/dist/core/feedback/consolidation-prep.js +254 -0
- package/dist/core/feedback/eviction-scorer.d.ts +81 -0
- package/dist/core/feedback/eviction-scorer.js +136 -0
- package/dist/core/feedback/lesson-format.d.ts +79 -0
- package/dist/core/feedback/lesson-format.js +199 -0
- package/dist/core/feedback/lesson-injection.d.ts +98 -0
- package/dist/core/feedback/lesson-injection.js +174 -0
- package/dist/core/feedback/lesson-merge.d.ts +51 -0
- package/dist/core/feedback/lesson-merge.js +88 -0
- package/dist/core/feedback/lesson-store-overview.d.ts +46 -0
- package/dist/core/feedback/lesson-store-overview.js +42 -0
- package/dist/core/feedback/promotion-gate.d.ts +69 -0
- package/dist/core/feedback/promotion-gate.js +117 -0
- package/dist/core/feedback/regeneralization-prep.d.ts +87 -0
- package/dist/core/feedback/regeneralization-prep.js +152 -0
- package/dist/core/feedback/scope-parser.d.ts +86 -0
- package/dist/core/feedback/scope-parser.js +141 -0
- 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 +83 -1
- package/dist/core/injection-policy.js +61 -3
- 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 +51 -1
- package/dist/core/signal-detector.js +321 -24
- 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 +60 -14
- package/dist/core/today-direct-writer.js +90 -13
- 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/wiki/wiki-fts.js +13 -6
- 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/feedback-signals-store.d.ts +77 -0
- package/dist/db/feedback-signals-store.js +144 -0
- package/dist/db/migrations.js +380 -0
- package/dist/db/observations.d.ts +2 -2
- package/dist/db/observations.js +3 -3
- package/dist/db/schema.js +260 -22
- 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/always-disallowed.d.ts +1 -1
- package/dist/safety/always-disallowed.js +39 -0
- 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 +97 -18
- 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 +34 -8
- package/dist/services/browser-history/lifecycle/platform.js +44 -2
- 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/mcp/probe.js +30 -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 +45 -12
- package/dist/settings/runtime-settings.js +215 -40
- package/dist/settings/settings-store.js +11 -3
- package/package.json +4 -4
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-Tuning Review Cycle — Verify stage / auto-revert monitor
|
|
3
|
+
* (SELF_TUNING_REVIEW_CYCLE_DESIGN.md §3.4, Phase 3).
|
|
4
|
+
*
|
|
5
|
+
* Piggybacks the existing hourly cron tick (scheduler.ts — same
|
|
6
|
+
* fire-and-forget slot as the auth probe; no new scheduled session, P2) and
|
|
7
|
+
* throttles itself to one pass per UTC day via
|
|
8
|
+
* {@link REVERT_MONITOR_STATE_KEY}. Seven days after an applied config
|
|
9
|
+
* change, it recomputes the rule's target metric over the verify window
|
|
10
|
+
* `[applied_at, applied_at + 7d)` and:
|
|
11
|
+
*
|
|
12
|
+
* - **regression past the rule's margin** → revert through the shared
|
|
13
|
+
* {@link revertAppliedTuningChange} (config restored via the
|
|
14
|
+
* `applyConfigUpdates` chokepoint, ledger stamped `reverted_at` — which
|
|
15
|
+
* triggers the 28-day re-proposal cool-down — audit
|
|
16
|
+
* `self_tuning.reverted`, `self_critique` signal so the failure becomes
|
|
17
|
+
* a lesson) and DM the owner;
|
|
18
|
+
* - **no regression** → stamp `verified_at` + audit
|
|
19
|
+
* `self_tuning.verified` so the entry is never re-examined.
|
|
20
|
+
*
|
|
21
|
+
* Per-rule margins (D3/D4 — named constants, deliberately not settings
|
|
22
|
+
* keys):
|
|
23
|
+
* - R1 reverts if daily novelty≥2 observation arrivals fall >30% below
|
|
24
|
+
* the pre-change baseline (stale pre-pass suppressing signal) OR the
|
|
25
|
+
* cautious-escalate tick share rises >10 pt.
|
|
26
|
+
* - R3 reverts if >10% of `stage0_silent` ticks in the window carried
|
|
27
|
+
* `maxNoveltyScore ≥ 2` in their audited snapshot — harm only the
|
|
28
|
+
* raised ceiling can introduce (today's gate never silences novelty≥2).
|
|
29
|
+
* - R5 reverts on the explicit-correction proxy: any negative explicit /
|
|
30
|
+
* self_critique signal citing a lesson within the window.
|
|
31
|
+
*
|
|
32
|
+
* The monitor runs regardless of `selfTuningEnabled`: entries only exist
|
|
33
|
+
* once actuation has run, and a safety rollback must keep working even if
|
|
34
|
+
* the owner turns the loop off afterwards. Only `config`-actuator entries
|
|
35
|
+
* are verified — lesson/schedule entries carry no machine state.
|
|
36
|
+
*/
|
|
37
|
+
import { TUNING_METRIC_WINDOW_DAYS, auditSelfTuning, computeR1Metric, computeR3Metric, countLessonRegressionSignals, ledgerStateKey, listLedgerEntries, revertAppliedTuningChange, } from "./tuning-actuator.js";
|
|
38
|
+
import { readRuntimeState, writeRuntimeState } from "../../db/runtime-state.js";
|
|
39
|
+
import { createLogger } from "../../logging.js";
|
|
40
|
+
const logger = createLogger("tuning-revert-monitor");
|
|
41
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
42
|
+
/**
|
|
43
|
+
* Daily-throttle state key. Dot-separated namespace on purpose — the
|
|
44
|
+
* Measure stage's `gatherLedger` scans `self_tuning:%` and must never pick
|
|
45
|
+
* monitor state up as a phantom ledger entry (same rule as the pending
|
|
46
|
+
* cycle key).
|
|
47
|
+
*/
|
|
48
|
+
export const REVERT_MONITOR_STATE_KEY = "self_tuning.revert_monitor";
|
|
49
|
+
/** §3.4 — days between apply and the verify pass. */
|
|
50
|
+
export const TUNING_VERIFY_WINDOW_DAYS = TUNING_METRIC_WINDOW_DAYS;
|
|
51
|
+
/** D4 — R1 reverts when novelty≥2 arrivals fall >30% below baseline. */
|
|
52
|
+
export const R1_NOVELTY_ARRIVALS_MAX_DROP = 0.3;
|
|
53
|
+
/** D4 — R1 reverts when the cautious-escalate share rises >10 pt. */
|
|
54
|
+
export const R1_CAUTIOUS_ESCALATE_MAX_RISE = 0.1;
|
|
55
|
+
/** D3 — R3 reverts when >10% of silent ticks carried novelty≥2 snapshots. */
|
|
56
|
+
export const R3_SILENT_NOVELTY_GE2_MAX_SHARE = 0.1;
|
|
57
|
+
function isR1Metric(value) {
|
|
58
|
+
return (typeof value === "object" &&
|
|
59
|
+
value !== null &&
|
|
60
|
+
typeof value.noveltyGe2PerDay === "number" &&
|
|
61
|
+
typeof value.cautiousEscalateShare === "number");
|
|
62
|
+
}
|
|
63
|
+
function pct(value) {
|
|
64
|
+
return `${Math.round(value * 100)}%`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Decide one applied entry's fate. Pure given the DB rows: every margin is
|
|
68
|
+
* compared against telemetry that already exists (D3 — no recomputation of
|
|
69
|
+
* live signals). An entry whose `applied_at` cannot be parsed, or whose
|
|
70
|
+
* rule has no metric, settles as verified with an explanatory result — the
|
|
71
|
+
* conservative direction is "leave the change in place", never "revert
|
|
72
|
+
* without evidence".
|
|
73
|
+
*/
|
|
74
|
+
export function evaluateAppliedEntry(db, entry, now) {
|
|
75
|
+
const appliedMs = Date.parse(entry.blob.applied_at);
|
|
76
|
+
if (Number.isNaN(appliedMs)) {
|
|
77
|
+
return { action: "verify", result: "invalid_applied_at" };
|
|
78
|
+
}
|
|
79
|
+
const windowEndMs = appliedMs + TUNING_VERIFY_WINDOW_DAYS * DAY_MS;
|
|
80
|
+
if (now.getTime() < windowEndMs)
|
|
81
|
+
return { action: "wait" };
|
|
82
|
+
const from = new Date(appliedMs);
|
|
83
|
+
const to = new Date(windowEndMs);
|
|
84
|
+
if (entry.blob.rule === "R1") {
|
|
85
|
+
if (!isR1Metric(entry.blob.baselineMetric)) {
|
|
86
|
+
return { action: "verify", result: "no_baseline" };
|
|
87
|
+
}
|
|
88
|
+
const baseline = entry.blob.baselineMetric;
|
|
89
|
+
const current = computeR1Metric(db, from, to);
|
|
90
|
+
if (baseline.noveltyGe2PerDay > 0 &&
|
|
91
|
+
current.noveltyGe2PerDay <
|
|
92
|
+
baseline.noveltyGe2PerDay * (1 - R1_NOVELTY_ARRIVALS_MAX_DROP)) {
|
|
93
|
+
return {
|
|
94
|
+
action: "revert",
|
|
95
|
+
reason: `novelty>=2 observation arrivals fell to ` +
|
|
96
|
+
`${current.noveltyGe2PerDay.toFixed(2)}/day vs baseline ` +
|
|
97
|
+
`${baseline.noveltyGe2PerDay.toFixed(2)}/day (>30% drop)`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (current.cautiousEscalateShare >
|
|
101
|
+
baseline.cautiousEscalateShare + R1_CAUTIOUS_ESCALATE_MAX_RISE) {
|
|
102
|
+
return {
|
|
103
|
+
action: "revert",
|
|
104
|
+
reason: `cautious-escalate tick share rose to ` +
|
|
105
|
+
`${pct(current.cautiousEscalateShare)} vs baseline ` +
|
|
106
|
+
`${pct(baseline.cautiousEscalateShare)} (>10 pt rise)`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return { action: "verify", result: "pass" };
|
|
110
|
+
}
|
|
111
|
+
if (entry.blob.rule === "R3") {
|
|
112
|
+
const metric = computeR3Metric(db, from, to);
|
|
113
|
+
if (metric.stage0Ticks > 0 &&
|
|
114
|
+
metric.noveltyGe2 / metric.stage0Ticks > R3_SILENT_NOVELTY_GE2_MAX_SHARE) {
|
|
115
|
+
return {
|
|
116
|
+
action: "revert",
|
|
117
|
+
reason: `${metric.noveltyGe2}/${metric.stage0Ticks} silent ticks carried ` +
|
|
118
|
+
`maxNoveltyScore>=2 (>10% — harm from the raised ceiling)`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return { action: "verify", result: "pass" };
|
|
122
|
+
}
|
|
123
|
+
if (entry.blob.rule === "R5") {
|
|
124
|
+
const signals = countLessonRegressionSignals(db, from, to);
|
|
125
|
+
if (signals > 0) {
|
|
126
|
+
return {
|
|
127
|
+
action: "revert",
|
|
128
|
+
reason: `${signals} explicit-correction signal(s) cited a lesson within ` +
|
|
129
|
+
"the verify window (forgotten-lesson proxy)",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return { action: "verify", result: "pass" };
|
|
133
|
+
}
|
|
134
|
+
return { action: "verify", result: "no_metric" };
|
|
135
|
+
}
|
|
136
|
+
/** §3.4 — the one-line owner DM for an auto-revert. */
|
|
137
|
+
export function buildAutoRevertDmMessage(entry, reason) {
|
|
138
|
+
return (`Self-tuning auto-revert: restored ${entry.key} to ` +
|
|
139
|
+
`${String(entry.blob.prev)} — ${reason}. The key is now in a 28-day ` +
|
|
140
|
+
"re-proposal cool-down.");
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The cron-tick entry point. Throttled to one pass per UTC day; the state
|
|
144
|
+
* write happens before the scan so a mid-pass failure waits for tomorrow
|
|
145
|
+
* instead of retrying every tick. Each entry is processed in isolation —
|
|
146
|
+
* one broken entry never blocks the rest.
|
|
147
|
+
*/
|
|
148
|
+
export async function runSelfTuningRevertMonitor(deps, now = new Date()) {
|
|
149
|
+
const today = now.toISOString().slice(0, 10);
|
|
150
|
+
const state = readRuntimeState(deps.db, REVERT_MONITOR_STATE_KEY);
|
|
151
|
+
if (state?.lastRunDay === today) {
|
|
152
|
+
return { ran: false, reverted: [], verified: [] };
|
|
153
|
+
}
|
|
154
|
+
writeRuntimeState(deps.db, REVERT_MONITOR_STATE_KEY, { lastRunDay: today });
|
|
155
|
+
const run = { ran: true, reverted: [], verified: [] };
|
|
156
|
+
const due = listLedgerEntries(deps.db).filter((entry) => entry.blob.actuator === "config" &&
|
|
157
|
+
entry.blob.reverted_at === undefined &&
|
|
158
|
+
entry.blob.verified_at === undefined);
|
|
159
|
+
for (const entry of due) {
|
|
160
|
+
try {
|
|
161
|
+
const decision = evaluateAppliedEntry(deps.db, entry, now);
|
|
162
|
+
if (decision.action === "wait")
|
|
163
|
+
continue;
|
|
164
|
+
if (decision.action === "revert") {
|
|
165
|
+
const result = await revertAppliedTuningChange(deps, entry, {
|
|
166
|
+
trigger: "auto",
|
|
167
|
+
reason: decision.reason,
|
|
168
|
+
now,
|
|
169
|
+
});
|
|
170
|
+
if (!result.ok) {
|
|
171
|
+
logger.warn({ key: entry.key, error: result.error }, "Auto-revert failed at the config chokepoint");
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
run.reverted.push(entry.key);
|
|
175
|
+
if (deps.sendDm) {
|
|
176
|
+
try {
|
|
177
|
+
await deps.sendDm(buildAutoRevertDmMessage(entry, decision.reason));
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
logger.warn({ err, key: entry.key }, "Auto-revert DM failed");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
logger.warn({ key: entry.key }, "Auto-revert applied without DM path — owner not notified");
|
|
185
|
+
}
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
// decision.action === "verify" — clean window (or no metric): stamp
|
|
189
|
+
// so the entry is never re-examined; revertability via
|
|
190
|
+
// `!revert tuning` is unaffected. Re-read before writing (same
|
|
191
|
+
// discipline as revertAppliedTuningChange): an `!revert tuning`
|
|
192
|
+
// landing between this pass's scan and this stamp must not have its
|
|
193
|
+
// `reverted_at` clobbered by the stale scanned blob — that would
|
|
194
|
+
// both resurrect the key as revertable and drop its 28d cool-down.
|
|
195
|
+
const current = readRuntimeState(deps.db, ledgerStateKey(entry.key)) ?? entry.blob;
|
|
196
|
+
writeRuntimeState(deps.db, ledgerStateKey(entry.key), {
|
|
197
|
+
...current,
|
|
198
|
+
verified_at: now.toISOString(),
|
|
199
|
+
verify_result: decision.result,
|
|
200
|
+
});
|
|
201
|
+
auditSelfTuning(deps.db, "self_tuning.verified", "autonomous", "success", {
|
|
202
|
+
key: entry.key,
|
|
203
|
+
rule: entry.blob.rule,
|
|
204
|
+
verifyResult: decision.result,
|
|
205
|
+
});
|
|
206
|
+
run.verified.push(entry.key);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
logger.warn({ err, key: entry.key }, "Revert-monitor entry failed");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return run;
|
|
213
|
+
}
|
|
@@ -4,6 +4,12 @@ import type { EventBus } from "./event-bus.js";
|
|
|
4
4
|
import type { MessageHub } from "../adapters/message-hub.js";
|
|
5
5
|
import type { ObserverManager } from "../observers/manager.js";
|
|
6
6
|
export interface HealthStatus {
|
|
7
|
+
/**
|
|
8
|
+
* Seconds since daemon start. Every consumer of the `/health` `uptime`
|
|
9
|
+
* field (`bin/aitne.mjs formatUptime`, dashboard `formatUptime`)
|
|
10
|
+
* formats seconds — this was milliseconds until 2026-06-10, which made
|
|
11
|
+
* `aitne status` report a minutes-old daemon as days of uptime.
|
|
12
|
+
*/
|
|
7
13
|
daemonUptime: number;
|
|
8
14
|
eventBusSize: number;
|
|
9
15
|
activeSessions: number;
|
|
@@ -89,7 +89,7 @@ export class HealthMonitor {
|
|
|
89
89
|
dbConnected = false;
|
|
90
90
|
}
|
|
91
91
|
return {
|
|
92
|
-
daemonUptime: Date.now() - this.startedAt.getTime(),
|
|
92
|
+
daemonUptime: Math.floor((Date.now() - this.startedAt.getTime()) / 1000),
|
|
93
93
|
eventBusSize: this.eventBus.size,
|
|
94
94
|
activeSessions,
|
|
95
95
|
dbConnected,
|
|
@@ -83,7 +83,7 @@ export interface InjectionPolicy {
|
|
|
83
83
|
* redaction-aware wikilinks. Also drops the `*` policy-file merge
|
|
84
84
|
* because the lite-tier skill bundle never invokes MCP; the redaction
|
|
85
85
|
* policy is re-declared inline.
|
|
86
|
-
* - **
|
|
86
|
+
* - **Activity scan** (`routine.activity_scan`) — task-flow §"Execution
|
|
87
87
|
* budget" explicitly tells the agent NOT to read roadmap / projects /
|
|
88
88
|
* user files unless an observation warrants it.
|
|
89
89
|
* - **Today refresh** (`routine.today_refresh`) — dashboard-triggered
|
|
@@ -105,3 +105,85 @@ export interface InjectionPolicy {
|
|
|
105
105
|
* not a third boolean on this struct.
|
|
106
106
|
*/
|
|
107
107
|
export declare function getInjectionPolicy(eventOrProcessKey: string): InjectionPolicy;
|
|
108
|
+
/**
|
|
109
|
+
* FEEDBACK_LEARNING_LOOP_DESIGN.md §5 — the Stage-3 *opt-in* resolver for the
|
|
110
|
+
* feedback learning-loop's `<agent_lessons>` blocks.
|
|
111
|
+
*
|
|
112
|
+
* Co-located with `getInjectionPolicy` on purpose: this module is the single
|
|
113
|
+
* source of truth for "which surface sees which always-/sometimes-injected
|
|
114
|
+
* block", and the design explicitly rejects scattering an
|
|
115
|
+
* `isMessageEvent(event) || isNotifyDecidingRoutine(event)` check across
|
|
116
|
+
* `context-builder.ts` (it would re-introduce the fragmentation V20
|
|
117
|
+
* consolidated away). `<agent_lessons>` is **default-off** — only the handful
|
|
118
|
+
* of surfaces below want it — so it is an *opt-in* resolver, not a member of
|
|
119
|
+
* the `alwaysBlocks` *opt-out* set (which is for default-*on* heavy blocks a
|
|
120
|
+
* narrow routine sheds; a positive opt-out member would be the wrong polarity).
|
|
121
|
+
*
|
|
122
|
+
* Three fields, matching the design's documented shape:
|
|
123
|
+
*
|
|
124
|
+
* - `global` — inject `policies/agent-lessons.md ## Lessons` (scope `agent`:
|
|
125
|
+
* global agent-operating behaviour — notification discipline, filter
|
|
126
|
+
* quality). Phase 3 consumer: `ContextBuilder`.
|
|
127
|
+
* - `slim` — use the hard-2048-byte, top-N-by-score variant on the hourly
|
|
128
|
+
* notify turn (§6). Only `routine.activity_scan` sets it. Implies `global`.
|
|
129
|
+
* - `self` — eligible for the per-agent `policies/agents/<slug>/lessons.md`
|
|
130
|
+
* block (scope `agent:<slug>`). **Phase 4 consumer.** The builder reads it
|
|
131
|
+
* next to `<agent_identity>` and gates it on a resolved, path-safe slug
|
|
132
|
+
* stamped onto `event.data.agentId` at the dispatch site — `self === true`
|
|
133
|
+
* here means "this surface *may* carry self lessons"; an actual injection
|
|
134
|
+
* additionally requires the run to be bound to an Agent. `activity_scan`
|
|
135
|
+
* keeps `self: false` so the slim notify turn never carries a second block.
|
|
136
|
+
*
|
|
137
|
+
* **Surface keying is grounded in the real event-type strings build() sees,
|
|
138
|
+
* not the design's prose shorthand:**
|
|
139
|
+
* - DM / dashboard messages arrive as `message.*` (dashboard DMs included —
|
|
140
|
+
* they are `message.*` with `platform="dashboard"`).
|
|
141
|
+
* - The morning routine's *notify-deciding* stage builds context as
|
|
142
|
+
* `routine.morning_routine_today` (Stage A). The umbrella
|
|
143
|
+
* `routine.morning_routine` never reaches `build()` (the orchestrator
|
|
144
|
+
* decomposes it into the two stage events), and Stage B
|
|
145
|
+
* (`routine.morning_routine_journal`) is a lite journal author that decides
|
|
146
|
+
* no notifications — injecting lessons there would be wasted bytes against
|
|
147
|
+
* the §0 cost constraint. So Stage A is keyed, the umbrella and Stage B are
|
|
148
|
+
* not.
|
|
149
|
+
* - `routine.activity_scan` is the escalated Stage-3 LLM/notify turn (gate
|
|
150
|
+
* Layers 1–3 are code and build no prompt), so the slim block bites exactly
|
|
151
|
+
* where the notify decision is made. The `.triage` lite classification is
|
|
152
|
+
* intentionally excluded.
|
|
153
|
+
* - `scheduled.task` is **binding-aware** (Phase 4). A *bare* scheduled.task
|
|
154
|
+
* (generic close-the-loop task, observer-emitted cron, roadmap refresh — no
|
|
155
|
+
* resolved Agent) gets nothing, preserving the §5 last-row opt-out. A
|
|
156
|
+
* scheduled.task that *resolves to an Agent* (`agentBound`, a user-defined
|
|
157
|
+
* task-output Agent — `report-writer` et al.) is the §5 "Defined-agent
|
|
158
|
+
* execution" row: it gets global + self so feedback on the Agent's output
|
|
159
|
+
* reaches that Agent (requirement #3). The builder supplies the binding fact
|
|
160
|
+
* via `opts.agentBound` so the decision still lives in this one module rather
|
|
161
|
+
* than fragmenting a `resolveAgentId() != null` check into `context-builder.ts`.
|
|
162
|
+
* - Everything else — observers, `fetch_window`, `today_refresh`, and any
|
|
163
|
+
* unlisted key — gets nothing (the §5 "this surface gets almost nothing"
|
|
164
|
+
* row, mirroring `buildFetchWindowContext`).
|
|
165
|
+
*/
|
|
166
|
+
export interface AgentLessonsInjection {
|
|
167
|
+
/** Inject the global `policies/agent-lessons.md ## Lessons` block. */
|
|
168
|
+
readonly global: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Eligible for the per-agent `policies/agents/<slug>/lessons.md` block.
|
|
171
|
+
* Phase 4 consumer — gated additionally on a resolved slug at the build site.
|
|
172
|
+
*/
|
|
173
|
+
readonly self: boolean;
|
|
174
|
+
/** Use the slim, hard-2048-byte, top-N-by-score hourly notify variant. */
|
|
175
|
+
readonly slim: boolean;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Resolve which `<agent_lessons>` block(s) a surface receives. See
|
|
179
|
+
* {@link AgentLessonsInjection} for the field/keying rationale.
|
|
180
|
+
*
|
|
181
|
+
* `opts.agentBound` (Phase 4) tells the resolver whether this firing resolved
|
|
182
|
+
* to an Agent (`resolveAgentId() != null`, surfaced by the builder as a stamped
|
|
183
|
+
* `event.data.agentId`). It only changes the binding-aware `scheduled.task`
|
|
184
|
+
* surface — every other key returns the same shape regardless — so a call with
|
|
185
|
+
* no opts is identical to the Phase-3 behaviour.
|
|
186
|
+
*/
|
|
187
|
+
export declare function getAgentLessonsInjection(eventOrProcessKey: string, opts?: {
|
|
188
|
+
agentBound?: boolean;
|
|
189
|
+
}): AgentLessonsInjection;
|
|
@@ -59,7 +59,7 @@ const DEFAULT_POLICY = {
|
|
|
59
59
|
* redaction-aware wikilinks. Also drops the `*` policy-file merge
|
|
60
60
|
* because the lite-tier skill bundle never invokes MCP; the redaction
|
|
61
61
|
* policy is re-declared inline.
|
|
62
|
-
* - **
|
|
62
|
+
* - **Activity scan** (`routine.activity_scan`) — task-flow §"Execution
|
|
63
63
|
* budget" explicitly tells the agent NOT to read roadmap / projects /
|
|
64
64
|
* user files unless an observation warrants it.
|
|
65
65
|
* - **Today refresh** (`routine.today_refresh`) — dashboard-triggered
|
|
@@ -88,9 +88,9 @@ export function getInjectionPolicy(eventOrProcessKey) {
|
|
|
88
88
|
policyFileGlobalMerge: false,
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
-
// Narrow routines (
|
|
91
|
+
// Narrow routines (activity scan, today refresh) — drop both heavy
|
|
92
92
|
// blocks. `*` policy merge is preserved (redaction.md is non-negotiable).
|
|
93
|
-
if (eventOrProcessKey === "routine.
|
|
93
|
+
if (eventOrProcessKey === "routine.activity_scan" ||
|
|
94
94
|
eventOrProcessKey === "routine.today_refresh") {
|
|
95
95
|
return {
|
|
96
96
|
alwaysBlocks: NO_BLOCKS,
|
|
@@ -115,3 +115,61 @@ export function getInjectionPolicy(eventOrProcessKey) {
|
|
|
115
115
|
}
|
|
116
116
|
return DEFAULT_POLICY;
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Pre-allocated frozen shapes — `getAgentLessonsInjection` returns shared
|
|
120
|
+
* instances so equality comparisons are stable and allocation-free, mirroring
|
|
121
|
+
* the `ALL_BLOCKS` / `NO_BLOCKS` pattern above.
|
|
122
|
+
*/
|
|
123
|
+
const LESSONS_DM_REVIEW = Object.freeze({
|
|
124
|
+
global: true,
|
|
125
|
+
self: true,
|
|
126
|
+
slim: false,
|
|
127
|
+
});
|
|
128
|
+
const LESSONS_HOURLY = Object.freeze({
|
|
129
|
+
global: true,
|
|
130
|
+
self: false,
|
|
131
|
+
slim: true,
|
|
132
|
+
});
|
|
133
|
+
const LESSONS_NONE = Object.freeze({
|
|
134
|
+
global: false,
|
|
135
|
+
self: false,
|
|
136
|
+
slim: false,
|
|
137
|
+
});
|
|
138
|
+
/**
|
|
139
|
+
* Resolve which `<agent_lessons>` block(s) a surface receives. See
|
|
140
|
+
* {@link AgentLessonsInjection} for the field/keying rationale.
|
|
141
|
+
*
|
|
142
|
+
* `opts.agentBound` (Phase 4) tells the resolver whether this firing resolved
|
|
143
|
+
* to an Agent (`resolveAgentId() != null`, surfaced by the builder as a stamped
|
|
144
|
+
* `event.data.agentId`). It only changes the binding-aware `scheduled.task`
|
|
145
|
+
* surface — every other key returns the same shape regardless — so a call with
|
|
146
|
+
* no opts is identical to the Phase-3 behaviour.
|
|
147
|
+
*/
|
|
148
|
+
export function getAgentLessonsInjection(eventOrProcessKey, opts) {
|
|
149
|
+
// DM / dashboard messages — the primary surface lessons calibrate.
|
|
150
|
+
if (eventOrProcessKey.startsWith("message.")) {
|
|
151
|
+
return LESSONS_DM_REVIEW;
|
|
152
|
+
}
|
|
153
|
+
switch (eventOrProcessKey) {
|
|
154
|
+
// Scheduled DM tone session (morning briefing, meeting nudges, …) — same
|
|
155
|
+
// conversational posture as a live DM.
|
|
156
|
+
case "scheduled.dm":
|
|
157
|
+
// Notify-deciding routines: morning Stage A + the review cadences. Each
|
|
158
|
+
// owns a go/no-go `/api/notify` decision that lessons should calibrate.
|
|
159
|
+
case "routine.morning_routine_today":
|
|
160
|
+
case "routine.evening_review":
|
|
161
|
+
case "routine.weekly_review":
|
|
162
|
+
case "routine.monthly_review":
|
|
163
|
+
return LESSONS_DM_REVIEW;
|
|
164
|
+
// Hourly notify turn — slim, hard-capped notification-discipline variant.
|
|
165
|
+
case "routine.activity_scan":
|
|
166
|
+
return LESSONS_HOURLY;
|
|
167
|
+
// Defined-agent task execution (§5 "Defined-agent execution"). A bare
|
|
168
|
+
// scheduled.task stays NONE (the §5 opt-out); one that resolves to an Agent
|
|
169
|
+
// gets global + self so a generated Agent sees feedback on its own output.
|
|
170
|
+
case "scheduled.task":
|
|
171
|
+
return opts?.agentBound ? LESSONS_DM_REVIEW : LESSONS_NONE;
|
|
172
|
+
default:
|
|
173
|
+
return LESSONS_NONE;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -131,6 +131,10 @@ export function cascadeNativeBindingsOnMainSwitch(db, newMainBackendId) {
|
|
|
131
131
|
// against the same drift covered by the c8-ignored branch above.
|
|
132
132
|
/* c8 ignore next */
|
|
133
133
|
deniedTools: state.deniedTools ?? [],
|
|
134
|
+
// User configuration that must survive the disable/re-enable cycle —
|
|
135
|
+
// PATCH re-enables with `previous.fetchTargets`, so dropping it here
|
|
136
|
+
// would silently wipe the allowlist on a main-backend change.
|
|
137
|
+
fetchTargets: state.fetchTargets ?? [],
|
|
134
138
|
lastChangedAt: now,
|
|
135
139
|
});
|
|
136
140
|
flipped.push({
|
|
@@ -58,7 +58,7 @@ export declare function renderNoteSourcesSection(integrations: IntegrationsRecor
|
|
|
58
58
|
/**
|
|
59
59
|
* INTEGRATION_NATIVE_MODE_DESIGN.md §7.3 — render the full per-session
|
|
60
60
|
* routing table that the per-backend instruction file (`CLAUDE.md` /
|
|
61
|
-
* `AGENTS.md` / `GEMINI.md`) and the
|
|
61
|
+
* `AGENTS.md` / `GEMINI.md`) and the activity_scan / DM task-flow files
|
|
62
62
|
* substitute in for the `<integration-routing-table>` placeholder.
|
|
63
63
|
*
|
|
64
64
|
* Always renders every registered integration, even when all rows are
|
|
@@ -77,7 +77,7 @@ export declare function renderIntegrationRoutingTable(integrations: Integrations
|
|
|
77
77
|
* `native` rows; `disabled` rows are filtered out entirely so the
|
|
78
78
|
* task-flow's "for each integration" loop has zero iterations for them.
|
|
79
79
|
*
|
|
80
|
-
* This is what the
|
|
80
|
+
* This is what the activity_scan and DM task-flow files iterate over;
|
|
81
81
|
* the full {@link renderIntegrationRoutingTable} is for the instruction
|
|
82
82
|
* file's read-only audit summary.
|
|
83
83
|
*/
|
|
@@ -63,24 +63,53 @@ function consumeSelfWrite(absPath) {
|
|
|
63
63
|
return pendingSelfWrites.delete(absPath);
|
|
64
64
|
}
|
|
65
65
|
// ── Render ─────────────────────────────────────────────────────────────────
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
66
|
+
// Frontmatter contract. `policies/integrations.md` lives inside the vault
|
|
67
|
+
// under the `policies/` authority class, so it MUST satisfy the vault
|
|
68
|
+
// frontmatter validator (`context-frontmatter.ts`): `type: rule`,
|
|
69
|
+
// `owner ∈ {agent, shared, user}`, and an ISO `updated` date. Before the
|
|
70
|
+
// CONTEXT_VAULT_REDESIGN restructure this file lived at the un-validated
|
|
71
|
+
// `~/.personal-agent/integrations.md`, so it shipped a bespoke
|
|
72
|
+
// daemon-snapshot frontmatter (`owner: daemon`, no `type`/`updated`). The
|
|
73
|
+
// restructure moved it under `policies/` and added the generic `policies/`
|
|
74
|
+
// validation, but this renderer was never reconciled — leaving every
|
|
75
|
+
// install's file flagged "frontmatter requires `type`" by Vault Health.
|
|
76
|
+
//
|
|
77
|
+
// `owner` is `shared` because the file is a daemon-rendered snapshot of
|
|
78
|
+
// `settings.integrations_json` that the user may also hand-edit (chokidar
|
|
79
|
+
// reconciles edits back into the DB) — the same mixed authority as
|
|
80
|
+
// `policies/management.md`. The Dashboard (Settings → Connections) remains
|
|
81
|
+
// the canonical edit surface. See §14.3 of
|
|
71
82
|
// docs/design/14-integration-delegation.md.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
//
|
|
84
|
+
// `updated` is derived from the most recent `lastChangedAt` across all
|
|
85
|
+
// integration rows (truncated to a calendar date) so the render stays a
|
|
86
|
+
// pure function of DB state: booting re-renders byte-identical output until
|
|
87
|
+
// a mode actually changes, preserving the idempotency contract above.
|
|
88
|
+
const FRONTMATTER_FALLBACK_UPDATED = "2026-04-17";
|
|
89
|
+
function renderFrontmatter(integrations) {
|
|
90
|
+
let latest = "";
|
|
91
|
+
for (const key of INTEGRATION_KEYS) {
|
|
92
|
+
const ts = integrations[key].lastChangedAt;
|
|
93
|
+
if (ts > latest)
|
|
94
|
+
latest = ts;
|
|
95
|
+
}
|
|
96
|
+
const updated = /^\d{4}-\d{2}-\d{2}/.test(latest)
|
|
97
|
+
? latest.slice(0, 10)
|
|
98
|
+
: FRONTMATTER_FALLBACK_UPDATED;
|
|
99
|
+
return `---
|
|
100
|
+
type: rule
|
|
101
|
+
slug: integrations
|
|
102
|
+
owner: shared
|
|
103
|
+
updated: ${updated}
|
|
76
104
|
schema_version: 1
|
|
77
105
|
---
|
|
78
106
|
`;
|
|
107
|
+
}
|
|
79
108
|
const MODES_SECTION = `## Modes
|
|
80
109
|
|
|
81
110
|
- **direct** — daemon holds credentials and polls; full feature set; setup required.
|
|
82
111
|
- **delegated** — daemon proxies a separate backend connector on a cadence; reduced features; zero setup.
|
|
83
|
-
- **native** — main backend's own native MCP / connector reaches the integration on-demand within the same DM /
|
|
112
|
+
- **native** — main backend's own native MCP / connector reaches the integration on-demand within the same DM / activity_scan turn; no daemon polling and no daemon-side proxy.
|
|
84
113
|
- **disabled** — integration off.
|
|
85
114
|
`;
|
|
86
115
|
function renderCurrentStateTable(integrations) {
|
|
@@ -132,15 +161,23 @@ export function renderNoteSourcesSection(integrations, notes) {
|
|
|
132
161
|
else if (notion.mode === "delegated" && notion.delegatedBackend) {
|
|
133
162
|
notionLine = `enabled (delegated via ${notion.delegatedBackend})`;
|
|
134
163
|
}
|
|
164
|
+
else if (notion.mode === "native" && notion.nativeBackend) {
|
|
165
|
+
notionLine = `enabled (native via ${notion.nativeBackend})`;
|
|
166
|
+
}
|
|
135
167
|
else {
|
|
136
168
|
notionLine = "enabled (direct)";
|
|
137
169
|
}
|
|
170
|
+
const notionTargets = (notion.fetchTargets ?? []).map((target) => target.label);
|
|
171
|
+
const notionTargetsLine = notionTargets.length > 0
|
|
172
|
+
? notionTargets.join(", ")
|
|
173
|
+
: "—";
|
|
138
174
|
return [
|
|
139
175
|
"## Note Sources",
|
|
140
176
|
"",
|
|
141
177
|
"<!-- Auto-generated. Edit settings via Dashboard → Settings → Note. Hand-edits are overwritten on next render. -->",
|
|
142
178
|
`- Obsidian vault (personal): ${obsidianLine}`,
|
|
143
179
|
`- Notion: ${notionLine}`,
|
|
180
|
+
`- Notion routine fetch targets: ${notionTargetsLine}`,
|
|
144
181
|
"",
|
|
145
182
|
].join("\n");
|
|
146
183
|
}
|
|
@@ -218,7 +255,7 @@ function renderToolDenySection(integrations) {
|
|
|
218
255
|
/**
|
|
219
256
|
* INTEGRATION_NATIVE_MODE_DESIGN.md §7.3 — render the full per-session
|
|
220
257
|
* routing table that the per-backend instruction file (`CLAUDE.md` /
|
|
221
|
-
* `AGENTS.md` / `GEMINI.md`) and the
|
|
258
|
+
* `AGENTS.md` / `GEMINI.md`) and the activity_scan / DM task-flow files
|
|
222
259
|
* substitute in for the `<integration-routing-table>` placeholder.
|
|
223
260
|
*
|
|
224
261
|
* Always renders every registered integration, even when all rows are
|
|
@@ -247,7 +284,7 @@ export function renderIntegrationRoutingTable(integrations) {
|
|
|
247
284
|
* `native` rows; `disabled` rows are filtered out entirely so the
|
|
248
285
|
* task-flow's "for each integration" loop has zero iterations for them.
|
|
249
286
|
*
|
|
250
|
-
* This is what the
|
|
287
|
+
* This is what the activity_scan and DM task-flow files iterate over;
|
|
251
288
|
* the full {@link renderIntegrationRoutingTable} is for the instruction
|
|
252
289
|
* file's read-only audit summary.
|
|
253
290
|
*/
|
|
@@ -381,7 +418,7 @@ export function renderManagementMd(integrations, notes = {
|
|
|
381
418
|
externalObsidianWatch: true,
|
|
382
419
|
}) {
|
|
383
420
|
return [
|
|
384
|
-
|
|
421
|
+
renderFrontmatter(integrations),
|
|
385
422
|
"# Integration Management\n",
|
|
386
423
|
MODES_SECTION,
|
|
387
424
|
renderCurrentStateTable(integrations),
|
|
@@ -666,6 +703,7 @@ function mergeParsedIntoDb(dbState, parsed) {
|
|
|
666
703
|
if (semanticChange) {
|
|
667
704
|
merged[key] = {
|
|
668
705
|
...next,
|
|
706
|
+
fetchTargets: prev.fetchTargets ?? [],
|
|
669
707
|
lastChangedAt: new Date().toISOString(),
|
|
670
708
|
};
|
|
671
709
|
}
|
|
@@ -110,7 +110,7 @@ export interface MorningPipelineOrchestratorDeps {
|
|
|
110
110
|
* morning-routine-optimization.md Phase 6 — ⑥ AgentJournalAppender
|
|
111
111
|
* needs the safety write-tracker so the journal's atomic write does
|
|
112
112
|
* not get tagged as a user-actor change by the obsidian / git
|
|
113
|
-
* observers (which would re-trigger the
|
|
113
|
+
* observers (which would re-trigger the activity scan on the agent's
|
|
114
114
|
* own output). The context-index reconciler is intentionally NOT
|
|
115
115
|
* threaded here: `journal/agent.md` is not in the indexable set, so
|
|
116
116
|
* the chokidar fallback path covers it without an explicit hint.
|
|
@@ -254,7 +254,7 @@ export declare class MorningRoutinePipelineOrchestrator {
|
|
|
254
254
|
* (audit's internal try/catch swallowed a real SQLite error AND
|
|
255
255
|
* `processResult`'s notification path threw too), the parent-audit
|
|
256
256
|
* emitter will return `stage_a_row_missing` and the pre-routine gate
|
|
257
|
-
* stays unfired for the day — that day's
|
|
257
|
+
* stays unfired for the day — that day's activity_scan / evening_review
|
|
258
258
|
* are skipped with `morning_routine_pending_for_today`, but
|
|
259
259
|
* `MAX_RETRIES`-bounded `scheduleMorningRetry` does NOT loop on this
|
|
260
260
|
* shape because today.md health is independent. The day's automation
|
|
@@ -358,7 +358,7 @@ export class MorningRoutinePipelineOrchestrator {
|
|
|
358
358
|
* (audit's internal try/catch swallowed a real SQLite error AND
|
|
359
359
|
* `processResult`'s notification path threw too), the parent-audit
|
|
360
360
|
* emitter will return `stage_a_row_missing` and the pre-routine gate
|
|
361
|
-
* stays unfired for the day — that day's
|
|
361
|
+
* stays unfired for the day — that day's activity_scan / evening_review
|
|
362
362
|
* are skipped with `morning_routine_pending_for_today`, but
|
|
363
363
|
* `MAX_RETRIES`-bounded `scheduleMorningRetry` does NOT loop on this
|
|
364
364
|
* shape because today.md health is independent. The day's automation
|
|
@@ -993,7 +993,7 @@ export class MorningRoutinePipelineOrchestrator {
|
|
|
993
993
|
// could still call `Write` on `daily/<date>.md` directly —
|
|
994
994
|
// bypassing the daemon-side `DailyJournalComposer` chokepoint
|
|
995
995
|
// and racing it. Mirrors the precedent at
|
|
996
|
-
// `dispatcher-
|
|
996
|
+
// `dispatcher-activity-scan.ts:1003` (`routine.activity_scan.triage`).
|
|
997
997
|
//
|
|
998
998
|
// Activation requires the clamp gate in `claude-code-core.ts`
|
|
999
999
|
// to honour an empty array as "no tools" — fixed in the same
|