@aitne/daemon 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/adapter-watchdog.d.ts +70 -0
- package/dist/adapters/adapter-watchdog.js +115 -0
- package/dist/adapters/discord.d.ts +17 -1
- package/dist/adapters/discord.js +33 -0
- package/dist/adapters/notification-manager.d.ts +27 -1
- package/dist/adapters/notification-manager.js +54 -39
- package/dist/adapters/slack-adapter.d.ts +26 -1
- package/dist/adapters/slack-adapter.js +41 -0
- package/dist/adapters/telegram-adapter.d.ts +18 -1
- package/dist/adapters/telegram-adapter.js +41 -2
- package/dist/adapters/types.d.ts +20 -0
- package/dist/adapters/whatsapp-adapter.d.ts +26 -7
- package/dist/adapters/whatsapp-adapter.js +74 -21
- package/dist/api/env-writer.js +8 -5
- package/dist/api/helpers/agent-errors-registry.d.ts +5 -5
- package/dist/api/helpers/agent-errors-registry.js +5 -5
- package/dist/api/routes/agent.js +33 -12
- package/dist/api/routes/agents/index.js +75 -16
- package/dist/api/routes/agents/views.d.ts +37 -2
- package/dist/api/routes/agents/views.js +64 -2
- package/dist/api/routes/background-task.d.ts +22 -0
- package/dist/api/routes/background-task.js +338 -0
- package/dist/api/routes/browser-history.js +9 -1
- package/dist/api/routes/context/permissions.js +3 -2
- package/dist/api/routes/context/snapshots.js +0 -3
- package/dist/api/routes/context/write.js +3 -17
- package/dist/api/routes/dashboard/config.js +48 -12
- package/dist/api/routes/dashboard/cost-approvals.js +66 -0
- package/dist/api/routes/dashboard/notifications.js +9 -9
- package/dist/api/routes/integrations/crud-patch.js +5 -1
- package/dist/api/routes/integrations-reconcile.js +2 -2
- package/dist/api/routes/notion.d.ts +1 -1
- package/dist/api/routes/observations.js +7 -7
- package/dist/api/routes/obsidian.d.ts +1 -1
- package/dist/api/routes/receipts.js +5 -1
- package/dist/api/routes/setup-migrate.js +1 -1
- package/dist/api/routes/setup.js +1 -1
- package/dist/api/routes/task-flows.d.ts +1 -1
- package/dist/api/routes/task-flows.js +1 -1
- package/dist/api/routes/tuning.d.ts +29 -0
- package/dist/api/routes/tuning.js +304 -0
- package/dist/api/server.d.ts +44 -16
- package/dist/api/server.js +9 -0
- package/dist/bootstrap/adapters.d.ts +19 -0
- package/dist/bootstrap/adapters.js +61 -0
- package/dist/bootstrap/api.d.ts +5 -3
- package/dist/bootstrap/api.js +45 -13
- package/dist/bootstrap/catchup.d.ts +1 -1
- package/dist/bootstrap/catchup.js +11 -11
- package/dist/bootstrap/event-pipeline.d.ts +11 -0
- package/dist/bootstrap/event-pipeline.js +245 -7
- package/dist/bootstrap/observers.js +9 -6
- package/dist/bootstrap/schedule-helpers.d.ts +104 -6
- package/dist/bootstrap/schedule-helpers.js +172 -19
- package/dist/config.js +26 -12
- package/dist/core/agent-core.d.ts +33 -1
- package/dist/core/agent-core.js +36 -1
- package/dist/core/agents/activity-scan-cadence.d.ts +103 -0
- package/dist/core/agents/activity-scan-cadence.js +127 -0
- package/dist/core/agents/agent-route-override.d.ts +53 -0
- package/dist/core/agents/agent-route-override.js +69 -0
- package/dist/core/agents/builtin-registry.d.ts +51 -14
- package/dist/core/agents/builtin-registry.js +92 -15
- package/dist/core/agents/config-gate-reconcile.d.ts +38 -0
- package/dist/core/agents/config-gate-reconcile.js +51 -0
- package/dist/core/agents/cron-substitute.d.ts +1 -1
- package/dist/core/agents/cron-substitute.js +1 -1
- package/dist/core/agents/custom-routine-migration.d.ts +60 -0
- package/dist/core/agents/custom-routine-migration.js +149 -0
- package/dist/core/agents/firing-blocked.d.ts +1 -1
- package/dist/core/agents/hourly-cadence.d.ts +102 -0
- package/dist/core/agents/hourly-cadence.js +126 -0
- package/dist/core/agents/loader-boot.js +23 -0
- package/dist/core/agents/loader.d.ts +19 -0
- package/dist/core/agents/loader.js +34 -2
- package/dist/core/agents/override-merge.d.ts +1 -1
- package/dist/core/agents/override-merge.js +9 -1
- package/dist/core/agents/recurrence-convert.d.ts +1 -1
- package/dist/core/agents/recurrence-convert.js +1 -1
- package/dist/core/agents/recurring-schedule-adapter.js +8 -0
- package/dist/core/alerts.js +6 -6
- package/dist/core/backends/auth-health-monitor.d.ts +2 -2
- package/dist/core/backends/auth-health-monitor.js +1 -1
- package/dist/core/backends/backend-router.d.ts +27 -1
- package/dist/core/backends/backend-router.js +165 -1
- package/dist/core/backends/claude-code-core.d.ts +71 -31
- package/dist/core/backends/claude-code-core.js +282 -54
- package/dist/core/backends/cli-quota-guards.d.ts +29 -1
- package/dist/core/backends/cli-quota-guards.js +40 -5
- package/dist/core/backends/codex-core.d.ts +6 -0
- package/dist/core/backends/codex-core.js +22 -6
- package/dist/core/backends/failure-spend.d.ts +58 -0
- package/dist/core/backends/failure-spend.js +137 -0
- package/dist/core/backends/gemini-cli-core.d.ts +6 -0
- package/dist/core/backends/gemini-cli-core.js +25 -6
- package/dist/core/backends/model-registry.d.ts +1 -1
- package/dist/core/backends/model-registry.js +4 -4
- package/dist/core/backends/opencode-core.d.ts +1 -1
- package/dist/core/backends/opencode-core.js +5 -5
- package/dist/core/backends/plan-presets.js +39 -15
- package/dist/core/bang-commands/commands-cost.js +3 -1
- package/dist/core/bang-commands/commands-report.js +4 -3
- package/dist/core/bang-commands/commands-research.js +4 -1
- package/dist/core/bang-commands/commands-revert-tuning.d.ts +18 -0
- package/dist/core/bang-commands/commands-revert-tuning.js +63 -0
- package/dist/core/bang-commands/commands-stop-start.js +3 -3
- package/dist/core/bang-commands/commands-task-control.d.ts +19 -0
- package/dist/core/bang-commands/commands-task-control.js +147 -0
- package/dist/core/bang-commands/commands-wiki.js +5 -5
- package/dist/core/bang-commands/index.d.ts +2 -0
- package/dist/core/bang-commands/index.js +12 -0
- package/dist/core/bang-commands/registry.d.ts +12 -0
- package/dist/core/browser-history/research-cluster-fanout.d.ts +28 -14
- package/dist/core/browser-history/research-cluster-fanout.js +39 -16
- package/dist/core/channel-timeline.d.ts +5 -1
- package/dist/core/channel-timeline.js +13 -0
- package/dist/core/context/index-reconciler.js +5 -2
- package/dist/core/context/policy-index-reconciler.d.ts +6 -4
- package/dist/core/context/policy-index-runner.js +25 -6
- package/dist/core/context-builder-calendar.js +10 -2
- package/dist/core/context-builder-conversation.d.ts +8 -1
- package/dist/core/context-builder-conversation.js +41 -7
- package/dist/core/context-builder-yesterday.js +4 -3
- package/dist/core/context-builder.d.ts +7 -2
- package/dist/core/context-builder.js +62 -20
- package/dist/core/context-file-serializer.d.ts +1 -1
- package/dist/core/context-file-serializer.js +1 -1
- package/dist/core/context-health.js +2 -2
- package/dist/core/context-paths.d.ts +1 -1
- package/dist/core/context-paths.js +1 -1
- package/dist/core/context-validation/prepare-write.js +1 -1
- package/dist/core/context-validation/routine-rulebook.d.ts +1 -1
- package/dist/core/context-vault-aliases.d.ts +0 -13
- package/dist/core/context-vault-aliases.js +37 -0
- package/dist/core/custom-routines.d.ts +99 -0
- package/dist/core/custom-routines.js +187 -0
- package/dist/core/daemon-api-cli.js +49 -0
- package/dist/core/day-boundary.d.ts +46 -0
- package/dist/core/day-boundary.js +40 -0
- package/dist/core/dispatcher-activity-scan.d.ts +221 -0
- package/dist/core/dispatcher-activity-scan.js +775 -0
- package/dist/core/dispatcher-error-handling.d.ts +6 -11
- package/dist/core/dispatcher-error-handling.js +38 -62
- package/dist/core/dispatcher-hourly-check.js +6 -1
- package/dist/core/dispatcher-message-handler.d.ts +10 -0
- package/dist/core/dispatcher-message-handler.js +17 -0
- package/dist/core/dispatcher-morning-routine.d.ts +6 -6
- package/dist/core/dispatcher-morning-routine.js +13 -13
- package/dist/core/dispatcher-result-processor.d.ts +33 -0
- package/dist/core/dispatcher-result-processor.js +167 -11
- package/dist/core/dispatcher-scheduled-background-task.d.ts +42 -0
- package/dist/core/dispatcher-scheduled-background-task.js +89 -0
- package/dist/core/dispatcher-scheduled-tasks.d.ts +63 -1
- package/dist/core/dispatcher-scheduled-tasks.js +213 -6
- package/dist/core/dispatcher-task-delivery.d.ts +105 -0
- package/dist/core/dispatcher-task-delivery.js +555 -0
- package/dist/core/dispatcher-types.d.ts +48 -9
- package/dist/core/dispatcher-types.js +3 -3
- package/dist/core/dispatcher.d.ts +112 -31
- package/dist/core/dispatcher.js +284 -59
- package/dist/core/dm-freshness-metrics.d.ts +1 -1
- package/dist/core/drift-effects.js +2 -2
- package/dist/core/feedback/consolidation-prep.js +17 -5
- package/dist/core/feedback/eviction-scorer.js +6 -2
- package/dist/core/feedback/lesson-format.js +9 -4
- package/dist/core/feedback/lesson-injection.d.ts +1 -1
- package/dist/core/feedback/lesson-injection.js +17 -2
- package/dist/core/feedback/lesson-store-overview.d.ts +8 -4
- package/dist/core/feedback/lesson-store-overview.js +8 -4
- package/dist/core/feedback/regeneralization-prep.js +29 -16
- package/dist/core/feedback/self-performance-prep.d.ts +186 -0
- package/dist/core/feedback/self-performance-prep.js +541 -0
- package/dist/core/feedback/tuning-actuator.d.ts +198 -0
- package/dist/core/feedback/tuning-actuator.js +432 -0
- package/dist/core/feedback/tuning-recommender.d.ts +247 -0
- package/dist/core/feedback/tuning-recommender.js +580 -0
- package/dist/core/feedback/tuning-revert-monitor.d.ts +90 -0
- package/dist/core/feedback/tuning-revert-monitor.js +213 -0
- package/dist/core/health-monitor.d.ts +6 -0
- package/dist/core/health-monitor.js +1 -1
- package/dist/core/injection-policy.d.ts +4 -4
- package/dist/core/injection-policy.js +4 -4
- package/dist/core/integration-main-backend.js +4 -0
- package/dist/core/management-md.d.ts +2 -2
- package/dist/core/management-md.js +51 -13
- package/dist/core/morning/orchestrator.d.ts +2 -2
- package/dist/core/morning/orchestrator.js +2 -2
- package/dist/core/notification-gate.d.ts +64 -0
- package/dist/core/notification-gate.js +51 -0
- package/dist/core/notification-rate-limit.d.ts +40 -0
- package/dist/core/notification-rate-limit.js +50 -0
- package/dist/core/policy-files.d.ts +1 -1
- package/dist/core/policy-files.js +2 -2
- package/dist/core/pre-pass-freshness.d.ts +4 -4
- package/dist/core/retention.d.ts +5 -0
- package/dist/core/retention.js +20 -4
- package/dist/core/review-context.d.ts +1 -1
- package/dist/core/review-context.js +10 -5
- package/dist/core/roadmap-write-lock.d.ts +2 -1
- package/dist/core/roadmap-write-lock.js +15 -10
- package/dist/core/routine-acquisition-plan.d.ts +47 -1
- package/dist/core/routine-acquisition-plan.js +78 -20
- package/dist/core/routine-fetch-window-retry.js +7 -4
- package/dist/core/routine-fetch-window-runner.d.ts +39 -3
- package/dist/core/routine-fetch-window-runner.js +264 -13
- package/dist/core/routine-windows.d.ts +2 -2
- package/dist/core/routine-windows.js +8 -5
- package/dist/core/scheduler.d.ts +175 -16
- package/dist/core/scheduler.js +559 -102
- package/dist/core/signal-detector.d.ts +12 -0
- package/dist/core/signal-detector.js +53 -9
- package/dist/core/skills-compiler-denied-tools.js +2 -2
- package/dist/core/skills-compiler-skill-index.d.ts +2 -2
- package/dist/core/skills-compiler-skill-index.js +2 -2
- package/dist/core/skills-compiler-variants.d.ts +1 -1
- package/dist/core/skills-compiler-variants.js +8 -0
- package/dist/core/skills-compiler.d.ts +29 -26
- package/dist/core/skills-compiler.js +117 -81
- package/dist/core/skills-manifest.d.ts +37 -0
- package/dist/core/skills-manifest.js +73 -2
- package/dist/core/sleep-inhibitor.d.ts +79 -0
- package/dist/core/sleep-inhibitor.js +132 -0
- package/dist/core/slim-system-prompt-loader.d.ts +77 -0
- package/dist/core/slim-system-prompt-loader.js +141 -0
- package/dist/core/spawn-gates.d.ts +126 -0
- package/dist/core/spawn-gates.js +180 -0
- package/dist/core/today-direct-writer.d.ts +2 -2
- package/dist/core/today-direct-writer.js +1 -1
- package/dist/core/today-write-lock.d.ts +4 -2
- package/dist/core/today-write-lock.js +30 -20
- package/dist/core/wake-detector.d.ts +55 -0
- package/dist/core/wake-detector.js +80 -0
- package/dist/core/wiki/compile-lock.d.ts +1 -1
- package/dist/core/wiki/compile-lock.js +1 -1
- package/dist/core/workdir.js +15 -6
- package/dist/db/activity-scan-signals.d.ts +77 -0
- package/dist/db/activity-scan-signals.js +378 -0
- package/dist/db/agents-store.d.ts +28 -0
- package/dist/db/agents-store.js +62 -0
- package/dist/db/background-task-clarifications-store.d.ts +81 -0
- package/dist/db/background-task-clarifications-store.js +152 -0
- package/dist/db/background-task-store.d.ts +207 -0
- package/dist/db/background-task-store.js +380 -0
- package/dist/db/browser-history-store.d.ts +39 -6
- package/dist/db/browser-history-store.js +51 -7
- package/dist/db/browser-task-clarifications-store.d.ts +12 -0
- package/dist/db/browser-task-clarifications-store.js +35 -5
- package/dist/db/browser-task-store.d.ts +3 -0
- package/dist/db/browser-task-store.js +29 -4
- package/dist/db/deferred-dm.d.ts +86 -0
- package/dist/db/deferred-dm.js +199 -0
- package/dist/db/migrations.js +330 -0
- package/dist/db/observations.d.ts +2 -2
- package/dist/db/observations.js +3 -3
- package/dist/db/schema.js +217 -16
- package/dist/db/voice-transcripts-store.d.ts +1 -1
- package/dist/index.js +86 -29
- package/dist/messaging/browser-task-mcp-notifier.d.ts +12 -70
- package/dist/messaging/browser-task-mcp-notifier.js +30 -151
- package/dist/messaging/browser-task-screenshot-attachment.d.ts +15 -0
- package/dist/messaging/browser-task-screenshot-attachment.js +63 -0
- package/dist/observers/delegated-sync-worker.d.ts +6 -6
- package/dist/observers/delegated-sync-worker.js +10 -10
- package/dist/observers/git-delegated-cron.d.ts +1 -1
- package/dist/observers/git-delegated-cron.js +2 -2
- package/dist/observers/github-poller-classifier.d.ts +3 -3
- package/dist/observers/github-poller-classifier.js +3 -3
- package/dist/observers/imminent-event-scheduler.d.ts +1 -1
- package/dist/observers/imminent-event-scheduler.js +1 -1
- package/dist/observers/mail-poller.d.ts +1 -0
- package/dist/observers/mail-poller.js +42 -3
- package/dist/observers/observation-summarizer/summarizer-client.d.ts +2 -2
- package/dist/observers/observation-summarizer/summarizer-client.js +2 -2
- package/dist/observers/observation-summarizer/worker.d.ts +2 -2
- package/dist/observers/observation-summarizer/worker.js +4 -4
- package/dist/observers/obsidian-watcher.d.ts +1 -1
- package/dist/observers/obsidian-watcher.js +1 -1
- package/dist/safety/agent-write-tracker.d.ts +4 -4
- package/dist/safety/agent-write-tracker.js +4 -4
- package/dist/safety/audit.d.ts +43 -5
- package/dist/safety/audit.js +86 -18
- package/dist/safety/risk-classifier.d.ts +6 -0
- package/dist/safety/risk-classifier.js +75 -11
- package/dist/scheduler/activity-scan-gate.d.ts +86 -0
- package/dist/scheduler/activity-scan-gate.js +132 -0
- package/dist/services/background-task/background-task-budget.d.ts +80 -0
- package/dist/services/background-task/background-task-budget.js +91 -0
- package/dist/services/background-task/background-task-driver.d.ts +105 -0
- package/dist/services/background-task/background-task-driver.js +416 -0
- package/dist/services/background-task/background-task-runner.d.ts +96 -0
- package/dist/services/background-task/background-task-runner.js +673 -0
- package/dist/services/background-task/background-task-tools.d.ts +84 -0
- package/dist/services/background-task/background-task-tools.js +247 -0
- package/dist/services/background-task/background-task-transition-events.d.ts +43 -0
- package/dist/services/background-task/background-task-transition-events.js +54 -0
- package/dist/services/browser-history/automation/egress-denylist.d.ts +1 -1
- package/dist/services/browser-history/automation/egress-denylist.js +16 -6
- package/dist/services/browser-history/managed-chromium/sandbox-launcher.js +0 -1
- package/dist/services/browser-task/browser-task-runner.js +53 -8
- package/dist/services/observations-batch.d.ts +1 -1
- package/dist/services/observations-batch.js +2 -2
- package/dist/settings/runtime-settings.d.ts +38 -11
- package/dist/settings/runtime-settings.js +203 -40
- package/dist/settings/settings-store.js +11 -3
- package/package.json +4 -4
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background-task driver — generic Claude Agent SDK glue for the
|
|
3
|
+
* per-task worker loop. BACKGROUND_TASK_RUNNER_DESIGN.md §4.1.
|
|
4
|
+
*
|
|
5
|
+
* The browser-task driver's analogue, with the entire Playwright /
|
|
6
|
+
* managed-Chromium / allowlist / final-confirm plane removed. A worker
|
|
7
|
+
* is a plain `query()` session seeded with a SELF-CONTAINED brief and a
|
|
8
|
+
* three-tool MCP envelope (`read_memory`, `ask_user`, `finish`) plus the
|
|
9
|
+
* SDK's `WebSearch` / `WebFetch` for research-type work. It opts out of
|
|
10
|
+
* the `<user>` / `<management_rules>` injection (`settingSources:
|
|
11
|
+
* ["project"]`, as browser-task does) — the brief carries the context,
|
|
12
|
+
* the output-language directive, persona hints for the `draft`, and the
|
|
13
|
+
* notification policy / criteria.
|
|
14
|
+
*
|
|
15
|
+
* Responsibilities:
|
|
16
|
+
* 1. Resolve the `(model, maxTurns, maxBudgetUsd, executeTimeout)`
|
|
17
|
+
* envelope from `process_backend_config` + the row's tier/budget
|
|
18
|
+
* (`background-task-budget.ts`), pinned for the task's lifetime.
|
|
19
|
+
* 2. Render a per-task workdir (empty dir + CLAUDE.md agent profile).
|
|
20
|
+
* 3. Drive `query()` until terminal, honouring the AbortController
|
|
21
|
+
* (cancel + timeout) — no Playwright resources to release.
|
|
22
|
+
* 4. Hand back a `DriverRunResult` the runner maps to terminal state /
|
|
23
|
+
* park. On the death paths the artifact is NULL — the runner
|
|
24
|
+
* synthesizes the fail-loud artifact (§4.3).
|
|
25
|
+
*
|
|
26
|
+
* Excluded from the 100% coverage gate — SDK stream consumer. The pure
|
|
27
|
+
* sub-pieces (budget envelope) live in the covered set.
|
|
28
|
+
*/
|
|
29
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
32
|
+
import { getTaskFlow } from "../../core/prompts.js";
|
|
33
|
+
import { ALWAYS_DISALLOWED_TOOLS } from "../../safety/always-disallowed.js";
|
|
34
|
+
import { getBackgroundTask, setBackendSessionId, } from "../../db/background-task-store.js";
|
|
35
|
+
import { createLogger } from "../../logging.js";
|
|
36
|
+
import { resolveBackgroundTaskEnvelope, } from "./background-task-budget.js";
|
|
37
|
+
import { BACKGROUND_TASK_MCP_SERVER_NAME, BACKGROUND_TASK_TOOL_FQNS, createBackgroundTaskMcpServer, createBackgroundTaskRuntime, } from "./background-task-tools.js";
|
|
38
|
+
import { noopBackgroundTaskTransitionEmitter, } from "./background-task-transition-events.js";
|
|
39
|
+
const logger = createLogger("background-task-driver");
|
|
40
|
+
/** SDK tools the worker may use beyond its MCP envelope. Research-type
|
|
41
|
+
* background tasks (the flagship category) need web access; everything
|
|
42
|
+
* filesystem/shell is denied (the worker must not read arbitrary host
|
|
43
|
+
* files or write shared memory — §10.4). */
|
|
44
|
+
const BACKGROUND_TASK_WEB_TOOLS = ["WebSearch", "WebFetch"];
|
|
45
|
+
/** Defence-in-depth deny list. These are NOT in `allowedTools`, so
|
|
46
|
+
* `dontAsk` already denies them — listing them ensures a future
|
|
47
|
+
* regression that widens `allowedTools` cannot unlock filesystem/shell
|
|
48
|
+
* access. */
|
|
49
|
+
const BACKGROUND_TASK_EXTRA_DISALLOWED = [
|
|
50
|
+
"Bash",
|
|
51
|
+
"Read",
|
|
52
|
+
"Write",
|
|
53
|
+
"Edit",
|
|
54
|
+
"NotebookEdit",
|
|
55
|
+
"Glob",
|
|
56
|
+
"Grep",
|
|
57
|
+
];
|
|
58
|
+
/** Read the operator-editable envelope row for `background_task`. */
|
|
59
|
+
export function loadBackgroundTaskProcessConfig(db) {
|
|
60
|
+
try {
|
|
61
|
+
const row = db
|
|
62
|
+
.prepare(`SELECT main_backend, main_model, max_turns, max_budget_usd
|
|
63
|
+
FROM process_backend_config
|
|
64
|
+
WHERE process_key = 'background_task'`)
|
|
65
|
+
.get();
|
|
66
|
+
if (!row)
|
|
67
|
+
return null;
|
|
68
|
+
return {
|
|
69
|
+
mainBackend: row.main_backend,
|
|
70
|
+
mainModel: row.main_model,
|
|
71
|
+
maxTurns: row.max_turns,
|
|
72
|
+
maxBudgetUsd: row.max_budget_usd,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
logger.warn({ err }, "background-task: process_backend_config read failed; falling back to tier defaults");
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Acquire a fresh workdir + runtime + binding for `row`. The runner
|
|
82
|
+
* calls this BEFORE `runDriver` so it can stash the handle in its parked
|
|
83
|
+
* map before any turn fires (an ask_user on the first turn must find the
|
|
84
|
+
* handle already there).
|
|
85
|
+
*/
|
|
86
|
+
export async function prepareDriverHandle(input) {
|
|
87
|
+
const { deps, row } = input;
|
|
88
|
+
// Pin the envelope BEFORE any work so a misconfigured backend fails
|
|
89
|
+
// fast AND the binding is frozen for the task's lifetime (no surprise
|
|
90
|
+
// model swap between yield and clarify resume).
|
|
91
|
+
const resolved = resolveBackgroundTaskEnvelope({
|
|
92
|
+
tier: row.tier,
|
|
93
|
+
maxBudgetUsd: row.maxBudgetUsd,
|
|
94
|
+
processConfig: loadBackgroundTaskProcessConfig(deps.db),
|
|
95
|
+
});
|
|
96
|
+
if (!resolved.ok) {
|
|
97
|
+
return { ok: false, reason: "backend_misconfigured", detail: resolved.detail };
|
|
98
|
+
}
|
|
99
|
+
const cwd = join(deps.paDataDir, "background-task-sessions", row.id);
|
|
100
|
+
await mkdir(cwd, { recursive: true });
|
|
101
|
+
try {
|
|
102
|
+
const profileBody = await readAgentProfileBody(deps.workspaceDir);
|
|
103
|
+
await writeFile(join(cwd, "CLAUDE.md"), profileBody, "utf-8");
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
logger.warn({ err, taskId: row.id }, "background-task: agent profile read failed — proceeding with minimal CLAUDE.md");
|
|
107
|
+
await writeFile(join(cwd, "CLAUDE.md"), "# Background Task Worker\n", "utf-8");
|
|
108
|
+
}
|
|
109
|
+
const abortController = new AbortController();
|
|
110
|
+
const runtime = createBackgroundTaskRuntime({
|
|
111
|
+
taskId: row.id,
|
|
112
|
+
db: deps.db,
|
|
113
|
+
contextDir: deps.contextDir,
|
|
114
|
+
clarificationTtlMs: deps.clarificationTtlMs,
|
|
115
|
+
abortSignal: abortController.signal,
|
|
116
|
+
transitionEmitter: deps.transitionEmitter,
|
|
117
|
+
nowFn: deps.nowFn,
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
ok: true,
|
|
121
|
+
handle: {
|
|
122
|
+
abortController,
|
|
123
|
+
cwd,
|
|
124
|
+
runtime,
|
|
125
|
+
sdkSessionId: row.backendSessionId,
|
|
126
|
+
binding: resolved.envelope,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Drive the initial turn — the worker reads the brief and works. */
|
|
131
|
+
export async function runDriver(deps, row, handle) {
|
|
132
|
+
const startMs = (deps.nowFn ?? (() => Date.now()))();
|
|
133
|
+
return runQuery({
|
|
134
|
+
deps,
|
|
135
|
+
handle,
|
|
136
|
+
row,
|
|
137
|
+
prompt: renderTaskPrompt(row),
|
|
138
|
+
resume: null,
|
|
139
|
+
startMs,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/** Resume a parked task after `/clarify` lands the owner's answer. Uses
|
|
143
|
+
* the persisted SDK session id so the prompt cache stays warm. Works both
|
|
144
|
+
* in-process (warm parked handle) and across a daemon restart (the runner
|
|
145
|
+
* reconstructs the handle from the persisted `backend_session_id`); in the
|
|
146
|
+
* cross-restart case a session the SDK can no longer load surfaces as
|
|
147
|
+
* `resume_unavailable`. */
|
|
148
|
+
export async function resumeDriver(deps, row, handle, userAnswer) {
|
|
149
|
+
if (!handle.sdkSessionId) {
|
|
150
|
+
return {
|
|
151
|
+
outcome: "no_finish",
|
|
152
|
+
sdkSessionId: null,
|
|
153
|
+
detail: "no sdk session id captured — cannot resume",
|
|
154
|
+
costUsd: 0,
|
|
155
|
+
numTurns: 0,
|
|
156
|
+
durationMs: 0,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const startMs = (deps.nowFn ?? (() => Date.now()))();
|
|
160
|
+
return runQuery({
|
|
161
|
+
deps,
|
|
162
|
+
handle,
|
|
163
|
+
row,
|
|
164
|
+
prompt: `The owner answered your clarification:\n\n${userAnswer}\n\nContinue the task.`,
|
|
165
|
+
resume: handle.sdkSessionId,
|
|
166
|
+
isResume: true,
|
|
167
|
+
startMs,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* BACKGROUND_TASK_RUNNER_DESIGN.md §10.2 / Phase 4 — resume a task that was
|
|
172
|
+
* mid-execution when the daemon restarted, using the persisted SDK session
|
|
173
|
+
* id (`backend_session_id`) so the warm transcript + prompt cache survive
|
|
174
|
+
* the restart instead of re-running the brief from scratch. The runner
|
|
175
|
+
* reconstructs the handle (`prepareDriverHandle` recreates the per-task
|
|
176
|
+
* workdir + sets `sdkSessionId` from the row). When the SDK can no longer
|
|
177
|
+
* load the session, this returns `resume_unavailable` and the runner falls
|
|
178
|
+
* back to re-dispatch-from-brief — so resume is a pure optimization with no
|
|
179
|
+
* regression.
|
|
180
|
+
*/
|
|
181
|
+
export async function resumeFromBootDriver(deps, row, handle) {
|
|
182
|
+
if (!handle.sdkSessionId) {
|
|
183
|
+
return {
|
|
184
|
+
outcome: "resume_unavailable",
|
|
185
|
+
sdkSessionId: null,
|
|
186
|
+
detail: "no persisted sdk session id — cannot resume across restart",
|
|
187
|
+
costUsd: 0,
|
|
188
|
+
numTurns: 0,
|
|
189
|
+
durationMs: 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const startMs = (deps.nowFn ?? (() => Date.now()))();
|
|
193
|
+
return runQuery({
|
|
194
|
+
deps,
|
|
195
|
+
handle,
|
|
196
|
+
row,
|
|
197
|
+
prompt: "The daemon restarted while you were working on this task. Pick up "
|
|
198
|
+
+ "exactly where you left off and finish it. If you had already "
|
|
199
|
+
+ "gathered enough, call finish() now.",
|
|
200
|
+
resume: handle.sdkSessionId,
|
|
201
|
+
isResume: true,
|
|
202
|
+
startMs,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
async function runQuery(input) {
|
|
206
|
+
const { deps, handle, row, prompt, resume, isResume = false, startMs } = input;
|
|
207
|
+
const mcpServer = createBackgroundTaskMcpServer(handle.runtime);
|
|
208
|
+
const mcpServers = {
|
|
209
|
+
[BACKGROUND_TASK_MCP_SERVER_NAME]: mcpServer,
|
|
210
|
+
};
|
|
211
|
+
const binding = handle.binding;
|
|
212
|
+
let sessionId = handle.sdkSessionId;
|
|
213
|
+
let costUsd = 0;
|
|
214
|
+
let numTurns = 0;
|
|
215
|
+
let stopReason = null;
|
|
216
|
+
let isError = false;
|
|
217
|
+
let sawInit = false;
|
|
218
|
+
let outcome = "completed";
|
|
219
|
+
let detail = null;
|
|
220
|
+
const timeoutMs = binding.executeTimeoutMinutes * 60 * 1000;
|
|
221
|
+
const timeoutTimer = setTimeout(() => {
|
|
222
|
+
handle.abortController.abort(new Error("background_task_execute_timeout"));
|
|
223
|
+
}, timeoutMs);
|
|
224
|
+
try {
|
|
225
|
+
const stream = query({
|
|
226
|
+
prompt,
|
|
227
|
+
options: {
|
|
228
|
+
...(resume ? { resume } : {}),
|
|
229
|
+
cwd: handle.cwd,
|
|
230
|
+
model: binding.modelId,
|
|
231
|
+
maxTurns: binding.maxTurns,
|
|
232
|
+
maxBudgetUsd: binding.maxBudgetUsd,
|
|
233
|
+
abortController: handle.abortController,
|
|
234
|
+
permissionMode: "dontAsk",
|
|
235
|
+
allowedTools: [
|
|
236
|
+
...BACKGROUND_TASK_TOOL_FQNS,
|
|
237
|
+
...BACKGROUND_TASK_WEB_TOOLS,
|
|
238
|
+
],
|
|
239
|
+
disallowedTools: [
|
|
240
|
+
...ALWAYS_DISALLOWED_TOOLS,
|
|
241
|
+
...BACKGROUND_TASK_EXTRA_DISALLOWED,
|
|
242
|
+
],
|
|
243
|
+
mcpServers,
|
|
244
|
+
// CLAUDE.md in cwd is the persona; settingSources includes
|
|
245
|
+
// "project" so the SDK auto-loads it but NOT the daemon's
|
|
246
|
+
// <user>/<management_rules> (the brief is self-contained).
|
|
247
|
+
settingSources: ["project"],
|
|
248
|
+
persistSession: true,
|
|
249
|
+
includePartialMessages: false,
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
try {
|
|
253
|
+
for await (const message of stream) {
|
|
254
|
+
if (message.type === "system"
|
|
255
|
+
&& message.subtype === "init") {
|
|
256
|
+
sawInit = true;
|
|
257
|
+
const sid = message.session_id ?? null;
|
|
258
|
+
if (sid) {
|
|
259
|
+
sessionId = sid;
|
|
260
|
+
handle.sdkSessionId = sid;
|
|
261
|
+
// Persist so a /clarify resume (and the boot path) can find it.
|
|
262
|
+
try {
|
|
263
|
+
setBackendSessionId(deps.db, row.id, sid);
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
/* c8 ignore next 3 -- defensive */
|
|
267
|
+
logger.warn({ err, taskId: row.id }, "setBackendSessionId failed (continuing)");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else if (message.type === "result") {
|
|
272
|
+
const result = message;
|
|
273
|
+
stopReason = result.stop_reason ?? null;
|
|
274
|
+
isError = !!result.is_error;
|
|
275
|
+
costUsd += result.total_cost_usd ?? 0;
|
|
276
|
+
numTurns = result.num_turns ?? numTurns;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
try {
|
|
282
|
+
await stream.return?.(undefined);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
/* ignore */
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const dbStateAfterStream = getBackgroundTask(deps.db, row.id)?.state ?? "running";
|
|
289
|
+
if (handle.abortController.signal.aborted) {
|
|
290
|
+
const reason = handle.abortController.signal.reason;
|
|
291
|
+
if (reason instanceof Error
|
|
292
|
+
&& reason.message === "background_task_execute_timeout") {
|
|
293
|
+
outcome = "timeout";
|
|
294
|
+
detail = `executeTimeoutMinutes=${binding.executeTimeoutMinutes}`;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
outcome = "cancelled";
|
|
298
|
+
detail = reason instanceof Error ? reason.message : String(reason ?? "abort");
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else if (isResume && !sawInit) {
|
|
302
|
+
// A resume attempt whose session never loaded — no `init` message
|
|
303
|
+
// ever arrived (e.g. the SDK can no longer find the persisted
|
|
304
|
+
// session after a restart). The runner falls back to re-dispatch
|
|
305
|
+
// rather than fail-loud. Comes after the abort check so a cancel /
|
|
306
|
+
// timeout during resume is still classified as such.
|
|
307
|
+
outcome = "resume_unavailable";
|
|
308
|
+
detail = "resume target session did not load (no init message)";
|
|
309
|
+
}
|
|
310
|
+
else if (isError && stopReason && /max_turns/i.test(stopReason)) {
|
|
311
|
+
outcome = "max_turns_exceeded";
|
|
312
|
+
detail = stopReason;
|
|
313
|
+
}
|
|
314
|
+
else if (isError && stopReason && /budget|cost/i.test(stopReason)) {
|
|
315
|
+
outcome = "budget_exceeded";
|
|
316
|
+
detail = stopReason;
|
|
317
|
+
}
|
|
318
|
+
else if (handle.runtime.finishFlag.current || dbStateAfterStream === "completed") {
|
|
319
|
+
outcome = "completed";
|
|
320
|
+
}
|
|
321
|
+
else if (dbStateAfterStream === "awaiting_user"
|
|
322
|
+
&& handle.runtime.yieldFlag.current) {
|
|
323
|
+
outcome = "yielded_for_clarification";
|
|
324
|
+
}
|
|
325
|
+
else if (isError) {
|
|
326
|
+
outcome = "sdk_error";
|
|
327
|
+
detail = stopReason ?? "sdk_isError";
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
// Clean SDK end but the worker never called finish/ask_user. The
|
|
331
|
+
// runner treats this as a fail-loud terminal.
|
|
332
|
+
outcome = "no_finish";
|
|
333
|
+
detail = "SDK stream ended without finish() or ask_user()";
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch (err) {
|
|
337
|
+
if (handle.abortController.signal.aborted) {
|
|
338
|
+
outcome = "cancelled";
|
|
339
|
+
detail = err instanceof Error ? err.message : String(err);
|
|
340
|
+
}
|
|
341
|
+
else if (isResume && !sawInit) {
|
|
342
|
+
// The resume query threw before the session loaded — treat as a
|
|
343
|
+
// recoverable "couldn't resume" rather than a hard SDK error so the
|
|
344
|
+
// runner re-dispatches from brief.
|
|
345
|
+
outcome = "resume_unavailable";
|
|
346
|
+
detail = err instanceof Error ? err.message : String(err);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
outcome = "sdk_error";
|
|
350
|
+
detail = err instanceof Error ? err.message : String(err);
|
|
351
|
+
logger.error({ err, taskId: row.id }, "background-task SDK query threw");
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
finally {
|
|
355
|
+
clearTimeout(timeoutTimer);
|
|
356
|
+
}
|
|
357
|
+
const durationMs = (deps.nowFn ?? (() => Date.now()))() - startMs;
|
|
358
|
+
return { outcome, sdkSessionId: sessionId, detail, costUsd, numTurns, durationMs };
|
|
359
|
+
}
|
|
360
|
+
/** Remove the per-task workdir. Idempotent. Parked tasks (awaiting_user)
|
|
361
|
+
* do NOT call this — the runner keeps the handle in its parked map so
|
|
362
|
+
* /clarify can resume the warm SDK session. */
|
|
363
|
+
export async function releaseDriverHandle(deps, handle) {
|
|
364
|
+
try {
|
|
365
|
+
await rm(handle.cwd, { recursive: true, force: true });
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
/* c8 ignore start -- defensive */
|
|
369
|
+
logger.warn({ err, cwd: handle.cwd }, "background-task workdir cleanup failed");
|
|
370
|
+
/* c8 ignore stop */
|
|
371
|
+
}
|
|
372
|
+
void deps;
|
|
373
|
+
}
|
|
374
|
+
async function readAgentProfileBody(workspaceDir) {
|
|
375
|
+
const candidate = join(workspaceDir, "agent-assets", "agent-profiles", "background-task.md");
|
|
376
|
+
return readFile(candidate, "utf-8");
|
|
377
|
+
}
|
|
378
|
+
/** Render the worker prompt: the `background_task` task-flow (tool
|
|
379
|
+
* policy + notify-disposition rules + output-language reminder) with the
|
|
380
|
+
* `{context}` block, then the self-contained brief. */
|
|
381
|
+
function renderTaskPrompt(row) {
|
|
382
|
+
const flow = getTaskFlow("background_task");
|
|
383
|
+
const body = flow.length > 0 ? flow : "## Background Task\n\n{context}\n";
|
|
384
|
+
return body
|
|
385
|
+
.replace(/\{context\}/g, renderContextBlock(row))
|
|
386
|
+
.concat("\n\n## Your task (the brief)\n\n", row.brief, "\n");
|
|
387
|
+
}
|
|
388
|
+
function renderContextBlock(row) {
|
|
389
|
+
const lines = [
|
|
390
|
+
"<task>",
|
|
391
|
+
`<task_id>${row.id}</task_id>`,
|
|
392
|
+
`<title>${row.title ?? ""}</title>`,
|
|
393
|
+
`<notification_policy>${row.notificationPolicy}</notification_policy>`,
|
|
394
|
+
];
|
|
395
|
+
// Phase 4 if_significant criteria DSL (§4.3): when the spawn carried
|
|
396
|
+
// structured criteria, render them as a numbered checklist so the
|
|
397
|
+
// worker's notify decision is a deterministic per-criterion evaluation
|
|
398
|
+
// rather than a free judgement. Only meaningful for `if_significant`;
|
|
399
|
+
// injected only when present (else the worker falls back to the brief's
|
|
400
|
+
// prose criteria).
|
|
401
|
+
if (row.notificationPolicy === "if_significant"
|
|
402
|
+
&& row.significanceCriteria
|
|
403
|
+
&& row.significanceCriteria.length > 0) {
|
|
404
|
+
lines.push("<significance_criteria>");
|
|
405
|
+
lines.push("Set notify=true if AT LEAST ONE of these is met by your result; "
|
|
406
|
+
+ "otherwise notify=false. In `significance`, state which criteria "
|
|
407
|
+
+ "were met / unmet.");
|
|
408
|
+
row.significanceCriteria.forEach((c, i) => {
|
|
409
|
+
lines.push(`${i + 1}. ${c}`);
|
|
410
|
+
});
|
|
411
|
+
lines.push("</significance_criteria>");
|
|
412
|
+
}
|
|
413
|
+
lines.push("</task>");
|
|
414
|
+
return lines.join("\n");
|
|
415
|
+
}
|
|
416
|
+
void noopBackgroundTaskTransitionEmitter;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background-task runner — BACKGROUND_TASK_RUNNER_DESIGN.md §4.1.
|
|
3
|
+
*
|
|
4
|
+
* The browser-task runner's lifecycle skeleton, generalized: it owns the
|
|
5
|
+
* per-task lifecycle from POST → terminal state, REUSING the pure slot
|
|
6
|
+
* manager (`browser-task-slots.ts`) with a synthetic per-task slot key so
|
|
7
|
+
* background tasks contend ONLY on the global concurrency cap (never on a
|
|
8
|
+
* per-key queue). The Playwright plane is gone; the worker is a generic
|
|
9
|
+
* SDK session (`background-task-driver.ts`).
|
|
10
|
+
*
|
|
11
|
+
* What this owns beyond browser-task's runner:
|
|
12
|
+
* - DELIVERY ENQUEUE — the runner's `reconcileDriverOutcome` reads the
|
|
13
|
+
* finished artifact and decides delivery in ONE place (the design's
|
|
14
|
+
* "post-transition hook"): completed + notify=true ⇒ enqueue result;
|
|
15
|
+
* parked ⇒ enqueue the open clarification; failed/timeout/no_finish ⇒
|
|
16
|
+
* FAIL-LOUD synthesize a failure artifact (notify=true) + enqueue;
|
|
17
|
+
* cancelled ⇒ no delivery (the owner cancelled). notify=false ⇒ file
|
|
18
|
+
* only. The periodic recovery sweep re-enqueues any lost delivery.
|
|
19
|
+
*
|
|
20
|
+
* I/O-shaped. Excluded from the 100% coverage gate; the pure logic lives
|
|
21
|
+
* in the reused slot manager + the budget envelope.
|
|
22
|
+
*/
|
|
23
|
+
import type Database from "better-sqlite3";
|
|
24
|
+
import { type BackgroundTaskRow } from "../../db/background-task-store.js";
|
|
25
|
+
import { type DriverDeps } from "./background-task-driver.js";
|
|
26
|
+
import { type SlotState } from "../browser-task/browser-task-slots.js";
|
|
27
|
+
import { type BackgroundTaskTransitionEmitter } from "./background-task-transition-events.js";
|
|
28
|
+
/** Shared mutable slot-state container — the runner and the route layer
|
|
29
|
+
* hold the same instance (cancel-while-pending vs runner promote race). */
|
|
30
|
+
export interface BackgroundTaskSlotStateRef {
|
|
31
|
+
state: SlotState;
|
|
32
|
+
}
|
|
33
|
+
export declare function createBackgroundTaskSlotStateRef(maxConcurrent: number): BackgroundTaskSlotStateRef;
|
|
34
|
+
/**
|
|
35
|
+
* Delivery enqueuer — the runner hands the artifact to this so a
|
|
36
|
+
* `task.delivery` event lands on the bus. Wired in bootstrap from the
|
|
37
|
+
* `createBackgroundTask*DeliveryEvent` factories + the event bus (keeps
|
|
38
|
+
* the runner free of any core/messaging import).
|
|
39
|
+
*/
|
|
40
|
+
export interface BackgroundTaskDeliveryEnqueuer {
|
|
41
|
+
enqueueResult(input: {
|
|
42
|
+
taskId: string;
|
|
43
|
+
originatingChannel: string | null;
|
|
44
|
+
title: string;
|
|
45
|
+
draft: string;
|
|
46
|
+
report: string;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
enqueueClarification(input: {
|
|
49
|
+
taskId: string;
|
|
50
|
+
originatingChannel: string | null;
|
|
51
|
+
title: string;
|
|
52
|
+
clarificationId: string;
|
|
53
|
+
question: string;
|
|
54
|
+
contextSummary: string | null;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
export interface BackgroundTaskRunnerDeps {
|
|
58
|
+
db: Database.Database;
|
|
59
|
+
slotStateRef: BackgroundTaskSlotStateRef;
|
|
60
|
+
/** Drives the worker SDK session. Absent only in early-boot ordering
|
|
61
|
+
* races / tests — when absent the runner fails the row fast. */
|
|
62
|
+
driver?: DriverDeps;
|
|
63
|
+
/** Enqueues `task.delivery` events. Optional — when absent (tests) the
|
|
64
|
+
* artifact is still written + filed; only the push is skipped. */
|
|
65
|
+
deliveryEnqueuer?: BackgroundTaskDeliveryEnqueuer;
|
|
66
|
+
transitionEmitter?: BackgroundTaskTransitionEmitter;
|
|
67
|
+
/** BACKGROUND_TASK_RUNNER_DESIGN.md §10.2 / Phase 4 — when true, a task
|
|
68
|
+
* with a captured SDK session id is resumed via `query({resume})` across
|
|
69
|
+
* a daemon restart (boot) or a clarify-after-restart, falling back to
|
|
70
|
+
* re-dispatch-from-brief when the session can't be loaded. Wired from
|
|
71
|
+
* `backgroundTaskResumeAcrossRestart`; defaults to false (the v1
|
|
72
|
+
* re-dispatch-only behaviour) when omitted. */
|
|
73
|
+
resumeAcrossRestart?: boolean;
|
|
74
|
+
nowFn?: () => number;
|
|
75
|
+
}
|
|
76
|
+
export interface RunResult {
|
|
77
|
+
ok: boolean;
|
|
78
|
+
reason: "completed" | "failed" | "timeout" | "cancelled" | "parked_awaiting_user" | "no_driver" | "queued" | "task_missing" | "already_terminal";
|
|
79
|
+
state: BackgroundTaskRow["state"] | null;
|
|
80
|
+
}
|
|
81
|
+
export interface BackgroundTaskRunner {
|
|
82
|
+
runFromPost(taskId: string): Promise<RunResult>;
|
|
83
|
+
runFromScheduleRow(taskId: string): Promise<RunResult>;
|
|
84
|
+
cancel(taskId: string, reason: string): Promise<boolean>;
|
|
85
|
+
resumeAfterClarification(input: {
|
|
86
|
+
taskId: string;
|
|
87
|
+
clarificationId: string;
|
|
88
|
+
answer: string;
|
|
89
|
+
}): Promise<RunResult>;
|
|
90
|
+
/** Phase 4 (§10.2) — boot recovery for ONE non-terminal task: resume the
|
|
91
|
+
* warm SDK session when possible, else re-dispatch from brief. */
|
|
92
|
+
resumeFromBoot(taskId: string): Promise<RunResult>;
|
|
93
|
+
expireForDeadline(taskId: string, kind: "clarification_deadline" | "queue_timeout", waitedMs?: number): Promise<RunResult>;
|
|
94
|
+
__peekParkedIds(): readonly string[];
|
|
95
|
+
}
|
|
96
|
+
export declare function createBackgroundTaskRunner(deps: BackgroundTaskRunnerDeps): BackgroundTaskRunner;
|