@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,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `!revert tuning` — undo the most recent applied self-tuning config change
|
|
3
|
+
* (SELF_TUNING_REVIEW_CYCLE_DESIGN.md §3.4, Phase 3).
|
|
4
|
+
*
|
|
5
|
+
* The owner-side escape hatch for the Autonomous-plus-DM actuation posture
|
|
6
|
+
* (D1): every applied change DMs "Reply `!revert tuning` to undo", and this
|
|
7
|
+
* command is that reply. It restores the ledger's `prev` value through the
|
|
8
|
+
* same `applyConfigUpdates` chokepoint the actuator used, stamps
|
|
9
|
+
* `reverted_at` (which puts the key into the 28-day re-proposal cool-down),
|
|
10
|
+
* audits `self_tuning.reverted`, and records an explicit-correction
|
|
11
|
+
* feedback signal so the lesson loop learns from the owner's override.
|
|
12
|
+
*
|
|
13
|
+
* `runsWhilePaused: true` — a pure DB/config write with no LLM dispatch,
|
|
14
|
+
* and the owner may well have paused the agent *because* of a bad tuning
|
|
15
|
+
* change; the undo must not be locked behind `!start`.
|
|
16
|
+
*/
|
|
17
|
+
import { BangArgError } from "./registry.js";
|
|
18
|
+
import { applyConfigUpdates } from "../../api/env-writer.js";
|
|
19
|
+
import { createSettingsStore } from "../../settings/settings-store.js";
|
|
20
|
+
import { findLatestRevertableEntry, listLedgerEntries, revertAppliedTuningChange, } from "../feedback/tuning-actuator.js";
|
|
21
|
+
const USAGE = "Usage: `!revert tuning` — undo the most recent self-tuning config change.";
|
|
22
|
+
export const revertTuningCommand = {
|
|
23
|
+
prefix: "!revert",
|
|
24
|
+
title: "Revert self-tuning",
|
|
25
|
+
describe: "undo the most recent self-tuning config change",
|
|
26
|
+
details: [
|
|
27
|
+
"Restores the previous value of the most recently applied self-tuning config change via the standard config chokepoint.",
|
|
28
|
+
"The reverted key enters a 28-day re-proposal cool-down so the loop cannot immediately re-apply it.",
|
|
29
|
+
"Pure DB/config write — works while the agent is paused.",
|
|
30
|
+
],
|
|
31
|
+
runsWhilePaused: true,
|
|
32
|
+
parseArgs: (rest) => {
|
|
33
|
+
if (rest.toLowerCase() !== "tuning") {
|
|
34
|
+
throw new BangArgError(USAGE);
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
},
|
|
38
|
+
handler: async (ctx) => {
|
|
39
|
+
const entry = findLatestRevertableEntry(listLedgerEntries(ctx.db));
|
|
40
|
+
if (!entry) {
|
|
41
|
+
await ctx.notify("No applied self-tuning change to revert. Applied changes show up in the daemon's per-change DM and the weekly review's tuning ledger.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const settingsStore = createSettingsStore(ctx.db);
|
|
45
|
+
const result = await revertAppliedTuningChange({
|
|
46
|
+
db: ctx.db,
|
|
47
|
+
applyUpdates: (updates) => applyConfigUpdates(ctx.config, settingsStore, updates, { db: ctx.db }),
|
|
48
|
+
feedbackLearningEnabled: ctx.config.feedbackLearningEnabled,
|
|
49
|
+
}, entry, {
|
|
50
|
+
trigger: "bang_command",
|
|
51
|
+
reason: "owner requested revert via !revert tuning",
|
|
52
|
+
now: new Date(),
|
|
53
|
+
});
|
|
54
|
+
if (!result.ok) {
|
|
55
|
+
await ctx.notify(`Could not revert ${entry.key}: ${result.error}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
await ctx.notify(`Reverted ${entry.key} ${String(entry.blob.proposed)} → ` +
|
|
59
|
+
`${String(entry.blob.prev)} (rule ${entry.blob.rule}, applied ` +
|
|
60
|
+
`${entry.blob.applied_at.slice(0, 10)}). The key is now in a ` +
|
|
61
|
+
"28-day re-proposal cool-down.");
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -45,7 +45,7 @@ export const stopCommand = {
|
|
|
45
45
|
"",
|
|
46
46
|
"Halted:",
|
|
47
47
|
"- Morning / evening / weekly / monthly review",
|
|
48
|
-
"-
|
|
48
|
+
"- Activity scan, profile sweep",
|
|
49
49
|
"- Scheduled DMs",
|
|
50
50
|
"",
|
|
51
51
|
"Still running:",
|
|
@@ -63,7 +63,7 @@ export const startCommand = {
|
|
|
63
63
|
describe: "Resume autonomous work paused by !stop.",
|
|
64
64
|
details: [
|
|
65
65
|
"Clears the user-paused state.",
|
|
66
|
-
"Queued observations are consumed by the next eligible
|
|
66
|
+
"Queued observations are consumed by the next eligible activity scan.",
|
|
67
67
|
],
|
|
68
68
|
runsWhilePaused: true,
|
|
69
69
|
handler: async (ctx) => {
|
|
@@ -82,7 +82,7 @@ export const startCommand = {
|
|
|
82
82
|
"",
|
|
83
83
|
`Was paused: ${formatLocalLong(prev.since, ctx.config)}`,
|
|
84
84
|
"",
|
|
85
|
-
"Next
|
|
85
|
+
"Next activity scan will consume any observations",
|
|
86
86
|
"queued during the pause.",
|
|
87
87
|
].join("\n"));
|
|
88
88
|
},
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-task control bang-commands — `!status` (list the active detached
|
|
3
|
+
* tasks) and `!stop <id>` (cancel one of them). BACKGROUND_TASK_RUNNER_
|
|
4
|
+
* DESIGN.md Phase 4.
|
|
5
|
+
*
|
|
6
|
+
* Both run OUTSIDE the SessionGate (the bang interceptor in
|
|
7
|
+
* `dispatcher-message-handler` runs before any agent backend, ahead of
|
|
8
|
+
* the gate), so the owner can inspect and cancel a long-running worker
|
|
9
|
+
* even while a DM turn is in flight or a delivery turn is gated. Both are
|
|
10
|
+
* `runsWhilePaused: true` — pure DB reads + a runner abort, no LLM work.
|
|
11
|
+
*
|
|
12
|
+
* `!stop` is a PREFIX command. Bare `!stop` resolves to the exact-match
|
|
13
|
+
* pause command (`commands-stop-start.ts`); `!stop <id>` falls through to
|
|
14
|
+
* the prefix matcher here and cancels that single task. The two never
|
|
15
|
+
* collide because the registry resolves exact matches before prefixes.
|
|
16
|
+
*/
|
|
17
|
+
import type { BangCommand, BangPrefixCommand } from "./registry.js";
|
|
18
|
+
export declare const statusCommand: BangCommand;
|
|
19
|
+
export declare const stopTaskCommand: BangPrefixCommand;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-task control bang-commands — `!status` (list the active detached
|
|
3
|
+
* tasks) and `!stop <id>` (cancel one of them). BACKGROUND_TASK_RUNNER_
|
|
4
|
+
* DESIGN.md Phase 4.
|
|
5
|
+
*
|
|
6
|
+
* Both run OUTSIDE the SessionGate (the bang interceptor in
|
|
7
|
+
* `dispatcher-message-handler` runs before any agent backend, ahead of
|
|
8
|
+
* the gate), so the owner can inspect and cancel a long-running worker
|
|
9
|
+
* even while a DM turn is in flight or a delivery turn is gated. Both are
|
|
10
|
+
* `runsWhilePaused: true` — pure DB reads + a runner abort, no LLM work.
|
|
11
|
+
*
|
|
12
|
+
* `!stop` is a PREFIX command. Bare `!stop` resolves to the exact-match
|
|
13
|
+
* pause command (`commands-stop-start.ts`); `!stop <id>` falls through to
|
|
14
|
+
* the prefix matcher here and cancels that single task. The two never
|
|
15
|
+
* collide because the registry resolves exact matches before prefixes.
|
|
16
|
+
*/
|
|
17
|
+
import { getBackgroundTask, listBackgroundTasks, } from "../../db/background-task-store.js";
|
|
18
|
+
import { getBrowserTask, listBrowserTasks, } from "../../db/browser-task-store.js";
|
|
19
|
+
const BACKGROUND_ACTIVE_STATES = ["pending", "running", "awaiting_user"];
|
|
20
|
+
const BROWSER_ACTIVE_STATES = [
|
|
21
|
+
"pending",
|
|
22
|
+
"running",
|
|
23
|
+
"awaiting_user",
|
|
24
|
+
"final_confirm",
|
|
25
|
+
];
|
|
26
|
+
function backgroundTitle(row) {
|
|
27
|
+
return row.title ?? row.brief.slice(0, 60);
|
|
28
|
+
}
|
|
29
|
+
function browserTitle(row) {
|
|
30
|
+
return row.description.slice(0, 60);
|
|
31
|
+
}
|
|
32
|
+
/** Gather every non-terminal background + browser task, newest first. */
|
|
33
|
+
function listActiveTasks(ctx) {
|
|
34
|
+
const background = listBackgroundTasks(ctx.db, {
|
|
35
|
+
states: [...BACKGROUND_ACTIVE_STATES],
|
|
36
|
+
limit: 50,
|
|
37
|
+
}).map((row) => ({
|
|
38
|
+
id: row.id,
|
|
39
|
+
kind: "background",
|
|
40
|
+
state: row.state,
|
|
41
|
+
title: backgroundTitle(row),
|
|
42
|
+
}));
|
|
43
|
+
const browser = listBrowserTasks(ctx.db, {
|
|
44
|
+
states: [...BROWSER_ACTIVE_STATES],
|
|
45
|
+
limit: 50,
|
|
46
|
+
}).map((row) => ({
|
|
47
|
+
id: row.id,
|
|
48
|
+
kind: "browser",
|
|
49
|
+
state: row.state,
|
|
50
|
+
title: browserTitle(row),
|
|
51
|
+
}));
|
|
52
|
+
return [...background, ...browser];
|
|
53
|
+
}
|
|
54
|
+
/** Short, copy-pasteable id stem for the `!status` listing. The full id is
|
|
55
|
+
* a uuid v4; the first 8 chars are unique in practice and `!stop` accepts
|
|
56
|
+
* any unambiguous prefix. */
|
|
57
|
+
function shortId(id) {
|
|
58
|
+
return id.slice(0, 8);
|
|
59
|
+
}
|
|
60
|
+
function formatStatus(tasks) {
|
|
61
|
+
if (tasks.length === 0) {
|
|
62
|
+
return "No background or browser tasks are running.";
|
|
63
|
+
}
|
|
64
|
+
const lines = tasks.map((t) => {
|
|
65
|
+
const label = t.kind === "background" ? "bg" : "web";
|
|
66
|
+
return `• [${label} ${shortId(t.id)}] ${t.state} — ${t.title}`;
|
|
67
|
+
});
|
|
68
|
+
const header = tasks.length === 1
|
|
69
|
+
? "1 active task:"
|
|
70
|
+
: `${tasks.length} active tasks:`;
|
|
71
|
+
return [
|
|
72
|
+
header,
|
|
73
|
+
...lines,
|
|
74
|
+
"",
|
|
75
|
+
"Stop one with !stop <id> (the short id shown above is fine).",
|
|
76
|
+
].join("\n");
|
|
77
|
+
}
|
|
78
|
+
export const statusCommand = {
|
|
79
|
+
name: "!status",
|
|
80
|
+
title: "Task status",
|
|
81
|
+
describe: "List running background / browser tasks",
|
|
82
|
+
runsWhilePaused: true,
|
|
83
|
+
handler: async (ctx) => {
|
|
84
|
+
await ctx.notify(formatStatus(listActiveTasks(ctx)));
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
/** Resolve the owner-typed id to a single active task. Accepts the full
|
|
88
|
+
* uuid or any unambiguous prefix (so the `!status` short id works). */
|
|
89
|
+
function resolveTarget(ctx, raw) {
|
|
90
|
+
const needle = raw.trim().toLowerCase();
|
|
91
|
+
// Exact-id fast path — a full uuid lands here without scanning.
|
|
92
|
+
const bg = getBackgroundTask(ctx.db, needle);
|
|
93
|
+
if (bg && BACKGROUND_ACTIVE_STATES.includes(bg.state)) {
|
|
94
|
+
return { kind: "ok", task: { id: bg.id, kind: "background", state: bg.state, title: backgroundTitle(bg) } };
|
|
95
|
+
}
|
|
96
|
+
const web = getBrowserTask(ctx.db, needle);
|
|
97
|
+
if (web && BROWSER_ACTIVE_STATES.includes(web.state)) {
|
|
98
|
+
return { kind: "ok", task: { id: web.id, kind: "browser", state: web.state, title: browserTitle(web) } };
|
|
99
|
+
}
|
|
100
|
+
// Prefix match across the active set.
|
|
101
|
+
const matches = listActiveTasks(ctx).filter((t) => t.id.toLowerCase().startsWith(needle));
|
|
102
|
+
if (matches.length === 1)
|
|
103
|
+
return { kind: "ok", task: matches[0] };
|
|
104
|
+
if (matches.length === 0)
|
|
105
|
+
return { kind: "none" };
|
|
106
|
+
return { kind: "ambiguous", count: matches.length };
|
|
107
|
+
}
|
|
108
|
+
export const stopTaskCommand = {
|
|
109
|
+
prefix: "!stop",
|
|
110
|
+
title: "Stop a task",
|
|
111
|
+
describe: "Cancel one task by id (bare !stop pauses the agent)",
|
|
112
|
+
runsWhilePaused: true,
|
|
113
|
+
handler: async (ctx, args) => {
|
|
114
|
+
const id = typeof args === "string" ? args : "";
|
|
115
|
+
if (id.length === 0) {
|
|
116
|
+
// Unreachable in practice — bare `!stop` resolves to the exact pause
|
|
117
|
+
// command — but guard so a future resolver change can't crash here.
|
|
118
|
+
await ctx.notify("Usage: !stop <id> to cancel one task (run !status for ids). " +
|
|
119
|
+
"Bare !stop pauses the whole agent.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const resolved = resolveTarget(ctx, id);
|
|
123
|
+
if (resolved.kind === "none") {
|
|
124
|
+
await ctx.notify(`No active task matches "${id}". Run !status to see what's running.`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (resolved.kind === "ambiguous") {
|
|
128
|
+
await ctx.notify(`"${id}" matches ${resolved.count} active tasks — add more characters of the id.`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const task = resolved.task;
|
|
132
|
+
const cancel = task.kind === "background"
|
|
133
|
+
? ctx.cancelBackgroundTask
|
|
134
|
+
: ctx.cancelBrowserTask;
|
|
135
|
+
if (!cancel) {
|
|
136
|
+
// Runner not wired (lite install / boot race). The row still exists;
|
|
137
|
+
// tell the owner rather than silently dropping the request.
|
|
138
|
+
await ctx.notify(`Can't stop that ${task.kind} task right now — the runner isn't available. ` +
|
|
139
|
+
"Try again in a moment.");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const ok = await cancel(task.id, "user_bang_stop");
|
|
143
|
+
await ctx.notify(ok
|
|
144
|
+
? `Stopping the ${task.kind} task "${task.title}".`
|
|
145
|
+
: `Couldn't stop "${task.title}" — it may have already finished.`);
|
|
146
|
+
},
|
|
147
|
+
};
|
|
@@ -108,7 +108,7 @@ async function requireWikiWorkspace(ctx, workspaceName = null) {
|
|
|
108
108
|
export const ingestCommand = {
|
|
109
109
|
prefix: "!ingest",
|
|
110
110
|
title: "Ingest URLs",
|
|
111
|
-
describe: "ingest one or more URLs into the wiki's raw layer
|
|
111
|
+
describe: "ingest one or more URLs into the wiki's raw layer",
|
|
112
112
|
parseArgs(rest) {
|
|
113
113
|
try {
|
|
114
114
|
const { workspaceName, rest: payload } = splitWorkspaceToken(rest);
|
|
@@ -355,7 +355,7 @@ function renderCompileInProgressDm(holder, workspaceName) {
|
|
|
355
355
|
export const askCommand = {
|
|
356
356
|
prefix: "!ask",
|
|
357
357
|
title: "Ask wiki",
|
|
358
|
-
describe: "ask a question against the wiki
|
|
358
|
+
describe: "ask a question against the wiki",
|
|
359
359
|
parseArgs(rest) {
|
|
360
360
|
const { workspaceName, rest: payload } = splitWorkspaceToken(rest);
|
|
361
361
|
const question = payload.trim();
|
|
@@ -458,7 +458,7 @@ export const wikiHelpCommand = {
|
|
|
458
458
|
export const lintCommand = {
|
|
459
459
|
prefix: "!lint",
|
|
460
460
|
title: "Lint wiki",
|
|
461
|
-
describe: "audit the wiki and write a dated health report
|
|
461
|
+
describe: "audit the wiki and write a dated health report",
|
|
462
462
|
parseArgs(rest) {
|
|
463
463
|
const { workspaceName, rest: payload } = splitWorkspaceToken(rest);
|
|
464
464
|
if (payload.trim().length > 0) {
|
|
@@ -494,7 +494,7 @@ export const lintCommand = {
|
|
|
494
494
|
export const traceCommand = {
|
|
495
495
|
prefix: "!trace",
|
|
496
496
|
title: "Trace wiki topic",
|
|
497
|
-
describe: "reconstruct an idea's evolution across raw / wiki / outputs
|
|
497
|
+
describe: "reconstruct an idea's evolution across raw / wiki / outputs",
|
|
498
498
|
parseArgs(rest) {
|
|
499
499
|
const { workspaceName, rest: payload } = splitWorkspaceToken(rest);
|
|
500
500
|
const topic = payload.trim();
|
|
@@ -560,7 +560,7 @@ export function parseConnectArgs(rest) {
|
|
|
560
560
|
export const connectCommand = {
|
|
561
561
|
prefix: "!connect",
|
|
562
562
|
title: "Connect wiki domains",
|
|
563
|
-
describe: "bridge two domains in the wiki
|
|
563
|
+
describe: "bridge two domains in the wiki",
|
|
564
564
|
parseArgs(rest) {
|
|
565
565
|
return parseConnectArgs(rest);
|
|
566
566
|
},
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
export { BangCommandRegistry, BangArgError, buildPausedNotice, buildUnknownCommandReply, getBangCommandName, makeNotify, normalizeBangCommandText, tryHandle, } from "./registry.js";
|
|
7
7
|
export type { BangCommand, BangCommandContext, BangCommandMatch, BangPrefixCommand, RegisteredBangCommand, } from "./registry.js";
|
|
8
8
|
export { stopCommand, startCommand } from "./commands-stop-start.js";
|
|
9
|
+
export { statusCommand, stopTaskCommand } from "./commands-task-control.js";
|
|
9
10
|
export { closeCommand } from "./commands-close.js";
|
|
10
11
|
export { costAllCommand, costBackendCommands, formatCostAll, formatCostFiltered, } from "./commands-cost.js";
|
|
11
12
|
export { reportCommand, formatReport } from "./commands-report.js";
|
|
@@ -13,6 +14,7 @@ export { helpCommand, formatHelp } from "./commands-help.js";
|
|
|
13
14
|
export { askCommand, connectCommand, compileCommand, lintCommand, parseConnectArgs, traceCommand, ingestCommand, wikiHelpCommand, wikiStatusCommand, } from "./commands-wiki.js";
|
|
14
15
|
export { researchCommand, parseResearchArgs } from "./commands-research.js";
|
|
15
16
|
export { checksCommand, formatChecks } from "./commands-checks.js";
|
|
17
|
+
export { revertTuningCommand } from "./commands-revert-tuning.js";
|
|
16
18
|
export { buildSystemMarker, ensureSystemMarker, formatLocalLong, formatLocalShort, formatMoney, MOBILE_REPLY_BUDGET, truncateForMobile, } from "./format-utils.js";
|
|
17
19
|
export { CUSTOM_BANG_COMMAND_SOURCE, DEFAULT_USER_BANG_COMMAND_SKILLS, USER_BANG_COMMAND_NAME_PATTERN, buildUserBangCommandPrompt, createUserBangCommand, createUserBangCommandEvent, deleteUserBangCommand, getEnabledUserBangCommandByCommand, getUserBangCommandByCommand, getUserBangCommandById, listUserBangCommands, normalizeBangCommandName, parseEnabledSkills, resolveCommandSkillSlugs, serializeEnabledSkills, updateUserBangCommand, } from "./user-commands.js";
|
|
18
20
|
export type { NormalizeBangCommandNameResult, UserBangCommand, UserBangCommandInput, } from "./user-commands.js";
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { BangCommandRegistry, BangArgError, buildPausedNotice, buildUnknownCommandReply, getBangCommandName, makeNotify, normalizeBangCommandText, tryHandle, } from "./registry.js";
|
|
7
7
|
export { stopCommand, startCommand } from "./commands-stop-start.js";
|
|
8
|
+
export { statusCommand, stopTaskCommand } from "./commands-task-control.js";
|
|
8
9
|
export { closeCommand } from "./commands-close.js";
|
|
9
10
|
export { costAllCommand, costBackendCommands, formatCostAll, formatCostFiltered, } from "./commands-cost.js";
|
|
10
11
|
export { reportCommand, formatReport } from "./commands-report.js";
|
|
@@ -12,10 +13,12 @@ export { helpCommand, formatHelp } from "./commands-help.js";
|
|
|
12
13
|
export { askCommand, connectCommand, compileCommand, lintCommand, parseConnectArgs, traceCommand, ingestCommand, wikiHelpCommand, wikiStatusCommand, } from "./commands-wiki.js";
|
|
13
14
|
export { researchCommand, parseResearchArgs } from "./commands-research.js";
|
|
14
15
|
export { checksCommand, formatChecks } from "./commands-checks.js";
|
|
16
|
+
export { revertTuningCommand } from "./commands-revert-tuning.js";
|
|
15
17
|
export { buildSystemMarker, ensureSystemMarker, formatLocalLong, formatLocalShort, formatMoney, MOBILE_REPLY_BUDGET, truncateForMobile, } from "./format-utils.js";
|
|
16
18
|
export { CUSTOM_BANG_COMMAND_SOURCE, DEFAULT_USER_BANG_COMMAND_SKILLS, USER_BANG_COMMAND_NAME_PATTERN, buildUserBangCommandPrompt, createUserBangCommand, createUserBangCommandEvent, deleteUserBangCommand, getEnabledUserBangCommandByCommand, getUserBangCommandByCommand, getUserBangCommandById, listUserBangCommands, normalizeBangCommandName, parseEnabledSkills, resolveCommandSkillSlugs, serializeEnabledSkills, updateUserBangCommand, } from "./user-commands.js";
|
|
17
19
|
import { BangCommandRegistry } from "./registry.js";
|
|
18
20
|
import { startCommand, stopCommand } from "./commands-stop-start.js";
|
|
21
|
+
import { statusCommand, stopTaskCommand } from "./commands-task-control.js";
|
|
19
22
|
import { closeCommand } from "./commands-close.js";
|
|
20
23
|
import { costAllCommand, costBackendCommands, } from "./commands-cost.js";
|
|
21
24
|
import { reportCommand } from "./commands-report.js";
|
|
@@ -23,6 +26,7 @@ import { helpCommand } from "./commands-help.js";
|
|
|
23
26
|
import { askCommand, connectCommand, compileCommand, lintCommand, traceCommand, ingestCommand, wikiHelpCommand, wikiStatusCommand, } from "./commands-wiki.js";
|
|
24
27
|
import { researchCommand } from "./commands-research.js";
|
|
25
28
|
import { checksCommand } from "./commands-checks.js";
|
|
29
|
+
import { revertTuningCommand } from "./commands-revert-tuning.js";
|
|
26
30
|
/**
|
|
27
31
|
* Build a registry preloaded with the v1 built-in commands. The registry
|
|
28
32
|
* is mutable — callers can `.register(...)` more commands afterwards.
|
|
@@ -31,6 +35,11 @@ export function createDefaultBangCommandRegistry() {
|
|
|
31
35
|
const registry = new BangCommandRegistry();
|
|
32
36
|
registry.register(stopCommand);
|
|
33
37
|
registry.register(startCommand);
|
|
38
|
+
// BACKGROUND_TASK_RUNNER_DESIGN.md Phase 4 — per-task control. `!status`
|
|
39
|
+
// (exact) lists active detached tasks; `!stop <id>` (prefix) cancels one.
|
|
40
|
+
// Bare `!stop` still resolves to the exact pause command above.
|
|
41
|
+
registry.register(statusCommand);
|
|
42
|
+
registry.register(stopTaskCommand);
|
|
34
43
|
registry.register(closeCommand);
|
|
35
44
|
registry.register(costAllCommand);
|
|
36
45
|
for (const cmd of costBackendCommands) {
|
|
@@ -54,5 +63,8 @@ export function createDefaultBangCommandRegistry() {
|
|
|
54
63
|
// weekly surfacing lives inside `routine.weekly_review` and uses
|
|
55
64
|
// `/api/browser-history/reloads/weekly` instead.
|
|
56
65
|
registry.register(checksCommand);
|
|
66
|
+
// SELF_TUNING_REVIEW_CYCLE_DESIGN.md §3.4 Phase 3 — `!revert tuning`,
|
|
67
|
+
// the owner-side undo for autonomously applied tuning changes.
|
|
68
|
+
registry.register(revertTuningCommand);
|
|
57
69
|
return registry;
|
|
58
70
|
}
|
|
@@ -53,6 +53,18 @@ export interface BangCommandContext {
|
|
|
53
53
|
}>;
|
|
54
54
|
enqueueUserBangCommand?: (command: UserBangCommand, event: MessageEvent) => Promise<void>;
|
|
55
55
|
enqueueWikiEvent?: (event: Event) => Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Per-task `!stop <id>` cancel hooks (BACKGROUND_TASK_RUNNER_DESIGN.md
|
|
58
|
+
* Phase 4). Wired from the dispatcher's live runner instances, which
|
|
59
|
+
* hold the in-memory worker handles the abort needs to reach. Optional
|
|
60
|
+
* so bang-command unit fixtures (and lite installs without a runner)
|
|
61
|
+
* can omit them — the `!stop` handler tells the owner the runner is
|
|
62
|
+
* unavailable rather than silently dropping the request. Return `true`
|
|
63
|
+
* when an abort/terminal-transition was issued, `false` when the task
|
|
64
|
+
* was already gone.
|
|
65
|
+
*/
|
|
66
|
+
cancelBackgroundTask?: (taskId: string, reason: string) => Promise<boolean>;
|
|
67
|
+
cancelBrowserTask?: (taskId: string, reason: string) => Promise<boolean>;
|
|
56
68
|
/**
|
|
57
69
|
* BROWSER_HISTORY_INTEGRATION_PLAN P3 — wire-through for the
|
|
58
70
|
* `!research accept` / `!research wiki` paths. The bang handler
|
|
@@ -2,16 +2,24 @@
|
|
|
2
2
|
* Day-boundary fan-out helper — enumerates active research clusters
|
|
3
3
|
* with new meaningful activity in the last 24h and emits one
|
|
4
4
|
* `routine.research_cluster_update` event per cluster onto the
|
|
5
|
-
* EventBus. Called
|
|
6
|
-
*
|
|
5
|
+
* EventBus. Called from the scheduler's day-boundary callback (see
|
|
6
|
+
* `index.ts`), which can fire MORE than once per agent-day — the 04:00
|
|
7
|
+
* cron, wake catch-up (every detected sleep gap >= 5 min), and the
|
|
8
|
+
* morning self-heal all invoke it.
|
|
7
9
|
*
|
|
8
|
-
* BROWSER_HISTORY_INTEGRATION_PLAN §10.6 step 3
|
|
10
|
+
* BROWSER_HISTORY_INTEGRATION_PLAN §10.6 step 3; dedup added by
|
|
11
|
+
* RESEARCH_CLUSTER_COST_FIX_PLAN.md F1 after replayed day boundaries
|
|
12
|
+
* re-enqueued the same cluster ~25x in one morning.
|
|
9
13
|
*
|
|
10
|
-
* Pure orchestration — no LLM in this path.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* Pure orchestration — no LLM in this path. Each cluster is atomically
|
|
15
|
+
* CLAIMED for `todayAgentDay` BEFORE its event is enqueued, so neither a
|
|
16
|
+
* later sequential replay (`listClustersNeedingUpdate` filters claimed
|
|
17
|
+
* rows) nor a concurrently in-flight callback (the per-row claim is a
|
|
18
|
+
* single atomic UPDATE — only one fire wins) can double-fire — exactly
|
|
19
|
+
* one enqueue per cluster per agent day. A cluster whose claimed run
|
|
20
|
+
* fails retries on the next agent day; the journal task flow backfills
|
|
21
|
+
* the missed day. The fan-out is bounded by the `limit` parameter
|
|
22
|
+
* (default 25) so a backlog from a long outage cannot flood the queue.
|
|
15
23
|
*/
|
|
16
24
|
import type Database from "better-sqlite3";
|
|
17
25
|
export interface EventBusLike {
|
|
@@ -23,17 +31,23 @@ export interface FanoutResult {
|
|
|
23
31
|
enqueuedSlugs: string[];
|
|
24
32
|
}
|
|
25
33
|
/**
|
|
26
|
-
* Enumerate active clusters with new meaningful activity
|
|
27
|
-
*
|
|
28
|
-
* event per cluster. Always returns
|
|
29
|
-
* can log a structured summary.
|
|
34
|
+
* Enumerate active clusters with new meaningful activity not yet
|
|
35
|
+
* enqueued for `todayAgentDay` and enqueue one
|
|
36
|
+
* `routine.research_cluster_update` event per cluster. Always returns
|
|
37
|
+
* the slugs it enqueued so the caller can log a structured summary.
|
|
38
|
+
*
|
|
39
|
+
* `todayAgentDay` is the caller-computed local agent-day label
|
|
40
|
+
* (`getAgentDayDateStr(config.timezone, config.dayBoundaryHour)`) —
|
|
41
|
+
* the fan-out is timezone-blind by design so it stays pure and
|
|
42
|
+
* deterministic in tests.
|
|
30
43
|
*
|
|
31
44
|
* When `eventBus` is absent (early-boot path before EventBus wiring),
|
|
32
45
|
* the function returns an empty result without throwing — the next
|
|
33
46
|
* day-boundary tick re-evaluates and picks up the clusters once
|
|
34
|
-
* messaging is live.
|
|
47
|
+
* messaging is live. Nothing is stamped on that path.
|
|
35
48
|
*/
|
|
36
|
-
export declare function fanoutResearchClusterUpdates(db: Database.Database, eventBus: EventBusLike | null | undefined, options
|
|
49
|
+
export declare function fanoutResearchClusterUpdates(db: Database.Database, eventBus: EventBusLike | null | undefined, options: {
|
|
50
|
+
todayAgentDay: string;
|
|
37
51
|
lookbackMs?: number;
|
|
38
52
|
limit?: number;
|
|
39
53
|
nowMs?: number;
|
|
@@ -2,41 +2,64 @@
|
|
|
2
2
|
* Day-boundary fan-out helper — enumerates active research clusters
|
|
3
3
|
* with new meaningful activity in the last 24h and emits one
|
|
4
4
|
* `routine.research_cluster_update` event per cluster onto the
|
|
5
|
-
* EventBus. Called
|
|
6
|
-
*
|
|
5
|
+
* EventBus. Called from the scheduler's day-boundary callback (see
|
|
6
|
+
* `index.ts`), which can fire MORE than once per agent-day — the 04:00
|
|
7
|
+
* cron, wake catch-up (every detected sleep gap >= 5 min), and the
|
|
8
|
+
* morning self-heal all invoke it.
|
|
7
9
|
*
|
|
8
|
-
* BROWSER_HISTORY_INTEGRATION_PLAN §10.6 step 3
|
|
10
|
+
* BROWSER_HISTORY_INTEGRATION_PLAN §10.6 step 3; dedup added by
|
|
11
|
+
* RESEARCH_CLUSTER_COST_FIX_PLAN.md F1 after replayed day boundaries
|
|
12
|
+
* re-enqueued the same cluster ~25x in one morning.
|
|
9
13
|
*
|
|
10
|
-
* Pure orchestration — no LLM in this path.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* Pure orchestration — no LLM in this path. Each cluster is atomically
|
|
15
|
+
* CLAIMED for `todayAgentDay` BEFORE its event is enqueued, so neither a
|
|
16
|
+
* later sequential replay (`listClustersNeedingUpdate` filters claimed
|
|
17
|
+
* rows) nor a concurrently in-flight callback (the per-row claim is a
|
|
18
|
+
* single atomic UPDATE — only one fire wins) can double-fire — exactly
|
|
19
|
+
* one enqueue per cluster per agent day. A cluster whose claimed run
|
|
20
|
+
* fails retries on the next agent day; the journal task flow backfills
|
|
21
|
+
* the missed day. The fan-out is bounded by the `limit` parameter
|
|
22
|
+
* (default 25) so a backlog from a long outage cannot flood the queue.
|
|
15
23
|
*/
|
|
16
|
-
import { listClustersNeedingUpdate } from "../../db/browser-history-store.js";
|
|
24
|
+
import { claimClusterJournalEnqueue, listClustersNeedingUpdate, } from "../../db/browser-history-store.js";
|
|
17
25
|
import { createResearchCommandEvent } from "./research-events.js";
|
|
18
26
|
export const RESEARCH_CLUSTER_FANOUT_LOOKBACK_MS = 24 * 60 * 60 * 1000;
|
|
19
27
|
export const RESEARCH_CLUSTER_FANOUT_LIMIT = 25;
|
|
20
28
|
/**
|
|
21
|
-
* Enumerate active clusters with new meaningful activity
|
|
22
|
-
*
|
|
23
|
-
* event per cluster. Always returns
|
|
24
|
-
* can log a structured summary.
|
|
29
|
+
* Enumerate active clusters with new meaningful activity not yet
|
|
30
|
+
* enqueued for `todayAgentDay` and enqueue one
|
|
31
|
+
* `routine.research_cluster_update` event per cluster. Always returns
|
|
32
|
+
* the slugs it enqueued so the caller can log a structured summary.
|
|
33
|
+
*
|
|
34
|
+
* `todayAgentDay` is the caller-computed local agent-day label
|
|
35
|
+
* (`getAgentDayDateStr(config.timezone, config.dayBoundaryHour)`) —
|
|
36
|
+
* the fan-out is timezone-blind by design so it stays pure and
|
|
37
|
+
* deterministic in tests.
|
|
25
38
|
*
|
|
26
39
|
* When `eventBus` is absent (early-boot path before EventBus wiring),
|
|
27
40
|
* the function returns an empty result without throwing — the next
|
|
28
41
|
* day-boundary tick re-evaluates and picks up the clusters once
|
|
29
|
-
* messaging is live.
|
|
42
|
+
* messaging is live. Nothing is stamped on that path.
|
|
30
43
|
*/
|
|
31
|
-
export async function fanoutResearchClusterUpdates(db, eventBus, options
|
|
44
|
+
export async function fanoutResearchClusterUpdates(db, eventBus, options) {
|
|
32
45
|
if (!eventBus)
|
|
33
46
|
return { enqueuedSlugs: [] };
|
|
34
47
|
const lookbackMs = options.lookbackMs ?? RESEARCH_CLUSTER_FANOUT_LOOKBACK_MS;
|
|
35
48
|
const limit = options.limit ?? RESEARCH_CLUSTER_FANOUT_LIMIT;
|
|
36
49
|
const nowMs = options.nowMs ?? Date.now();
|
|
37
|
-
const rows = listClustersNeedingUpdate(db, lookbackMs, nowMs, limit);
|
|
50
|
+
const rows = listClustersNeedingUpdate(db, lookbackMs, nowMs, limit, options.todayAgentDay);
|
|
38
51
|
const enqueuedSlugs = [];
|
|
39
52
|
for (const row of rows) {
|
|
53
|
+
// Claim BEFORE the put. The claim is atomic, so if a concurrent
|
|
54
|
+
// day-boundary fire (the 04:00 cron is fire-and-forget and can
|
|
55
|
+
// overlap a wake catch-up) already claimed this cluster for today,
|
|
56
|
+
// our claim returns false and we skip it — exactly one enqueue per
|
|
57
|
+
// cluster per agent day. If the put then throws, the claim still
|
|
58
|
+
// persists so the cluster waits for the next agent day instead of
|
|
59
|
+
// re-firing on the next day-boundary replay.
|
|
60
|
+
if (!claimClusterJournalEnqueue(db, row.slug, options.todayAgentDay)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
40
63
|
await eventBus.put(createResearchCommandEvent({
|
|
41
64
|
processKey: "routine.research_cluster_update",
|
|
42
65
|
slug: row.slug,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type Database from "better-sqlite3";
|
|
2
2
|
import type { AgentConfig } from "../config.js";
|
|
3
|
-
export declare const PROACTIVE_FORWARD_TYPES: readonly ["proactive_forward", "proactive_forward_batched", "scheduled_dm"];
|
|
3
|
+
export declare const PROACTIVE_FORWARD_TYPES: readonly ["proactive_forward", "proactive_forward_batched", "scheduled_dm", "task_result", "task_clarification"];
|
|
4
4
|
export type ProactiveForwardType = (typeof PROACTIVE_FORWARD_TYPES)[number];
|
|
5
5
|
/**
|
|
6
6
|
* Render-side suffix that goes after the role tag in the assembled
|
|
@@ -17,6 +17,9 @@ export type ProactiveForwardType = (typeof PROACTIVE_FORWARD_TYPES)[number];
|
|
|
17
17
|
* embed the dispatch timestamp here: every line already carries the
|
|
18
18
|
* `[timestamp]` prefix added by the caller, so a second timestamp
|
|
19
19
|
* in the suffix would just duplicate that information.
|
|
20
|
+
* - `task_result` / `task_clarification` → task-delivery annotations
|
|
21
|
+
* used by the DM agent to understand that a background task result or
|
|
22
|
+
* clarification was already surfaced.
|
|
20
23
|
*/
|
|
21
24
|
export declare function formatForwardSuffix(metadata: Record<string, unknown>): string;
|
|
22
25
|
export interface DeliveryRow {
|
|
@@ -41,4 +44,5 @@ export declare function recordProactiveForwardDeliveries(params: {
|
|
|
41
44
|
dispatchIds?: string[];
|
|
42
45
|
originSessionIds?: Array<number | null | undefined>;
|
|
43
46
|
notificationType: ProactiveForwardType;
|
|
47
|
+
extraMetadata?: Record<string, unknown>;
|
|
44
48
|
}): ProactiveForwardRecordResult;
|
|
@@ -12,6 +12,11 @@ export const PROACTIVE_FORWARD_TYPES = [
|
|
|
12
12
|
// `notification_log` only and the user's follow-up reply would
|
|
13
13
|
// have nothing to anchor to.
|
|
14
14
|
"scheduled_dm",
|
|
15
|
+
// BACKGROUND_TASK_RUNNER_DESIGN.md Phase 1 — task.delivery records
|
|
16
|
+
// browser-task results and clarification prompts into owner-facing
|
|
17
|
+
// conversation history.
|
|
18
|
+
"task_result",
|
|
19
|
+
"task_clarification",
|
|
15
20
|
];
|
|
16
21
|
/**
|
|
17
22
|
* Render-side suffix that goes after the role tag in the assembled
|
|
@@ -28,9 +33,16 @@ export const PROACTIVE_FORWARD_TYPES = [
|
|
|
28
33
|
* embed the dispatch timestamp here: every line already carries the
|
|
29
34
|
* `[timestamp]` prefix added by the caller, so a second timestamp
|
|
30
35
|
* in the suffix would just duplicate that information.
|
|
36
|
+
* - `task_result` / `task_clarification` → task-delivery annotations
|
|
37
|
+
* used by the DM agent to understand that a background task result or
|
|
38
|
+
* clarification was already surfaced.
|
|
31
39
|
*/
|
|
32
40
|
export function formatForwardSuffix(metadata) {
|
|
33
41
|
switch (getProactiveForwardType(metadata)) {
|
|
42
|
+
case "task_result":
|
|
43
|
+
return " (background task result delivered)";
|
|
44
|
+
case "task_clarification":
|
|
45
|
+
return " (background task clarification requested)";
|
|
34
46
|
case "scheduled_dm":
|
|
35
47
|
return " (scheduled DM dispatched)";
|
|
36
48
|
case "proactive_forward":
|
|
@@ -99,6 +111,7 @@ export function recordProactiveForwardDeliveries(params) {
|
|
|
99
111
|
notificationType: params.notificationType,
|
|
100
112
|
dispatchIds,
|
|
101
113
|
originSessionIds,
|
|
114
|
+
...(params.extraMetadata ?? {}),
|
|
102
115
|
};
|
|
103
116
|
let inserted = 0;
|
|
104
117
|
const sessionIds = [];
|
|
@@ -16,6 +16,9 @@ import { CONTEXT_RELATIVE_PATHS } from "../context-paths.js";
|
|
|
16
16
|
/** Vocabulary understood by `review-context.ts:reviewFlowsMatch`. */
|
|
17
17
|
const REVIEW_FLOW_VOCAB = new Set([
|
|
18
18
|
"all",
|
|
19
|
+
"activity-scan",
|
|
20
|
+
// Legacy token for the activity-scan flow (the agent was "Hourly Check"
|
|
21
|
+
// until v0.1.11); kept so pre-rename `_index.md` rows stay valid.
|
|
19
22
|
"hourly",
|
|
20
23
|
"morning",
|
|
21
24
|
"evening",
|
|
@@ -205,7 +208,7 @@ export function renderReconcilerBlockBody(rows, updated) {
|
|
|
205
208
|
lines.push("## Notes");
|
|
206
209
|
lines.push("");
|
|
207
210
|
lines.push("- Maintained by the daemon reconciler (`packages/daemon/src/core/context/index-reconciler.ts`).");
|
|
208
|
-
lines.push("- `Review flows` tokens: `all`, `
|
|
211
|
+
lines.push("- `Review flows` tokens: `all`, `activity-scan`, `morning`, `evening`, `weekly`,");
|
|
209
212
|
lines.push(" `monthly`, `roadmap`, or `-` when no flow should auto-load the file.");
|
|
210
213
|
return lines.join("\n");
|
|
211
214
|
}
|
|
@@ -271,7 +274,7 @@ function defaultCells(entry) {
|
|
|
271
274
|
if (path === CONTEXT_RELATIVE_PATHS.today) {
|
|
272
275
|
return emit({
|
|
273
276
|
purpose: "Current-day schedule, tasks, agent plan, handoff",
|
|
274
|
-
reviewFlows: "
|
|
277
|
+
reviewFlows: "activity-scan, morning, evening",
|
|
275
278
|
});
|
|
276
279
|
}
|
|
277
280
|
if (path === CONTEXT_RELATIVE_PATHS.yesterday) {
|
|
@@ -25,10 +25,12 @@ export interface PolicySnapshotEntry {
|
|
|
25
25
|
slug: string;
|
|
26
26
|
status: PolicyStatus;
|
|
27
27
|
/**
|
|
28
|
-
* Cron expression read from the linked
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* Cron expression read from the linked execution vehicle: the Agent's
|
|
29
|
+
* `policies/agents/<slug>/agent.md` `schedule.expression`, falling back to
|
|
30
|
+
* a legacy `policies/routines/custom/<slug>.md` `cron` (inert
|
|
31
|
+
* pre-migration files). Null when nothing is linked or neither file
|
|
32
|
+
* resolves. Frozen at snapshot time — edits propagate on the next
|
|
33
|
+
* reconcile pass.
|
|
32
34
|
*/
|
|
33
35
|
cadence: string | null;
|
|
34
36
|
/** Slug from `linked.routine` frontmatter, or null. */
|