@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
|
@@ -59,7 +59,7 @@ export const CONTEXT_RELATIVE_PATHS = {
|
|
|
59
59
|
},
|
|
60
60
|
routines: {
|
|
61
61
|
index: "policies/routines/_index.md",
|
|
62
|
-
|
|
62
|
+
activityScan: "policies/routines/activity-scan.md",
|
|
63
63
|
morning: "policies/routines/morning.md",
|
|
64
64
|
evening: "policies/routines/evening.md",
|
|
65
65
|
weekly: "policies/routines/weekly.md",
|
|
@@ -68,6 +68,11 @@ export const CONTEXT_RELATIVE_PATHS = {
|
|
|
68
68
|
},
|
|
69
69
|
// `~/.personal-agent/integrations.md` moved under `policies/`.
|
|
70
70
|
integrations: "policies/integrations.md",
|
|
71
|
+
// Feedback Learning Loop (FEEDBACK_LEARNING_LOOP_DESIGN.md §3.3) —
|
|
72
|
+
// global `agent`-scope lessons store, lazy-created on first nightly
|
|
73
|
+
// consolidation write. Per-agent (`agent:<slug>`) lessons live next to
|
|
74
|
+
// the agent definition under `policies/agents/<slug>/lessons.md` (Phase 4).
|
|
75
|
+
agentLessons: "policies/agent-lessons.md",
|
|
71
76
|
// User-registered skill bundles (lazy-created).
|
|
72
77
|
skillsDir: "policies/skills",
|
|
73
78
|
// ── plans/ ← projects/ + roadmap.md ──────────────────────────
|
|
@@ -221,6 +226,17 @@ export function gitRepoJournalPath(slug, dateStr) {
|
|
|
221
226
|
export function customRoutinePath(slug) {
|
|
222
227
|
return `policies/routines/custom/${slug}.md`;
|
|
223
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Relative path to an Agent's per-agent (`agent:<slug>`) feedback lessons store
|
|
231
|
+
* (FEEDBACK_LEARNING_LOOP_DESIGN.md §3.3, Phase 4). Sits next to the agent
|
|
232
|
+
* definition at `policies/agents/<slug>/agent.md`; lazy-created on the first
|
|
233
|
+
* nightly consolidation write and injected only into that agent's own
|
|
234
|
+
* executions. The slug is assumed pre-validated (`isSafeAgentSlug`); this
|
|
235
|
+
* helper does not re-validate — it only composes the canonical path.
|
|
236
|
+
*/
|
|
237
|
+
export function agentLessonsPath(slug) {
|
|
238
|
+
return `policies/agents/${slug}/lessons.md`;
|
|
239
|
+
}
|
|
224
240
|
/**
|
|
225
241
|
* Relative path to a dossier file for a given flow slug.
|
|
226
242
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseCustomRoutineSpec } from "../custom-
|
|
1
|
+
import { parseCustomRoutineSpec } from "../custom-routines.js";
|
|
2
2
|
import { validateContextFileFrontmatter } from "../context-frontmatter.js";
|
|
3
3
|
import { isAgentDefinitionPath, validateAgentDefinitionMarkdown, } from "../agents/validate-agent-md.js";
|
|
4
4
|
import { normalizeRoadmapForWrite, validateRoadmap, validateRoadmapTransition, } from "../roadmap-validate.js";
|
|
@@ -80,19 +80,6 @@ export interface VaultPathAlias {
|
|
|
80
80
|
* `observations.payload`, and `messages.metadata`.
|
|
81
81
|
*/
|
|
82
82
|
export declare const VAULT_PATH_ALIASES: readonly VaultPathAlias[];
|
|
83
|
-
/**
|
|
84
|
-
* Translate a vault-relative path from its legacy spelling to its
|
|
85
|
-
* canonical six-class spelling. Returns the input verbatim (with
|
|
86
|
-
* `aliased=false`) if no rule applies.
|
|
87
|
-
*
|
|
88
|
-
* Idempotent: calling the resolver on an already-canonical path is a
|
|
89
|
-
* no-op. This is what makes it safe to invoke unconditionally at every
|
|
90
|
-
* entry point.
|
|
91
|
-
*
|
|
92
|
-
* The resolver does not validate that the resulting path is reachable
|
|
93
|
-
* on disk — that is the caller's job (e.g. `safePath` for the HTTP
|
|
94
|
-
* route). The job here is purely string translation.
|
|
95
|
-
*/
|
|
96
83
|
export declare function aliasVaultPath(relativePath: string): VaultPathAliasResult;
|
|
97
84
|
/**
|
|
98
85
|
* Diagnostic helper — returns every alias entry whose `fromPrefix` would
|
|
@@ -44,6 +44,24 @@
|
|
|
44
44
|
*/
|
|
45
45
|
export const VAULT_PATH_ALIASES = [
|
|
46
46
|
// Directory-prefix renames. Longest first.
|
|
47
|
+
// v0.1.10 → v0.1.11 "Hourly Check" → "Activity Scan" rename: the legacy
|
|
48
|
+
// pre-vault-v2 spellings must land on the NEW canonical file rather than
|
|
49
|
+
// the retired `…/hourly.md`, so these exact-file entries sit before the
|
|
50
|
+
// generic `routines/` / `dossiers/` prefix rules. The already-canonical
|
|
51
|
+
// old spellings (`policies/routines/hourly.md`, `knowledge/dossiers/
|
|
52
|
+
// hourly.md`) are handled by RENAMED_CANONICAL_FILES instead — the
|
|
53
|
+
// canonical fast-path in `aliasVaultPath` would short-circuit before
|
|
54
|
+
// this table is consulted.
|
|
55
|
+
{
|
|
56
|
+
fromPrefix: "routines/hourly",
|
|
57
|
+
toPrefix: "policies/routines/activity-scan",
|
|
58
|
+
exactOnly: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
fromPrefix: "dossiers/hourly",
|
|
62
|
+
toPrefix: "knowledge/dossiers/activity-scan",
|
|
63
|
+
exactOnly: true,
|
|
64
|
+
},
|
|
47
65
|
// `rules/policies/` (legacy capture dir) before `rules/`.
|
|
48
66
|
{ fromPrefix: "rules/policies/", toPrefix: "policies/management-captures/" },
|
|
49
67
|
// `agent/scratch/` before `agent/`.
|
|
@@ -123,11 +141,30 @@ const DOMAIN_ENTITY_RE = new RegExp(`^(?<domain>${MANAGEMENT_DOMAIN_NAMES.join("
|
|
|
123
141
|
* on disk — that is the caller's job (e.g. `safePath` for the HTTP
|
|
124
142
|
* route). The job here is purely string translation.
|
|
125
143
|
*/
|
|
144
|
+
/**
|
|
145
|
+
* Canonical-shaped paths that were RENAMED in place (same class, new file
|
|
146
|
+
* name). Checked before the canonical fast-path — a renamed file's old
|
|
147
|
+
* spelling is otherwise indistinguishable from a valid canonical path.
|
|
148
|
+
* v0.1.10 → v0.1.11: the "Hourly Check" agent became "Activity Scan";
|
|
149
|
+
* migration 0010 renames the files on disk, these entries keep old
|
|
150
|
+
* skill/curl paths working for a deprecation window.
|
|
151
|
+
*/
|
|
152
|
+
const RENAMED_CANONICAL_FILES = {
|
|
153
|
+
"policies/routines/hourly": "policies/routines/activity-scan",
|
|
154
|
+
"policies/routines/hourly.md": "policies/routines/activity-scan.md",
|
|
155
|
+
"knowledge/dossiers/hourly": "knowledge/dossiers/activity-scan",
|
|
156
|
+
"knowledge/dossiers/hourly.md": "knowledge/dossiers/activity-scan.md",
|
|
157
|
+
};
|
|
126
158
|
export function aliasVaultPath(relativePath) {
|
|
127
159
|
// Defensive normalisation — strip a leading slash so the resolver can
|
|
128
160
|
// be called with either form. Trailing slashes are preserved
|
|
129
161
|
// (callers like the migration manifest use `state/inbox/` as a dir).
|
|
130
162
|
const input = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
163
|
+
// In-place file renames — must precede the canonical fast-path.
|
|
164
|
+
const renamed = RENAMED_CANONICAL_FILES[input];
|
|
165
|
+
if (renamed !== undefined) {
|
|
166
|
+
return { canonicalPath: renamed, legacyPath: input, aliased: true };
|
|
167
|
+
}
|
|
131
168
|
// Already canonical — fast path.
|
|
132
169
|
if (isCanonicalSixClassPath(input)) {
|
|
133
170
|
return { canonicalPath: input, legacyPath: input, aliased: false };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { customRoutineSlugFromKey } from "@aitne/shared";
|
|
2
|
+
/**
|
|
3
|
+
* LEGACY custom-routine file format (B-007 §5.8 Q3) — parsing/enumeration
|
|
4
|
+
* helpers only.
|
|
5
|
+
*
|
|
6
|
+
* The subsystem that FIRED these files (`CustomRoutineScheduler`, a per-file
|
|
7
|
+
* node-cron job emitting `routine.custom.<slug>` events) was retired at the
|
|
8
|
+
* Agents-hub redesign (AGENTS_HUB_REDESIGN_PLAN.md §3): user-defined recurring
|
|
9
|
+
* work is a user Agent now (`agents` + `recurring_schedules`, visible on
|
|
10
|
+
* `/agents` with metrics and execution history). What remains here serves two
|
|
11
|
+
* callers:
|
|
12
|
+
*
|
|
13
|
+
* 1. `core/agents/custom-routine-migration.ts` — the one-time boot converter
|
|
14
|
+
* that turns each valid `policies/routines/custom/<slug>.md` into a user
|
|
15
|
+
* Agent definition.
|
|
16
|
+
* 2. `core/context-validation/` — writes under the legacy path are still
|
|
17
|
+
* validated against this format so existing files stay well-formed
|
|
18
|
+
* (they are inert post-migration; the prompt-injection branch in
|
|
19
|
+
* `policy-files.ts` remains for one release).
|
|
20
|
+
*/
|
|
21
|
+
export interface CustomRoutineSpec {
|
|
22
|
+
slug: string;
|
|
23
|
+
cron: string;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Canonical model tier. Normalized from frontmatter — both legacy
|
|
27
|
+
* `light`/`heavy` and current `lite`/`medium`/`high` strings are
|
|
28
|
+
* accepted at parse time (`light → medium`, `heavy → high`).
|
|
29
|
+
*/
|
|
30
|
+
backendTier: "lite" | "medium" | "high";
|
|
31
|
+
maxBudgetUsd: number;
|
|
32
|
+
processKey: string;
|
|
33
|
+
}
|
|
34
|
+
export type CustomRoutineParseError = {
|
|
35
|
+
kind: "missing_field";
|
|
36
|
+
field: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: "invalid_cron";
|
|
39
|
+
value: string;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "invalid_slug";
|
|
42
|
+
value: string;
|
|
43
|
+
} | {
|
|
44
|
+
kind: "invalid_type";
|
|
45
|
+
value: string;
|
|
46
|
+
} | {
|
|
47
|
+
kind: "invalid_process_key";
|
|
48
|
+
value: string;
|
|
49
|
+
} | {
|
|
50
|
+
kind: "invalid_enabled";
|
|
51
|
+
value: string;
|
|
52
|
+
} | {
|
|
53
|
+
kind: "invalid_tier";
|
|
54
|
+
value: string;
|
|
55
|
+
} | {
|
|
56
|
+
kind: "invalid_budget";
|
|
57
|
+
value: string;
|
|
58
|
+
} | {
|
|
59
|
+
kind: "missing_checks_section";
|
|
60
|
+
} | {
|
|
61
|
+
kind: "no_frontmatter";
|
|
62
|
+
};
|
|
63
|
+
export interface CustomRoutineEnumerationResult {
|
|
64
|
+
specs: CustomRoutineSpec[];
|
|
65
|
+
errors: {
|
|
66
|
+
slug: string;
|
|
67
|
+
error: CustomRoutineParseError;
|
|
68
|
+
}[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse a `policies/routines/custom/<slug>.md` file body into a validated spec.
|
|
72
|
+
* Pure function — safe to unit-test exhaustively. Returns a discriminated
|
|
73
|
+
* result so callers can log structured errors without throwing.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseCustomRoutineSpec(slug: string, body: string): {
|
|
76
|
+
ok: true;
|
|
77
|
+
spec: CustomRoutineSpec;
|
|
78
|
+
} | {
|
|
79
|
+
ok: false;
|
|
80
|
+
error: CustomRoutineParseError;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Enumerate every `policies/routines/custom/*.md` file under `contextDir` and
|
|
84
|
+
* parse each into a spec. Errors are returned alongside the successful
|
|
85
|
+
* specs so callers can log them without aborting.
|
|
86
|
+
*
|
|
87
|
+
* The readers are injectable for tests — by default they read from disk; a
|
|
88
|
+
* missing directory yields empty results.
|
|
89
|
+
*/
|
|
90
|
+
export declare function enumerateCustomRoutines(contextDir: string, options?: {
|
|
91
|
+
readDir?: (dir: string) => string[];
|
|
92
|
+
readFile?: (path: string) => string;
|
|
93
|
+
}): CustomRoutineEnumerationResult;
|
|
94
|
+
/**
|
|
95
|
+
* Convenience: extract the slug from a `policies/routines/custom/<slug>.md` path.
|
|
96
|
+
* Returns null if the path is outside the custom-routine directory.
|
|
97
|
+
*/
|
|
98
|
+
export declare function slugFromCustomRoutinePath(relativePath: string): string | null;
|
|
99
|
+
export { customRoutineSlugFromKey };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import cron from "node-cron";
|
|
4
|
+
import { customRoutineKey, customRoutineSlugFromKey } from "@aitne/shared";
|
|
5
|
+
import { CONTEXT_RELATIVE_PATHS } from "./context-paths.js";
|
|
6
|
+
/**
|
|
7
|
+
* Extract the frontmatter body between the opening and closing `---`
|
|
8
|
+
* delimiters. Returns null when the file has no YAML frontmatter.
|
|
9
|
+
*/
|
|
10
|
+
function extractFrontmatter(content) {
|
|
11
|
+
if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const afterOpen = content.startsWith("---\r\n") ? 5 : 4;
|
|
15
|
+
const endIdx = content.indexOf("\n---", afterOpen - 1);
|
|
16
|
+
if (endIdx < 0)
|
|
17
|
+
return null;
|
|
18
|
+
return content.slice(afterOpen, endIdx);
|
|
19
|
+
}
|
|
20
|
+
function readScalar(frontmatter, field) {
|
|
21
|
+
const re = new RegExp(`^${field}\\s*:\\s*(.+?)\\s*$`, "m");
|
|
22
|
+
const m = frontmatter.match(re);
|
|
23
|
+
if (!m)
|
|
24
|
+
return null;
|
|
25
|
+
let v = m[1].trim();
|
|
26
|
+
// Strip surrounding quotes (single or double).
|
|
27
|
+
if ((v.startsWith('"') && v.endsWith('"')) ||
|
|
28
|
+
(v.startsWith("'") && v.endsWith("'"))) {
|
|
29
|
+
v = v.slice(1, -1);
|
|
30
|
+
}
|
|
31
|
+
return v;
|
|
32
|
+
}
|
|
33
|
+
function hasChecksSection(content) {
|
|
34
|
+
return /^##\s+Checks\s*$/m.test(content);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a `policies/routines/custom/<slug>.md` file body into a validated spec.
|
|
38
|
+
* Pure function — safe to unit-test exhaustively. Returns a discriminated
|
|
39
|
+
* result so callers can log structured errors without throwing.
|
|
40
|
+
*/
|
|
41
|
+
export function parseCustomRoutineSpec(slug, body) {
|
|
42
|
+
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(slug) || slug.length > 64) {
|
|
43
|
+
return { ok: false, error: { kind: "invalid_slug", value: slug } };
|
|
44
|
+
}
|
|
45
|
+
const fm = extractFrontmatter(body);
|
|
46
|
+
if (fm === null) {
|
|
47
|
+
return { ok: false, error: { kind: "no_frontmatter" } };
|
|
48
|
+
}
|
|
49
|
+
const typeRaw = readScalar(fm, "type");
|
|
50
|
+
if (!typeRaw) {
|
|
51
|
+
return { ok: false, error: { kind: "missing_field", field: "type" } };
|
|
52
|
+
}
|
|
53
|
+
if (typeRaw !== "rule") {
|
|
54
|
+
return { ok: false, error: { kind: "invalid_type", value: typeRaw } };
|
|
55
|
+
}
|
|
56
|
+
const slugRaw = readScalar(fm, "slug");
|
|
57
|
+
if (!slugRaw) {
|
|
58
|
+
return { ok: false, error: { kind: "missing_field", field: "slug" } };
|
|
59
|
+
}
|
|
60
|
+
if (slugRaw !== slug) {
|
|
61
|
+
return { ok: false, error: { kind: "invalid_slug", value: slugRaw } };
|
|
62
|
+
}
|
|
63
|
+
const processKeyRaw = readScalar(fm, "process_key");
|
|
64
|
+
if (!processKeyRaw) {
|
|
65
|
+
return { ok: false, error: { kind: "missing_field", field: "process_key" } };
|
|
66
|
+
}
|
|
67
|
+
if (processKeyRaw !== customRoutineKey(slug)) {
|
|
68
|
+
return { ok: false, error: { kind: "invalid_process_key", value: processKeyRaw } };
|
|
69
|
+
}
|
|
70
|
+
const cronExpr = readScalar(fm, "cron");
|
|
71
|
+
if (!cronExpr) {
|
|
72
|
+
return { ok: false, error: { kind: "missing_field", field: "cron" } };
|
|
73
|
+
}
|
|
74
|
+
if (!cron.validate(cronExpr)) {
|
|
75
|
+
return { ok: false, error: { kind: "invalid_cron", value: cronExpr } };
|
|
76
|
+
}
|
|
77
|
+
const tierRaw = readScalar(fm, "backend_tier");
|
|
78
|
+
if (!tierRaw) {
|
|
79
|
+
return { ok: false, error: { kind: "missing_field", field: "backend_tier" } };
|
|
80
|
+
}
|
|
81
|
+
// Accept the legacy two-tier names ("light" / "heavy") and the canonical
|
|
82
|
+
// three-tier names ("lite" / "medium" / "high"). Legacy "light" maps to
|
|
83
|
+
// Sonnet (medium) and "heavy" to Opus (high), preserving behavior of
|
|
84
|
+
// user-authored routine files written before the rename.
|
|
85
|
+
const tierAliasMap = {
|
|
86
|
+
"lite": "lite",
|
|
87
|
+
"medium": "medium",
|
|
88
|
+
"high": "high",
|
|
89
|
+
"light": "medium",
|
|
90
|
+
"heavy": "high",
|
|
91
|
+
};
|
|
92
|
+
const normalizedTier = tierAliasMap[tierRaw];
|
|
93
|
+
if (!normalizedTier) {
|
|
94
|
+
return { ok: false, error: { kind: "invalid_tier", value: tierRaw } };
|
|
95
|
+
}
|
|
96
|
+
const budgetRaw = readScalar(fm, "max_budget_usd");
|
|
97
|
+
if (!budgetRaw) {
|
|
98
|
+
return { ok: false, error: { kind: "missing_field", field: "max_budget_usd" } };
|
|
99
|
+
}
|
|
100
|
+
const budget = Number(budgetRaw);
|
|
101
|
+
if (!Number.isFinite(budget) || budget <= 0) {
|
|
102
|
+
return { ok: false, error: { kind: "invalid_budget", value: budgetRaw } };
|
|
103
|
+
}
|
|
104
|
+
const enabledRaw = readScalar(fm, "enabled");
|
|
105
|
+
if (!enabledRaw) {
|
|
106
|
+
return { ok: false, error: { kind: "missing_field", field: "enabled" } };
|
|
107
|
+
}
|
|
108
|
+
if (enabledRaw !== "true" && enabledRaw !== "false") {
|
|
109
|
+
return { ok: false, error: { kind: "invalid_enabled", value: enabledRaw } };
|
|
110
|
+
}
|
|
111
|
+
const enabled = enabledRaw === "true";
|
|
112
|
+
if (!hasChecksSection(body)) {
|
|
113
|
+
return { ok: false, error: { kind: "missing_checks_section" } };
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
ok: true,
|
|
117
|
+
spec: {
|
|
118
|
+
slug,
|
|
119
|
+
cron: cronExpr,
|
|
120
|
+
enabled,
|
|
121
|
+
backendTier: normalizedTier,
|
|
122
|
+
maxBudgetUsd: budget,
|
|
123
|
+
processKey: customRoutineKey(slug),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Enumerate every `policies/routines/custom/*.md` file under `contextDir` and
|
|
129
|
+
* parse each into a spec. Errors are returned alongside the successful
|
|
130
|
+
* specs so callers can log them without aborting.
|
|
131
|
+
*
|
|
132
|
+
* The readers are injectable for tests — by default they read from disk; a
|
|
133
|
+
* missing directory yields empty results.
|
|
134
|
+
*/
|
|
135
|
+
export function enumerateCustomRoutines(contextDir, options) {
|
|
136
|
+
const dir = join(contextDir, CONTEXT_RELATIVE_PATHS.routines.customDir);
|
|
137
|
+
const readDir = options?.readDir ?? defaultReadDir;
|
|
138
|
+
const readFile = options?.readFile ?? defaultReadFile;
|
|
139
|
+
const files = readDir(dir);
|
|
140
|
+
const specs = [];
|
|
141
|
+
const errors = [];
|
|
142
|
+
for (const fileName of files) {
|
|
143
|
+
if (!fileName.endsWith(".md"))
|
|
144
|
+
continue;
|
|
145
|
+
const slug = fileName.slice(0, -3);
|
|
146
|
+
let body;
|
|
147
|
+
try {
|
|
148
|
+
body = readFile(join(dir, fileName));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const result = parseCustomRoutineSpec(slug, body);
|
|
154
|
+
if (result.ok) {
|
|
155
|
+
specs.push(result.spec);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
errors.push({ slug, error: result.error });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { specs, errors };
|
|
162
|
+
}
|
|
163
|
+
function defaultReadDir(dir) {
|
|
164
|
+
if (!existsSync(dir))
|
|
165
|
+
return [];
|
|
166
|
+
return readdirSync(dir);
|
|
167
|
+
}
|
|
168
|
+
function defaultReadFile(path) {
|
|
169
|
+
return readFileSync(path, "utf-8");
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Convenience: extract the slug from a `policies/routines/custom/<slug>.md` path.
|
|
173
|
+
* Returns null if the path is outside the custom-routine directory.
|
|
174
|
+
*/
|
|
175
|
+
export function slugFromCustomRoutinePath(relativePath) {
|
|
176
|
+
const prefix = `${CONTEXT_RELATIVE_PATHS.routines.customDir}/`;
|
|
177
|
+
if (!relativePath.startsWith(prefix))
|
|
178
|
+
return null;
|
|
179
|
+
const rest = relativePath.slice(prefix.length);
|
|
180
|
+
if (!rest.endsWith(".md"))
|
|
181
|
+
return null;
|
|
182
|
+
const slug = rest.slice(0, -3);
|
|
183
|
+
if (slug.includes("/"))
|
|
184
|
+
return null;
|
|
185
|
+
return slug;
|
|
186
|
+
}
|
|
187
|
+
export { customRoutineSlugFromKey };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { delimiter, dirname, join } from "node:path";
|
|
3
|
+
import { BACKEND_IDS, getManagedApiKeyEnvVars, } from "@aitne/shared";
|
|
3
4
|
export const SESSION_DAEMON_API_BIN_DIR = join(".pa", "bin");
|
|
4
5
|
export const SESSION_DAEMON_API_CLI_REL_PATH = join(SESSION_DAEMON_API_BIN_DIR, "pa-api");
|
|
5
6
|
export const SESSION_DAEMON_CURL_SHIM_REL_PATH = join(SESSION_DAEMON_API_BIN_DIR, "curl");
|
|
@@ -708,6 +709,50 @@ export function ensureDaemonApiCli(sessionDir) {
|
|
|
708
709
|
/* c8 ignore stop */
|
|
709
710
|
return cliPath;
|
|
710
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Daemon-internal secrets that must NEVER be inherited by an agent
|
|
714
|
+
* subprocess. Agents run with bypassPermissions and have Bash; `env` /
|
|
715
|
+
* `printenv` / `process.env` cannot be blocked at the tool layer, so a
|
|
716
|
+
* prompt-injected session could exfiltrate anything in its environment.
|
|
717
|
+
* `PA_MASTER_PASSWORD` decrypts the *entire* file-fallback secret store and
|
|
718
|
+
* has no legitimate use in a child process.
|
|
719
|
+
*/
|
|
720
|
+
const ALWAYS_STRIP_FROM_CHILD_ENV = ["PA_MASTER_PASSWORD"];
|
|
721
|
+
function isBackendId(value) {
|
|
722
|
+
return BACKEND_IDS.includes(value);
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Strip credentials a child agent must not see from a copied env:
|
|
726
|
+
* - daemon-internal secrets (master password) — always.
|
|
727
|
+
* - inactive backends' provider API keys — when the active backend is
|
|
728
|
+
* known. The keychain mirror exports every configured backend's provider
|
|
729
|
+
* key into the daemon's `process.env` globally, so without this a Claude
|
|
730
|
+
* session's env also carries the user's OpenAI / Gemini / OpenCode
|
|
731
|
+
* credentials. Scoping the keys to the backend that actually uses them
|
|
732
|
+
* keeps a prompt-injected child from exfiltrating credentials it never
|
|
733
|
+
* needs. Only applied when `sessionBackend` is a recognised backend id;
|
|
734
|
+
* an absent/unknown value leaves the env untouched (no behaviour change
|
|
735
|
+
* for non-agent spawns).
|
|
736
|
+
*/
|
|
737
|
+
function scrubSensitiveChildEnv(env, sessionBackend) {
|
|
738
|
+
for (const name of ALWAYS_STRIP_FROM_CHILD_ENV) {
|
|
739
|
+
delete env[name];
|
|
740
|
+
}
|
|
741
|
+
if (!sessionBackend || !isBackendId(sessionBackend))
|
|
742
|
+
return;
|
|
743
|
+
const activeVars = new Set(getManagedApiKeyEnvVars(sessionBackend));
|
|
744
|
+
for (const backendId of BACKEND_IDS) {
|
|
745
|
+
if (backendId === sessionBackend)
|
|
746
|
+
continue;
|
|
747
|
+
for (const name of getManagedApiKeyEnvVars(backendId)) {
|
|
748
|
+
// Keep any var the active backend also relies on (shared across
|
|
749
|
+
// providers, e.g. a common cloud credential).
|
|
750
|
+
if (activeVars.has(name))
|
|
751
|
+
continue;
|
|
752
|
+
delete env[name];
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
711
756
|
export function buildDaemonApiCliEnv(sessionDir, apiPort, optionsOrReadToken) {
|
|
712
757
|
const options = typeof optionsOrReadToken === "string"
|
|
713
758
|
? { readToken: optionsOrReadToken }
|
|
@@ -717,10 +762,14 @@ export function buildDaemonApiCliEnv(sessionDir, apiPort, optionsOrReadToken) {
|
|
|
717
762
|
pathParts.push(process.env.PATH);
|
|
718
763
|
}
|
|
719
764
|
const env = {
|
|
720
|
-
...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string")),
|
|
765
|
+
...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string" && entry[0].toUpperCase() !== "PATH")),
|
|
721
766
|
PATH: pathParts.join(delimiter),
|
|
722
767
|
[DAEMON_API_BASE_URL_ENV]: `http://127.0.0.1:${apiPort}`,
|
|
723
768
|
};
|
|
769
|
+
// Remove daemon-internal secrets and inactive-backend provider keys before
|
|
770
|
+
// the child ever sees them. Must run after the process.env copy and before
|
|
771
|
+
// the PA_* identity vars below (which the child legitimately needs).
|
|
772
|
+
scrubSensitiveChildEnv(env, options.sessionBackend);
|
|
724
773
|
if (options.readToken) {
|
|
725
774
|
env[DAEMON_API_READ_TOKEN_ENV] = options.readToken;
|
|
726
775
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Day-boundary task runner with a per-agent-day idempotence marker —
|
|
3
|
+
* RESEARCH_CLUSTER_COST_FIX_PLAN.md F2 (defense in depth on top of the
|
|
4
|
+
* F1 per-cluster enqueue stamp).
|
|
5
|
+
*
|
|
6
|
+
* The scheduler invokes its day-boundary callback from THREE sites: the
|
|
7
|
+
* 04:00 cron, wake catch-up (which fires on every detected sleep gap
|
|
8
|
+
* >= 5 min — every macOS maintenance DarkWake), and the morning
|
|
9
|
+
* self-heal missed-fire path. Before this marker existed, each replay
|
|
10
|
+
* re-ran the full callback body; on 2026-06-11 that re-enqueued the
|
|
11
|
+
* same research cluster ~25x in one morning. Wrapping the body HERE —
|
|
12
|
+
* the single composition point — protects every future day-boundary
|
|
13
|
+
* addition, not just the research fan-out.
|
|
14
|
+
*
|
|
15
|
+
* Marker semantics: `runtime_state.day_boundary_last_agent_day` is
|
|
16
|
+
* written AFTER the body completes, not before. A sleep-interrupted or
|
|
17
|
+
* failed body therefore retries on the next scheduler fire (all three
|
|
18
|
+
* scheduler sites catch + log callback rejections), while replay safety
|
|
19
|
+
* of the individual steps comes from the steps themselves — the F1
|
|
20
|
+
* stamp for the fan-out, summarizeDmSessions' own incremental gating.
|
|
21
|
+
*/
|
|
22
|
+
import type Database from "better-sqlite3";
|
|
23
|
+
export declare const DAY_BOUNDARY_LAST_AGENT_DAY_KEY = "day_boundary_last_agent_day";
|
|
24
|
+
export interface DayBoundaryTasksDeps {
|
|
25
|
+
db: Database.Database;
|
|
26
|
+
/** Local agent-day label ('YYYY-MM-DD') the caller computes via
|
|
27
|
+
* `getAgentDayDateStr(config.timezone, config.dayBoundaryHour)`. */
|
|
28
|
+
todayAgentDay: string;
|
|
29
|
+
summarizeDmSessions: () => Promise<void>;
|
|
30
|
+
fanoutResearchClusterUpdates: () => Promise<{
|
|
31
|
+
enqueuedSlugs: string[];
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export type DayBoundaryTasksResult = {
|
|
35
|
+
ran: false;
|
|
36
|
+
} | {
|
|
37
|
+
ran: true;
|
|
38
|
+
enqueuedSlugs: string[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Run the day-boundary body at most once per agent-day. Returns
|
|
42
|
+
* `{ ran: false }` when the marker shows the body already completed for
|
|
43
|
+
* `todayAgentDay`. Errors propagate to the caller WITHOUT writing the
|
|
44
|
+
* marker, so the next scheduler fire retries the whole body.
|
|
45
|
+
*/
|
|
46
|
+
export declare function runDayBoundaryTasks(deps: DayBoundaryTasksDeps): Promise<DayBoundaryTasksResult>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Day-boundary task runner with a per-agent-day idempotence marker —
|
|
3
|
+
* RESEARCH_CLUSTER_COST_FIX_PLAN.md F2 (defense in depth on top of the
|
|
4
|
+
* F1 per-cluster enqueue stamp).
|
|
5
|
+
*
|
|
6
|
+
* The scheduler invokes its day-boundary callback from THREE sites: the
|
|
7
|
+
* 04:00 cron, wake catch-up (which fires on every detected sleep gap
|
|
8
|
+
* >= 5 min — every macOS maintenance DarkWake), and the morning
|
|
9
|
+
* self-heal missed-fire path. Before this marker existed, each replay
|
|
10
|
+
* re-ran the full callback body; on 2026-06-11 that re-enqueued the
|
|
11
|
+
* same research cluster ~25x in one morning. Wrapping the body HERE —
|
|
12
|
+
* the single composition point — protects every future day-boundary
|
|
13
|
+
* addition, not just the research fan-out.
|
|
14
|
+
*
|
|
15
|
+
* Marker semantics: `runtime_state.day_boundary_last_agent_day` is
|
|
16
|
+
* written AFTER the body completes, not before. A sleep-interrupted or
|
|
17
|
+
* failed body therefore retries on the next scheduler fire (all three
|
|
18
|
+
* scheduler sites catch + log callback rejections), while replay safety
|
|
19
|
+
* of the individual steps comes from the steps themselves — the F1
|
|
20
|
+
* stamp for the fan-out, summarizeDmSessions' own incremental gating.
|
|
21
|
+
*/
|
|
22
|
+
import { readRuntimeState, writeRuntimeState } from "../db/runtime-state.js";
|
|
23
|
+
export const DAY_BOUNDARY_LAST_AGENT_DAY_KEY = "day_boundary_last_agent_day";
|
|
24
|
+
/**
|
|
25
|
+
* Run the day-boundary body at most once per agent-day. Returns
|
|
26
|
+
* `{ ran: false }` when the marker shows the body already completed for
|
|
27
|
+
* `todayAgentDay`. Errors propagate to the caller WITHOUT writing the
|
|
28
|
+
* marker, so the next scheduler fire retries the whole body.
|
|
29
|
+
*/
|
|
30
|
+
export async function runDayBoundaryTasks(deps) {
|
|
31
|
+
const { db, todayAgentDay } = deps;
|
|
32
|
+
const lastRunAgentDay = readRuntimeState(db, DAY_BOUNDARY_LAST_AGENT_DAY_KEY);
|
|
33
|
+
if (lastRunAgentDay === todayAgentDay) {
|
|
34
|
+
return { ran: false };
|
|
35
|
+
}
|
|
36
|
+
await deps.summarizeDmSessions();
|
|
37
|
+
const fanout = await deps.fanoutResearchClusterUpdates();
|
|
38
|
+
writeRuntimeState(db, DAY_BOUNDARY_LAST_AGENT_DAY_KEY, todayAgentDay);
|
|
39
|
+
return { ran: true, enqueuedSlugs: fanout.enqueuedSlugs };
|
|
40
|
+
}
|