@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
|
@@ -5,7 +5,7 @@ import { composeSkillSet, getProfileForEvent, getProfileForProcess, resolveSkill
|
|
|
5
5
|
import { applyCharacterBlockRewrite, buildCharacterBlock } from "./character-block.js";
|
|
6
6
|
import { applyOutputLanguagePointerRewrite } from "./output-language-policy.js";
|
|
7
7
|
import { createLogger } from "../logging.js";
|
|
8
|
-
import {
|
|
8
|
+
import { loadSlimSystemPrompt } from "./slim-system-prompt-loader.js";
|
|
9
9
|
import { substituteIntegrationRoutingTables } from "./management-md.js";
|
|
10
10
|
import { loadCurationDeclaration, } from "./skill-curation/declarations.js";
|
|
11
11
|
import { resolveBuiltinSkillDir } from "./skill-source-paths.js";
|
|
@@ -21,18 +21,43 @@ import { cliInstructionFileName, cliSkillsDirName, prependCodexReadSensitiveBann
|
|
|
21
21
|
// spliceCurationAnchorsInSkill). Not exported — sibling modules carry
|
|
22
22
|
// their own peer loggers; the test suite spies on those.
|
|
23
23
|
const logger = createLogger("skills-compiler");
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
24
|
+
// Per-slim-key CLI skill bundle copied into the slim Codex / Gemini session
|
|
25
|
+
// (RESEARCH_CLUSTER_COST_FIX_PLAN.md F4 generalizes fetch-window-cost-reduction.md
|
|
26
|
+
// Phase 1.5). The slim materializer writes the slim system-prompt body — the
|
|
27
|
+
// SAME template the Claude SDK gets, via the shared `slim-system-prompt-loader`
|
|
28
|
+
// registry — as AGENTS.md / GEMINI.md and copies ONLY these skill dirs; the
|
|
29
|
+
// wide manifest's other slugs are restated by the task-flow / user prompt, so
|
|
30
|
+
// inlining them would re-bloat the session.
|
|
31
|
+
//
|
|
32
|
+
// Keys MUST mirror `SLIM_SYSTEM_PROMPT_LOADERS` (drift-guarded by a unit test):
|
|
33
|
+
// a slim system prompt with no CLI skill set — or vice versa — is a wiring bug.
|
|
34
|
+
// Typed `Partial<Record<ProcessKey, …>>` so a key rename in
|
|
35
|
+
// `@aitne/shared/process-key.ts` lights up here rather than dead-branching.
|
|
36
|
+
// - routine.fetch_window — the `observations` SKILL.md is the
|
|
37
|
+
// `/api/observations/batch` POST contract and the fetcher's only
|
|
38
|
+
// structural assertion; the integration partial the runner inlines covers
|
|
39
|
+
// the rest (`mail` / `notion` / `external-services` / `attach` dropped).
|
|
40
|
+
// - routine.research_cluster_update — `browser-history` (the
|
|
41
|
+
// `/api/browser-history/*` curl surface) + `context` (the research-journal
|
|
42
|
+
// write path); the task-flow inlines the endpoints + journal template.
|
|
43
|
+
export const SLIM_CLI_SKILL_SETS = {
|
|
44
|
+
"routine.fetch_window": ["observations"],
|
|
45
|
+
"routine.research_cluster_update": ["browser-history", "context"],
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The slim process key for this session, or null when it is not a slim key.
|
|
49
|
+
* Accepts the `processKey ?? eventType` union the CLI materializer threads
|
|
50
|
+
* (an event-type string that is not a registered ProcessKey simply misses the
|
|
51
|
+
* `in` check). Keyed off `SLIM_CLI_SKILL_SETS`, which the drift guard pins to
|
|
52
|
+
* `SLIM_SYSTEM_PROMPT_LOADERS`.
|
|
53
|
+
*/
|
|
54
|
+
function slimCliKeyFor(processKeyOrEvent) {
|
|
55
|
+
if (processKeyOrEvent === undefined)
|
|
56
|
+
return null;
|
|
57
|
+
return processKeyOrEvent in SLIM_CLI_SKILL_SETS
|
|
58
|
+
? processKeyOrEvent
|
|
59
|
+
: null;
|
|
60
|
+
}
|
|
36
61
|
/**
|
|
37
62
|
* Materializes backend-specific instruction files for session workdirs
|
|
38
63
|
* by reading directly from the source tree (agent-assets/agent-profiles/ and
|
|
@@ -238,15 +263,17 @@ export class SkillsCompiler {
|
|
|
238
263
|
else {
|
|
239
264
|
this.materializeCliSession(params.sessionDir, profileName, skills, params.backendId, params.processKey ?? params.eventType, profileBodyOverride, params.wikiWorkspaceName);
|
|
240
265
|
}
|
|
241
|
-
//
|
|
242
|
-
// CLI materializer drops every manifest skill except
|
|
243
|
-
// The Claude path
|
|
244
|
-
// the system prompt, not the
|
|
245
|
-
// actually written so log lines /
|
|
246
|
-
// manifest's pre-narrow list.
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
266
|
+
// fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
|
|
267
|
+
// F4 — the slim CLI materializer drops every manifest skill except the
|
|
268
|
+
// key's `SLIM_CLI_SKILL_SETS` bundle. The Claude path stays wide today
|
|
269
|
+
// (Phase 1/F4 swap only the system prompt + settingSources, not the
|
|
270
|
+
// workdir layout). Report what was actually written so log lines /
|
|
271
|
+
// callers see truth, not the manifest's pre-narrow list.
|
|
272
|
+
const slimCliKey = params.backendId !== "claude"
|
|
273
|
+
? slimCliKeyFor(params.processKey ?? params.eventType)
|
|
274
|
+
: null;
|
|
275
|
+
const effectiveSkills = slimCliKey
|
|
276
|
+
? [...(SLIM_CLI_SKILL_SETS[slimCliKey] ?? [])]
|
|
250
277
|
: skills;
|
|
251
278
|
return { profile: profileName, skills: effectiveSkills };
|
|
252
279
|
}
|
|
@@ -689,95 +716,104 @@ export class SkillsCompiler {
|
|
|
689
716
|
}
|
|
690
717
|
/**
|
|
691
718
|
* docs/design/appendices/skills-unification.md Phase 1 item 15 — the slim path does NOT
|
|
692
|
-
* emit a `<skill-index>` block or the skill-discovery preamble. The
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
* the
|
|
696
|
-
*
|
|
697
|
-
* fetcher to scan for skills before executing the acquisition plan.
|
|
719
|
+
* emit a `<skill-index>` block or the skill-discovery preamble. The slim
|
|
720
|
+
* system prompt is a self-contained operational contract and its copied
|
|
721
|
+
* skills are referenced inline by the dispatched user prompt / task-flow.
|
|
722
|
+
* Adding the index would mis-signal the agent to scan for skills before
|
|
723
|
+
* executing its mechanical task.
|
|
698
724
|
*
|
|
699
|
-
*
|
|
700
|
-
* materializer for `
|
|
725
|
+
* fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
|
|
726
|
+
* F4 — slim instruction-file materializer for a `SLIM_CLI_SKILL_SETS` process
|
|
727
|
+
* key on Codex / Gemini CLI.
|
|
701
728
|
*
|
|
702
|
-
* Mirrors the Claude SDK's
|
|
703
|
-
* `agent-assets/system-prompts
|
|
704
|
-
*
|
|
705
|
-
* GEMINI.md and copy only the
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
* (`routine-fetch-window-runner.ts:reassemblePrompt`) covers the
|
|
709
|
-
* per-attempt call shape, so `mail` / `notion` / `external-services` /
|
|
710
|
-
* `attach` skill bodies are deliberately omitted.
|
|
729
|
+
* Mirrors the Claude SDK's slim-systemPrompt swap (the same
|
|
730
|
+
* `agent-assets/system-prompts/<key>.md` template is the single source of
|
|
731
|
+
* truth, read through the shared `loadSlimSystemPrompt` registry): write the
|
|
732
|
+
* slim body verbatim as AGENTS.md / GEMINI.md and copy only the key's
|
|
733
|
+
* `SLIM_CLI_SKILL_SETS` bundle — the wide manifest's other slugs are restated
|
|
734
|
+
* by the dispatched user prompt / task-flow, so their bodies are omitted.
|
|
711
735
|
*
|
|
712
|
-
* No safety preamble / character / behavioral-rules / daemon-API
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
* (absolute-block list, Codex sandbox, Gemini admin TOML) still applies
|
|
717
|
-
* unchanged at runtime.
|
|
736
|
+
* No safety preamble / character / behavioral-rules / daemon-API sections —
|
|
737
|
+
* the slim template restates the only rules the session needs. The
|
|
738
|
+
* destructive-action policy layer (absolute-block list, Codex sandbox,
|
|
739
|
+
* Gemini admin TOML) still applies unchanged at runtime.
|
|
718
740
|
*
|
|
719
741
|
* The `<mcp-servers>` section is appended downstream by
|
|
720
742
|
* `services/mcp/session-materializer.ts:appendMcpSection` exactly as on
|
|
721
|
-
* the wide path
|
|
722
|
-
* that section without further changes here.
|
|
743
|
+
* the wide path.
|
|
723
744
|
*/
|
|
724
|
-
|
|
745
|
+
materializeSlimCliSession(sessionDir, backendId, processKey) {
|
|
746
|
+
const skillSlugs = SLIM_CLI_SKILL_SETS[processKey];
|
|
747
|
+
// Defensive: the caller only diverts registered slim keys, but guard so a
|
|
748
|
+
// future divert bug fails loud rather than writing an empty session.
|
|
749
|
+
if (skillSlugs === undefined)
|
|
750
|
+
return;
|
|
751
|
+
const slim = loadSlimSystemPrompt(processKey);
|
|
752
|
+
if (slim === null)
|
|
753
|
+
return; // Unreachable for registered keys; satisfies the type.
|
|
725
754
|
mkdirSync(sessionDir, { recursive: true });
|
|
726
|
-
const slim = loadFetchWindowSystemPrompt();
|
|
727
755
|
writeFileSync(join(sessionDir, cliInstructionFileName(backendId)), slim, "utf-8");
|
|
728
|
-
// Copy ONLY the
|
|
729
|
-
//
|
|
756
|
+
// Copy ONLY the key's slim skill dirs. The wide path's prune step is
|
|
757
|
+
// replaced by the explicit slug list — `pruneStaleBuiltinSkillDirs`
|
|
730
758
|
// removes any other built-in skill dir that a prior re-materialization
|
|
731
759
|
// (e.g. a fallback-driven wide path on the same workdir) may have left.
|
|
732
760
|
const cliSkillsRoot = cliSkillsDirName(backendId);
|
|
733
761
|
if (cliSkillsRoot === null)
|
|
734
|
-
return; // Claude-only —
|
|
762
|
+
return; // Claude-only — slim keys run on Claude SDK natively.
|
|
735
763
|
const skillsRoot = this.getSourceSkillsRoot();
|
|
736
764
|
const destSkillsRoot = join(sessionDir, cliSkillsRoot, "skills");
|
|
737
765
|
mkdirSync(destSkillsRoot, { recursive: true });
|
|
738
|
-
pruneStaleBuiltinSkillDirs(destSkillsRoot, skillsRoot, [
|
|
739
|
-
const
|
|
766
|
+
pruneStaleBuiltinSkillDirs(destSkillsRoot, skillsRoot, [...skillSlugs]);
|
|
767
|
+
for (const slug of skillSlugs) {
|
|
768
|
+
this.copySlimSkillDir(destSkillsRoot, skillsRoot, slug, backendId);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Copy one built-in skill dir into a slim CLI session, applying the same
|
|
773
|
+
* adaptation pipeline the wide CLI path runs (brand tokens, partial /
|
|
774
|
+
* reference includes, integration-mode filter, tool-deny prose, curation
|
|
775
|
+
* anchors). Frontmatter stays intact (skills-unification.md §R6). A
|
|
776
|
+
* no-op when the source SKILL.md is absent.
|
|
777
|
+
*/
|
|
778
|
+
copySlimSkillDir(destSkillsRoot, skillsRoot, slug, backendId) {
|
|
779
|
+
const src = resolveBuiltinSkillDir(skillsRoot, slug);
|
|
740
780
|
const skillMdPath = join(src, "SKILL.md");
|
|
741
781
|
if (!existsSync(skillMdPath))
|
|
742
782
|
return;
|
|
743
|
-
const destDir = join(destSkillsRoot,
|
|
783
|
+
const destDir = join(destSkillsRoot, slug);
|
|
744
784
|
cpSync(src, destDir, { recursive: true });
|
|
745
785
|
substituteBrandTokensInDir(destDir);
|
|
746
|
-
// No `substituteWikiWorkspaceTokensInDir` —
|
|
747
|
-
//
|
|
748
|
-
// process keys anyway.
|
|
786
|
+
// No `substituteWikiWorkspaceTokensInDir` — no slim key touches wiki
|
|
787
|
+
// workspace state, and the resolver is a no-op for non-wiki keys anyway.
|
|
749
788
|
let adapted = substituteBrandTokens(readFileSync(skillMdPath, "utf-8"));
|
|
750
|
-
//
|
|
751
|
-
//
|
|
789
|
+
// Run the partial / reference resolvers even on bodies that ship no
|
|
790
|
+
// `{{> base }}` / `{{> ref:* }}` directives today, so a future curation
|
|
752
791
|
// edit cannot silently drop content. Idempotent on plain content.
|
|
753
792
|
adapted = substituteBrandTokens(renderPartialIncludes(adapted, join(src, "SKILL.base.md")));
|
|
754
793
|
adapted = substituteBrandTokens(renderReferenceIncludes(adapted, src));
|
|
755
|
-
// Mode-conditional filter — observations/SKILL.md
|
|
756
|
-
// `<!-- mode:<predicate
|
|
757
|
-
//
|
|
758
|
-
// `delegated-
|
|
759
|
-
//
|
|
760
|
-
//
|
|
761
|
-
// for every integration. Idempotent on bodies that lack markers.
|
|
794
|
+
// Mode-conditional filter — skills (e.g. observations/SKILL.md) carry
|
|
795
|
+
// `<!-- mode:<predicate>:<integration> -->` markers because the
|
|
796
|
+
// source / consume contract differs across `direct` / `delegated-same` /
|
|
797
|
+
// `delegated-cross` / `native` / `disabled`. The wide path applies this
|
|
798
|
+
// at `materializeCliSession`; the slim path must match so the agent
|
|
799
|
+
// doesn't get every mode's prose. Idempotent on bodies that lack markers.
|
|
762
800
|
adapted = applyIntegrationModeFilter(adapted, this.integrations, backendId);
|
|
763
|
-
// Tool-deny policy stays in force — even
|
|
764
|
-
//
|
|
765
|
-
//
|
|
766
|
-
adapted = applyAllDeniedToolsForSkill(adapted,
|
|
767
|
-
// docs/design/appendices/skills-unification.md Phase 1 §R6 — frontmatter stays intact
|
|
768
|
-
// across all backends. `adaptSkillForCli` is gone; the source body's
|
|
769
|
-
// YAML preamble flows through verbatim.
|
|
801
|
+
// Tool-deny policy stays in force — even with a narrow allowlist, the
|
|
802
|
+
// soft-enforcement prose lands in the body BEFORE the CLI frontmatter
|
|
803
|
+
// strip so it's not lost.
|
|
804
|
+
adapted = applyAllDeniedToolsForSkill(adapted, slug, backendId, this.integrations);
|
|
770
805
|
writeFileSync(join(destDir, "SKILL.md"), adapted, "utf-8");
|
|
771
|
-
this.spliceCurationAnchorsInSkill(destDir,
|
|
806
|
+
this.spliceCurationAnchorsInSkill(destDir, slug);
|
|
772
807
|
}
|
|
773
808
|
materializeCliSession(sessionDir, profileName, skillSlugs, backendId, processKey, profileBodyOverride, wikiWorkspaceName) {
|
|
774
|
-
//
|
|
775
|
-
//
|
|
776
|
-
// Claude SDK's
|
|
777
|
-
//
|
|
809
|
+
// fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
|
|
810
|
+
// F4 — divert a slim process key to the slim materializer that mirrors the
|
|
811
|
+
// Claude SDK's slim-systemPrompt swap. Custom bang commands cannot reach
|
|
812
|
+
// this branch (they bind to `messaging.custom_command`), so
|
|
778
813
|
// `profileBodyOverride` is intentionally NOT forwarded.
|
|
779
|
-
|
|
780
|
-
|
|
814
|
+
const slimKey = slimCliKeyFor(processKey);
|
|
815
|
+
if (slimKey) {
|
|
816
|
+
this.materializeSlimCliSession(sessionDir, backendId, slimKey);
|
|
781
817
|
return;
|
|
782
818
|
}
|
|
783
819
|
// docs/design/appendices/skills-unification.md Phase 1 — directory-based skill delivery.
|
|
@@ -106,6 +106,43 @@ export declare function resolveSkillManifestForProcess(processKey: ProcessKey, o
|
|
|
106
106
|
db?: Database.Database | null;
|
|
107
107
|
messageText?: string | null;
|
|
108
108
|
}): string[];
|
|
109
|
+
/**
|
|
110
|
+
* Event-type / process-key families whose sessions do NOT receive the
|
|
111
|
+
* owner's user-authored skill library (`<contextDir>/policies/skills/`).
|
|
112
|
+
*
|
|
113
|
+
* User skills are general assistant extensions the owner writes for their
|
|
114
|
+
* conversational agent; `syncAllUserSkills` provisions them into a session
|
|
115
|
+
* workdir ON TOP of the process key's built-in manifest. Narrow-persona
|
|
116
|
+
* sessions run under dedicated profiles with tight, purpose-built manifests
|
|
117
|
+
* — dumping the full user-skill library into them is pure cold-start cost +
|
|
118
|
+
* attention dilution and never load-bearing (their work is fully covered by
|
|
119
|
+
* the built-in bundle, and the wiki/research skills are themselves
|
|
120
|
+
* built-ins, not user-authored).
|
|
121
|
+
*
|
|
122
|
+
* Excluded families:
|
|
123
|
+
* - `wiki.*` — the wiki-agent persona (ingest_url / compile /
|
|
124
|
+
* ask / lint / trace / connect).
|
|
125
|
+
* - `routine.research_*` — the browser-history research routines
|
|
126
|
+
* (cluster_update / offer_dm / dispatch /
|
|
127
|
+
* wiki_summary).
|
|
128
|
+
*
|
|
129
|
+
* Everything else (the conversational DM agent, DM-tone scheduled sessions,
|
|
130
|
+
* the daily/weekly/monthly routines, setup, knowledge import, git/github
|
|
131
|
+
* observers, scheduled tasks) keeps the prior "every user skill, every
|
|
132
|
+
* session" behaviour.
|
|
133
|
+
*/
|
|
134
|
+
export declare const USER_SKILL_EXCLUDED_PREFIXES: readonly string[];
|
|
135
|
+
/**
|
|
136
|
+
* Whether a session for `eventTypeOrProcessKey` should be provisioned with
|
|
137
|
+
* the owner's user-authored skills. Keyed on the SAME string the skill
|
|
138
|
+
* manifest resolves against (`processKey ?? eventType`) so the gate and the
|
|
139
|
+
* built-in manifest agree on what a session is.
|
|
140
|
+
*
|
|
141
|
+
* Gates the `syncAllUserSkills` call at every materialisation chokepoint
|
|
142
|
+
* (`createSessionWorkdir`, `ensureSessionWorkdir`, and the backend-fallback
|
|
143
|
+
* re-materialiser). See `USER_SKILL_EXCLUDED_PREFIXES`.
|
|
144
|
+
*/
|
|
145
|
+
export declare function eventTypeAcceptsUserSkills(eventTypeOrProcessKey: string): boolean;
|
|
109
146
|
export declare function getProfileForEvent(eventType: string): string;
|
|
110
147
|
export declare function getSkillsForEvent(eventType: string): string[];
|
|
111
148
|
export declare function getProfileForProcess(processKey: ProcessKey): string;
|
|
@@ -127,7 +127,7 @@ export const EVENT_SKILL_SETS = {
|
|
|
127
127
|
"roadmap",
|
|
128
128
|
"management-policy",
|
|
129
129
|
],
|
|
130
|
-
"routine.
|
|
130
|
+
"routine.activity_scan": [
|
|
131
131
|
"context",
|
|
132
132
|
"today",
|
|
133
133
|
"observations",
|
|
@@ -260,6 +260,14 @@ export const EVENT_SKILL_SETS = {
|
|
|
260
260
|
// (no prior research offer to respond to) — that exclusion does
|
|
261
261
|
// not generalise.
|
|
262
262
|
"browser-task",
|
|
263
|
+
// BACKGROUND_TASK_RUNNER_DESIGN.md §5 / Phase 3 — DM-driven entry
|
|
264
|
+
// point to hand a long-running / open-ended task to the detached
|
|
265
|
+
// background runner. Loaded for the first DM too: the user's literal
|
|
266
|
+
// first message ("research X for me", "audit my repos") is exactly
|
|
267
|
+
// the spawn surface. The `background-task-reply` clarify skill is
|
|
268
|
+
// correctly absent here — no prior task can be parked on the very
|
|
269
|
+
// first DM of a session — exactly as `browser-history-respond` is.
|
|
270
|
+
"background-task",
|
|
263
271
|
],
|
|
264
272
|
"message.received.dm": [
|
|
265
273
|
"context",
|
|
@@ -305,6 +313,20 @@ export const EVENT_SKILL_SETS = {
|
|
|
305
313
|
// the agent simply does not call its endpoints when the user's
|
|
306
314
|
// request doesn't ask for a browser task.
|
|
307
315
|
"browser-task",
|
|
316
|
+
// BACKGROUND_TASK_RUNNER_DESIGN.md §5 / Phase 3 — the generic
|
|
317
|
+
// long-running-task surface. `background-task` teaches the DM agent
|
|
318
|
+
// to compose a self-contained brief, set the notification policy,
|
|
319
|
+
// POST + ack + end the turn, and read `GET /:id` for follow-up
|
|
320
|
+
// detail. `background-task-reply` is the narrow clarify-relay (mirror
|
|
321
|
+
// of `browser-history-respond`) that translates the owner's answer to
|
|
322
|
+
// a parked task's question into `POST /:id/clarify`. Loaded for
|
|
323
|
+
// `message.received.dm` (which `message.dm` + `dashboard.chat`
|
|
324
|
+
// inherit via PROCESS_TO_EVENT_TYPE). The reply skill is NOT on
|
|
325
|
+
// `message.received.dm_first` (no task can be parked on a session's
|
|
326
|
+
// first DM). No conditional gate: both skill bodies no-op when their
|
|
327
|
+
// endpoints have nothing to act on, and the in-context cost is small.
|
|
328
|
+
"background-task",
|
|
329
|
+
"background-task-reply",
|
|
308
330
|
],
|
|
309
331
|
// ── Task events ──
|
|
310
332
|
"schedule.approaching": [
|
|
@@ -564,6 +586,13 @@ export const ALL_SKILLS = [
|
|
|
564
586
|
// `browser-history-managed` skill that fronted the frozen workflow
|
|
565
587
|
// registry was retired alongside the route + routines in Phase 6.
|
|
566
588
|
"browser-task",
|
|
589
|
+
// BACKGROUND_TASK_RUNNER_DESIGN.md §5 / Phase 3 — generic detached
|
|
590
|
+
// long-task surface. `background-task` (spawn) loads on
|
|
591
|
+
// `message.received.dm` + `message.received.dm_first`;
|
|
592
|
+
// `background-task-reply` (clarify relay) loads on `message.received.dm`
|
|
593
|
+
// only. Both are DM-agent-exclusive, like browser-task.
|
|
594
|
+
"background-task",
|
|
595
|
+
"background-task-reply",
|
|
567
596
|
// Scheduling split — `/schedule` is one-shot only; recurring tasks are
|
|
568
597
|
// Agents. This skill teaches the DM agent to author a detailed recurring
|
|
569
598
|
// Agent (`POST /api/agents`) when the user asks for an ongoing cadence.
|
|
@@ -579,7 +608,7 @@ const PROCESS_TO_EVENT_TYPE = {
|
|
|
579
608
|
"routine.evening_review": "routine.evening_review",
|
|
580
609
|
"routine.weekly_review": "routine.weekly_review",
|
|
581
610
|
"routine.monthly_review": "routine.monthly_review",
|
|
582
|
-
"routine.
|
|
611
|
+
"routine.activity_scan": "routine.activity_scan",
|
|
583
612
|
"routine.roadmap_refresh": "routine.roadmap_refresh",
|
|
584
613
|
"routine.today_refresh": "routine.today_refresh",
|
|
585
614
|
"routine.user_profile_sweep": "routine.user_profile_sweep",
|
|
@@ -887,6 +916,48 @@ export function resolveSkillManifest(eventType, opts) {
|
|
|
887
916
|
export function resolveSkillManifestForProcess(processKey, opts) {
|
|
888
917
|
return resolveSkillManifest(PROCESS_TO_EVENT_TYPE[processKey] ?? processKey, opts);
|
|
889
918
|
}
|
|
919
|
+
/**
|
|
920
|
+
* Event-type / process-key families whose sessions do NOT receive the
|
|
921
|
+
* owner's user-authored skill library (`<contextDir>/policies/skills/`).
|
|
922
|
+
*
|
|
923
|
+
* User skills are general assistant extensions the owner writes for their
|
|
924
|
+
* conversational agent; `syncAllUserSkills` provisions them into a session
|
|
925
|
+
* workdir ON TOP of the process key's built-in manifest. Narrow-persona
|
|
926
|
+
* sessions run under dedicated profiles with tight, purpose-built manifests
|
|
927
|
+
* — dumping the full user-skill library into them is pure cold-start cost +
|
|
928
|
+
* attention dilution and never load-bearing (their work is fully covered by
|
|
929
|
+
* the built-in bundle, and the wiki/research skills are themselves
|
|
930
|
+
* built-ins, not user-authored).
|
|
931
|
+
*
|
|
932
|
+
* Excluded families:
|
|
933
|
+
* - `wiki.*` — the wiki-agent persona (ingest_url / compile /
|
|
934
|
+
* ask / lint / trace / connect).
|
|
935
|
+
* - `routine.research_*` — the browser-history research routines
|
|
936
|
+
* (cluster_update / offer_dm / dispatch /
|
|
937
|
+
* wiki_summary).
|
|
938
|
+
*
|
|
939
|
+
* Everything else (the conversational DM agent, DM-tone scheduled sessions,
|
|
940
|
+
* the daily/weekly/monthly routines, setup, knowledge import, git/github
|
|
941
|
+
* observers, scheduled tasks) keeps the prior "every user skill, every
|
|
942
|
+
* session" behaviour.
|
|
943
|
+
*/
|
|
944
|
+
export const USER_SKILL_EXCLUDED_PREFIXES = [
|
|
945
|
+
"wiki.",
|
|
946
|
+
"routine.research_",
|
|
947
|
+
];
|
|
948
|
+
/**
|
|
949
|
+
* Whether a session for `eventTypeOrProcessKey` should be provisioned with
|
|
950
|
+
* the owner's user-authored skills. Keyed on the SAME string the skill
|
|
951
|
+
* manifest resolves against (`processKey ?? eventType`) so the gate and the
|
|
952
|
+
* built-in manifest agree on what a session is.
|
|
953
|
+
*
|
|
954
|
+
* Gates the `syncAllUserSkills` call at every materialisation chokepoint
|
|
955
|
+
* (`createSessionWorkdir`, `ensureSessionWorkdir`, and the backend-fallback
|
|
956
|
+
* re-materialiser). See `USER_SKILL_EXCLUDED_PREFIXES`.
|
|
957
|
+
*/
|
|
958
|
+
export function eventTypeAcceptsUserSkills(eventTypeOrProcessKey) {
|
|
959
|
+
return !USER_SKILL_EXCLUDED_PREFIXES.some((prefix) => eventTypeOrProcessKey.startsWith(prefix));
|
|
960
|
+
}
|
|
890
961
|
export function getProfileForEvent(eventType) {
|
|
891
962
|
for (const rule of PROFILE_RULES) {
|
|
892
963
|
if (rule.exact ? eventType === rule.prefix : eventType.startsWith(rule.prefix)) {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep-awake posture while the daemon runs (`preventSleepMode` setting):
|
|
3
|
+
*
|
|
4
|
+
* - `"off"` — never inhibit sleep.
|
|
5
|
+
* - `"ac"` — inhibit system sleep only while on AC power
|
|
6
|
+
* (macOS `caffeinate -s`; battery drain stays impossible).
|
|
7
|
+
* - `"always"` — additionally inhibit idle sleep on battery
|
|
8
|
+
* (macOS `caffeinate -i -s`).
|
|
9
|
+
*
|
|
10
|
+
* Why this exists: every timer in the daemon (node-cron, WakeDetector,
|
|
11
|
+
* in-flight agent sessions) freezes while the host sleeps. A sleeping
|
|
12
|
+
* laptop turns the 04:00 day-boundary flow into hours of wake-catchup
|
|
13
|
+
* replays riding macOS maintenance DarkWakes — each replay re-fires
|
|
14
|
+
* day-boundary work in a 1–2 minute window before the machine re-sleeps,
|
|
15
|
+
* and cold prompt caches make those runs 10× the normal cost. Keeping
|
|
16
|
+
* the machine awake while plugged in removes that failure mode at the
|
|
17
|
+
* source; server installs (which never sleep) are unaffected.
|
|
18
|
+
*
|
|
19
|
+
* macOS only. `caffeinate` cannot override lid-close (clamshell) sleep —
|
|
20
|
+
* that needs root (`pmset disablesleep 1`), which the daemon must not
|
|
21
|
+
* touch. Windows/Linux hosts running this daemon are assumed to be
|
|
22
|
+
* servers or desktops with OS-level power management; the inhibitor is a
|
|
23
|
+
* no-op there and logs once at debug level.
|
|
24
|
+
*/
|
|
25
|
+
export declare const PREVENT_SLEEP_MODES: readonly ["off", "ac", "always"];
|
|
26
|
+
export type PreventSleepMode = (typeof PREVENT_SLEEP_MODES)[number];
|
|
27
|
+
/** Respawns allowed after an unexpected `caffeinate` exit before giving up. */
|
|
28
|
+
export declare const SLEEP_INHIBITOR_MAX_RESPAWNS = 3;
|
|
29
|
+
/** Pause before a respawn so a persistently-dying binary cannot tight-loop. */
|
|
30
|
+
export declare const SLEEP_INHIBITOR_RESPAWN_DELAY_MS = 5000;
|
|
31
|
+
export interface SleepInhibitCommand {
|
|
32
|
+
command: string;
|
|
33
|
+
args: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Pure command resolver — `null` means "do not inhibit" (mode off or
|
|
37
|
+
* unsupported platform). `-w <pid>` ties the power assertion to the
|
|
38
|
+
* daemon process itself, so the assertion is released even if the daemon
|
|
39
|
+
* is SIGKILLed and `stop()` never runs.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveSleepInhibitCommand(platform: NodeJS.Platform, mode: PreventSleepMode, pid: number): SleepInhibitCommand | null;
|
|
42
|
+
export interface SleepInhibitorChild {
|
|
43
|
+
on(event: "error", listener: (err: Error) => void): unknown;
|
|
44
|
+
on(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): unknown;
|
|
45
|
+
kill(signal?: NodeJS.Signals): boolean;
|
|
46
|
+
unref(): void;
|
|
47
|
+
pid?: number;
|
|
48
|
+
}
|
|
49
|
+
export type SleepInhibitorSpawn = (command: string, args: readonly string[]) => SleepInhibitorChild;
|
|
50
|
+
export interface SleepInhibitorOptions {
|
|
51
|
+
mode: PreventSleepMode;
|
|
52
|
+
/** Injectable for tests; defaults to the host platform. */
|
|
53
|
+
platform?: NodeJS.Platform;
|
|
54
|
+
/** Injectable for tests; defaults to the daemon's own pid. */
|
|
55
|
+
pid?: number;
|
|
56
|
+
spawnFn?: SleepInhibitorSpawn;
|
|
57
|
+
respawnDelayMs?: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Holds a `caffeinate` child for the daemon's lifetime. Crash-safe by
|
|
61
|
+
* construction (`-w pid` above); `stop()` exists for symmetric shutdown
|
|
62
|
+
* and to suppress the respawn path during graceful exit.
|
|
63
|
+
*/
|
|
64
|
+
export declare class SleepInhibitor {
|
|
65
|
+
private readonly mode;
|
|
66
|
+
private readonly platform;
|
|
67
|
+
private readonly pid;
|
|
68
|
+
private readonly spawnFn;
|
|
69
|
+
private readonly respawnDelayMs;
|
|
70
|
+
private child;
|
|
71
|
+
private respawnTimer;
|
|
72
|
+
private respawns;
|
|
73
|
+
private started;
|
|
74
|
+
private stopped;
|
|
75
|
+
constructor(options: SleepInhibitorOptions);
|
|
76
|
+
start(): void;
|
|
77
|
+
stop(): void;
|
|
78
|
+
private spawnChild;
|
|
79
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createLogger } from "../logging.js";
|
|
3
|
+
const logger = createLogger("sleep-inhibitor");
|
|
4
|
+
/**
|
|
5
|
+
* Keep-awake posture while the daemon runs (`preventSleepMode` setting):
|
|
6
|
+
*
|
|
7
|
+
* - `"off"` — never inhibit sleep.
|
|
8
|
+
* - `"ac"` — inhibit system sleep only while on AC power
|
|
9
|
+
* (macOS `caffeinate -s`; battery drain stays impossible).
|
|
10
|
+
* - `"always"` — additionally inhibit idle sleep on battery
|
|
11
|
+
* (macOS `caffeinate -i -s`).
|
|
12
|
+
*
|
|
13
|
+
* Why this exists: every timer in the daemon (node-cron, WakeDetector,
|
|
14
|
+
* in-flight agent sessions) freezes while the host sleeps. A sleeping
|
|
15
|
+
* laptop turns the 04:00 day-boundary flow into hours of wake-catchup
|
|
16
|
+
* replays riding macOS maintenance DarkWakes — each replay re-fires
|
|
17
|
+
* day-boundary work in a 1–2 minute window before the machine re-sleeps,
|
|
18
|
+
* and cold prompt caches make those runs 10× the normal cost. Keeping
|
|
19
|
+
* the machine awake while plugged in removes that failure mode at the
|
|
20
|
+
* source; server installs (which never sleep) are unaffected.
|
|
21
|
+
*
|
|
22
|
+
* macOS only. `caffeinate` cannot override lid-close (clamshell) sleep —
|
|
23
|
+
* that needs root (`pmset disablesleep 1`), which the daemon must not
|
|
24
|
+
* touch. Windows/Linux hosts running this daemon are assumed to be
|
|
25
|
+
* servers or desktops with OS-level power management; the inhibitor is a
|
|
26
|
+
* no-op there and logs once at debug level.
|
|
27
|
+
*/
|
|
28
|
+
export const PREVENT_SLEEP_MODES = ["off", "ac", "always"];
|
|
29
|
+
/** Respawns allowed after an unexpected `caffeinate` exit before giving up. */
|
|
30
|
+
export const SLEEP_INHIBITOR_MAX_RESPAWNS = 3;
|
|
31
|
+
/** Pause before a respawn so a persistently-dying binary cannot tight-loop. */
|
|
32
|
+
export const SLEEP_INHIBITOR_RESPAWN_DELAY_MS = 5_000;
|
|
33
|
+
/**
|
|
34
|
+
* Pure command resolver — `null` means "do not inhibit" (mode off or
|
|
35
|
+
* unsupported platform). `-w <pid>` ties the power assertion to the
|
|
36
|
+
* daemon process itself, so the assertion is released even if the daemon
|
|
37
|
+
* is SIGKILLed and `stop()` never runs.
|
|
38
|
+
*/
|
|
39
|
+
export function resolveSleepInhibitCommand(platform, mode, pid) {
|
|
40
|
+
if (mode === "off")
|
|
41
|
+
return null;
|
|
42
|
+
if (platform !== "darwin")
|
|
43
|
+
return null;
|
|
44
|
+
const flags = mode === "always" ? ["-i", "-s"] : ["-s"];
|
|
45
|
+
return { command: "caffeinate", args: [...flags, "-w", String(pid)] };
|
|
46
|
+
}
|
|
47
|
+
const defaultSpawn = (command, args) => spawn(command, args, { stdio: "ignore" });
|
|
48
|
+
/**
|
|
49
|
+
* Holds a `caffeinate` child for the daemon's lifetime. Crash-safe by
|
|
50
|
+
* construction (`-w pid` above); `stop()` exists for symmetric shutdown
|
|
51
|
+
* and to suppress the respawn path during graceful exit.
|
|
52
|
+
*/
|
|
53
|
+
export class SleepInhibitor {
|
|
54
|
+
mode;
|
|
55
|
+
platform;
|
|
56
|
+
pid;
|
|
57
|
+
spawnFn;
|
|
58
|
+
respawnDelayMs;
|
|
59
|
+
child = null;
|
|
60
|
+
respawnTimer = null;
|
|
61
|
+
respawns = 0;
|
|
62
|
+
started = false;
|
|
63
|
+
stopped = false;
|
|
64
|
+
constructor(options) {
|
|
65
|
+
this.mode = options.mode;
|
|
66
|
+
this.platform = options.platform ?? process.platform;
|
|
67
|
+
this.pid = options.pid ?? process.pid;
|
|
68
|
+
this.spawnFn = options.spawnFn ?? defaultSpawn;
|
|
69
|
+
this.respawnDelayMs =
|
|
70
|
+
options.respawnDelayMs ?? SLEEP_INHIBITOR_RESPAWN_DELAY_MS;
|
|
71
|
+
}
|
|
72
|
+
start() {
|
|
73
|
+
if (this.started)
|
|
74
|
+
return;
|
|
75
|
+
this.started = true;
|
|
76
|
+
const cmd = resolveSleepInhibitCommand(this.platform, this.mode, this.pid);
|
|
77
|
+
if (!cmd) {
|
|
78
|
+
logger.debug({ mode: this.mode, platform: this.platform }, "Sleep inhibitor inactive (mode off or unsupported platform)");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.spawnChild(cmd);
|
|
82
|
+
}
|
|
83
|
+
stop() {
|
|
84
|
+
this.stopped = true;
|
|
85
|
+
if (this.respawnTimer) {
|
|
86
|
+
clearTimeout(this.respawnTimer);
|
|
87
|
+
this.respawnTimer = null;
|
|
88
|
+
}
|
|
89
|
+
// Kill but leave `this.child` set — the exit handler clears it and the
|
|
90
|
+
// `stopped` flag suppresses the respawn path.
|
|
91
|
+
this.child?.kill("SIGTERM");
|
|
92
|
+
}
|
|
93
|
+
spawnChild(cmd) {
|
|
94
|
+
let child;
|
|
95
|
+
try {
|
|
96
|
+
child = this.spawnFn(cmd.command, cmd.args);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
logger.warn({ err, command: cmd.command }, "Sleep inhibitor spawn failed — system sleep stays OS-managed");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.child = child;
|
|
103
|
+
// The inhibitor must never keep the event loop alive on its own.
|
|
104
|
+
child.unref();
|
|
105
|
+
child.on("error", (err) => {
|
|
106
|
+
// ENOENT and friends — the binary is missing or unrunnable, which a
|
|
107
|
+
// respawn cannot fix. Warn once and fall back to OS-managed sleep.
|
|
108
|
+
this.child = null;
|
|
109
|
+
logger.warn({ err, command: cmd.command }, "Sleep inhibitor process errored — system sleep stays OS-managed");
|
|
110
|
+
});
|
|
111
|
+
child.on("exit", (code, signal) => {
|
|
112
|
+
if (this.child !== child)
|
|
113
|
+
return; // killed by stop() or replaced
|
|
114
|
+
this.child = null;
|
|
115
|
+
if (this.stopped)
|
|
116
|
+
return;
|
|
117
|
+
if (this.respawns >= SLEEP_INHIBITOR_MAX_RESPAWNS) {
|
|
118
|
+
logger.error({ code, signal, respawns: this.respawns }, "Sleep inhibitor exited repeatedly — giving up; system sleep stays OS-managed");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
this.respawns += 1;
|
|
122
|
+
logger.warn({ code, signal, attempt: this.respawns }, "Sleep inhibitor exited unexpectedly — respawning");
|
|
123
|
+
this.respawnTimer = setTimeout(() => {
|
|
124
|
+
this.respawnTimer = null;
|
|
125
|
+
if (!this.stopped)
|
|
126
|
+
this.spawnChild(cmd);
|
|
127
|
+
}, this.respawnDelayMs);
|
|
128
|
+
this.respawnTimer.unref?.();
|
|
129
|
+
});
|
|
130
|
+
logger.info({ command: cmd.command, args: cmd.args, mode: this.mode }, "Sleep inhibitor active");
|
|
131
|
+
}
|
|
132
|
+
}
|