@aitne/daemon 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/adapter-watchdog.d.ts +70 -0
- package/dist/adapters/adapter-watchdog.js +115 -0
- package/dist/adapters/discord.d.ts +17 -1
- package/dist/adapters/discord.js +33 -0
- package/dist/adapters/notification-manager.d.ts +27 -1
- package/dist/adapters/notification-manager.js +54 -39
- package/dist/adapters/slack-adapter.d.ts +26 -1
- package/dist/adapters/slack-adapter.js +41 -0
- package/dist/adapters/telegram-adapter.d.ts +18 -1
- package/dist/adapters/telegram-adapter.js +41 -2
- package/dist/adapters/types.d.ts +20 -0
- package/dist/adapters/whatsapp-adapter.d.ts +26 -7
- package/dist/adapters/whatsapp-adapter.js +74 -21
- package/dist/api/env-writer.d.ts +1 -0
- package/dist/api/env-writer.js +17 -7
- package/dist/api/helpers/agent-errors-registry.d.ts +5 -5
- package/dist/api/helpers/agent-errors-registry.js +5 -5
- package/dist/api/routes/agent-schedule.js +5 -1
- package/dist/api/routes/agent.js +33 -12
- package/dist/api/routes/agents/index.js +75 -16
- package/dist/api/routes/agents/views.d.ts +37 -2
- package/dist/api/routes/agents/views.js +64 -2
- package/dist/api/routes/apple-calendar.js +4 -1
- package/dist/api/routes/background-task.d.ts +22 -0
- package/dist/api/routes/background-task.js +338 -0
- package/dist/api/routes/browser-history.js +9 -1
- package/dist/api/routes/calendar.js +12 -2
- package/dist/api/routes/context/path-resolve.js +6 -1
- package/dist/api/routes/context/permissions.js +12 -2
- package/dist/api/routes/context/snapshots.js +0 -3
- package/dist/api/routes/context/write.js +3 -17
- package/dist/api/routes/dashboard/config.js +58 -12
- package/dist/api/routes/dashboard/cost-approvals.js +66 -0
- package/dist/api/routes/dashboard/notifications.js +9 -9
- package/dist/api/routes/dashboard/oauth-google.js +5 -3
- package/dist/api/routes/feedback.d.ts +3 -0
- package/dist/api/routes/feedback.js +349 -0
- package/dist/api/routes/git.js +10 -3
- package/dist/api/routes/github.js +5 -1
- package/dist/api/routes/integrations/crud-patch.js +5 -1
- package/dist/api/routes/integrations-reconcile.js +2 -2
- package/dist/api/routes/mcp.js +65 -13
- package/dist/api/routes/notion.d.ts +1 -1
- package/dist/api/routes/observations.js +7 -7
- package/dist/api/routes/obsidian.d.ts +1 -1
- package/dist/api/routes/receipts.js +5 -1
- package/dist/api/routes/setup-migrate.js +1 -1
- package/dist/api/routes/setup.js +1 -1
- package/dist/api/routes/task-flows.d.ts +1 -1
- package/dist/api/routes/task-flows.js +1 -1
- package/dist/api/routes/tuning.d.ts +29 -0
- package/dist/api/routes/tuning.js +304 -0
- package/dist/api/server.d.ts +44 -16
- package/dist/api/server.js +12 -0
- package/dist/bootstrap/adapters.d.ts +19 -0
- package/dist/bootstrap/adapters.js +61 -0
- package/dist/bootstrap/api.d.ts +5 -3
- package/dist/bootstrap/api.js +45 -13
- package/dist/bootstrap/catchup.d.ts +1 -1
- package/dist/bootstrap/catchup.js +11 -11
- package/dist/bootstrap/event-pipeline.d.ts +11 -0
- package/dist/bootstrap/event-pipeline.js +246 -8
- package/dist/bootstrap/observers.js +9 -6
- package/dist/bootstrap/schedule-helpers.d.ts +104 -6
- package/dist/bootstrap/schedule-helpers.js +172 -19
- package/dist/config.js +32 -12
- package/dist/core/agent-core.d.ts +33 -1
- package/dist/core/agent-core.js +36 -1
- package/dist/core/agents/activity-scan-cadence.d.ts +103 -0
- package/dist/core/agents/activity-scan-cadence.js +127 -0
- package/dist/core/agents/agent-route-override.d.ts +53 -0
- package/dist/core/agents/agent-route-override.js +69 -0
- package/dist/core/agents/builtin-registry.d.ts +51 -14
- package/dist/core/agents/builtin-registry.js +92 -15
- package/dist/core/agents/config-gate-reconcile.d.ts +38 -0
- package/dist/core/agents/config-gate-reconcile.js +51 -0
- package/dist/core/agents/cron-substitute.d.ts +1 -1
- package/dist/core/agents/cron-substitute.js +1 -1
- package/dist/core/agents/custom-routine-migration.d.ts +60 -0
- package/dist/core/agents/custom-routine-migration.js +149 -0
- package/dist/core/agents/firing-blocked.d.ts +1 -1
- package/dist/core/agents/hourly-cadence.d.ts +102 -0
- package/dist/core/agents/hourly-cadence.js +126 -0
- package/dist/core/agents/loader-boot.js +23 -0
- package/dist/core/agents/loader.d.ts +19 -0
- package/dist/core/agents/loader.js +34 -2
- package/dist/core/agents/override-merge.d.ts +1 -1
- package/dist/core/agents/override-merge.js +9 -1
- package/dist/core/agents/recurrence-convert.d.ts +1 -1
- package/dist/core/agents/recurrence-convert.js +1 -1
- package/dist/core/agents/recurring-schedule-adapter.js +8 -0
- package/dist/core/alerts.js +6 -6
- package/dist/core/backends/auth-health-monitor.d.ts +2 -2
- package/dist/core/backends/auth-health-monitor.js +1 -1
- package/dist/core/backends/backend-router.d.ts +27 -1
- package/dist/core/backends/backend-router.js +165 -1
- package/dist/core/backends/claude-code-core.d.ts +71 -31
- package/dist/core/backends/claude-code-core.js +282 -54
- package/dist/core/backends/cli-quota-guards.d.ts +29 -1
- package/dist/core/backends/cli-quota-guards.js +40 -5
- package/dist/core/backends/codex-core.d.ts +6 -0
- package/dist/core/backends/codex-core.js +22 -6
- package/dist/core/backends/failure-spend.d.ts +58 -0
- package/dist/core/backends/failure-spend.js +137 -0
- package/dist/core/backends/gemini-cli-core.d.ts +6 -0
- package/dist/core/backends/gemini-cli-core.js +38 -6
- package/dist/core/backends/model-registry.d.ts +1 -1
- package/dist/core/backends/model-registry.js +4 -4
- package/dist/core/backends/opencode-core.d.ts +1 -1
- package/dist/core/backends/opencode-core.js +5 -5
- package/dist/core/backends/plan-presets.js +47 -18
- package/dist/core/bang-commands/commands-cost.js +3 -1
- package/dist/core/bang-commands/commands-report.js +4 -3
- package/dist/core/bang-commands/commands-research.js +4 -1
- package/dist/core/bang-commands/commands-revert-tuning.d.ts +18 -0
- package/dist/core/bang-commands/commands-revert-tuning.js +63 -0
- package/dist/core/bang-commands/commands-stop-start.js +3 -3
- package/dist/core/bang-commands/commands-task-control.d.ts +19 -0
- package/dist/core/bang-commands/commands-task-control.js +147 -0
- package/dist/core/bang-commands/commands-wiki.js +5 -5
- package/dist/core/bang-commands/index.d.ts +2 -0
- package/dist/core/bang-commands/index.js +12 -0
- package/dist/core/bang-commands/registry.d.ts +12 -0
- package/dist/core/browser-history/research-cluster-fanout.d.ts +28 -14
- package/dist/core/browser-history/research-cluster-fanout.js +39 -16
- package/dist/core/channel-timeline.d.ts +5 -1
- package/dist/core/channel-timeline.js +13 -0
- package/dist/core/context/index-reconciler.js +5 -2
- package/dist/core/context/policy-index-reconciler.d.ts +6 -4
- package/dist/core/context/policy-index-runner.js +25 -6
- package/dist/core/context-builder-calendar.js +10 -2
- package/dist/core/context-builder-conversation.d.ts +8 -1
- package/dist/core/context-builder-conversation.js +41 -7
- package/dist/core/context-builder-yesterday.js +4 -3
- package/dist/core/context-builder.d.ts +7 -2
- package/dist/core/context-builder.js +193 -5
- package/dist/core/context-file-serializer.d.ts +1 -1
- package/dist/core/context-file-serializer.js +1 -1
- package/dist/core/context-health.js +2 -2
- package/dist/core/context-paths.d.ts +11 -1
- package/dist/core/context-paths.js +17 -1
- package/dist/core/context-validation/prepare-write.js +1 -1
- package/dist/core/context-validation/routine-rulebook.d.ts +1 -1
- package/dist/core/context-vault-aliases.d.ts +0 -13
- package/dist/core/context-vault-aliases.js +37 -0
- package/dist/core/custom-routines.d.ts +99 -0
- package/dist/core/custom-routines.js +187 -0
- package/dist/core/daemon-api-cli.js +50 -1
- package/dist/core/day-boundary.d.ts +46 -0
- package/dist/core/day-boundary.js +40 -0
- package/dist/core/dispatcher-activity-scan.d.ts +221 -0
- package/dist/core/dispatcher-activity-scan.js +775 -0
- package/dist/core/dispatcher-error-handling.d.ts +6 -11
- package/dist/core/dispatcher-error-handling.js +38 -62
- package/dist/core/dispatcher-hourly-check.js +6 -1
- package/dist/core/dispatcher-message-handler.d.ts +10 -0
- package/dist/core/dispatcher-message-handler.js +24 -0
- package/dist/core/dispatcher-morning-routine.d.ts +6 -6
- package/dist/core/dispatcher-morning-routine.js +13 -13
- package/dist/core/dispatcher-result-processor.d.ts +33 -0
- package/dist/core/dispatcher-result-processor.js +167 -11
- package/dist/core/dispatcher-scheduled-background-task.d.ts +42 -0
- package/dist/core/dispatcher-scheduled-background-task.js +89 -0
- package/dist/core/dispatcher-scheduled-tasks.d.ts +104 -1
- package/dist/core/dispatcher-scheduled-tasks.js +480 -8
- package/dist/core/dispatcher-task-delivery.d.ts +105 -0
- package/dist/core/dispatcher-task-delivery.js +555 -0
- package/dist/core/dispatcher-types.d.ts +48 -9
- package/dist/core/dispatcher-types.js +3 -3
- package/dist/core/dispatcher.d.ts +112 -31
- package/dist/core/dispatcher.js +297 -60
- package/dist/core/dm-freshness-metrics.d.ts +1 -1
- package/dist/core/drift-effects.js +2 -2
- package/dist/core/feedback/consolidation-prep.d.ts +94 -0
- package/dist/core/feedback/consolidation-prep.js +254 -0
- package/dist/core/feedback/eviction-scorer.d.ts +81 -0
- package/dist/core/feedback/eviction-scorer.js +136 -0
- package/dist/core/feedback/lesson-format.d.ts +79 -0
- package/dist/core/feedback/lesson-format.js +199 -0
- package/dist/core/feedback/lesson-injection.d.ts +98 -0
- package/dist/core/feedback/lesson-injection.js +174 -0
- package/dist/core/feedback/lesson-merge.d.ts +51 -0
- package/dist/core/feedback/lesson-merge.js +88 -0
- package/dist/core/feedback/lesson-store-overview.d.ts +46 -0
- package/dist/core/feedback/lesson-store-overview.js +42 -0
- package/dist/core/feedback/promotion-gate.d.ts +69 -0
- package/dist/core/feedback/promotion-gate.js +117 -0
- package/dist/core/feedback/regeneralization-prep.d.ts +87 -0
- package/dist/core/feedback/regeneralization-prep.js +152 -0
- package/dist/core/feedback/scope-parser.d.ts +86 -0
- package/dist/core/feedback/scope-parser.js +141 -0
- package/dist/core/feedback/self-performance-prep.d.ts +186 -0
- package/dist/core/feedback/self-performance-prep.js +541 -0
- package/dist/core/feedback/tuning-actuator.d.ts +198 -0
- package/dist/core/feedback/tuning-actuator.js +432 -0
- package/dist/core/feedback/tuning-recommender.d.ts +247 -0
- package/dist/core/feedback/tuning-recommender.js +580 -0
- package/dist/core/feedback/tuning-revert-monitor.d.ts +90 -0
- package/dist/core/feedback/tuning-revert-monitor.js +213 -0
- package/dist/core/health-monitor.d.ts +6 -0
- package/dist/core/health-monitor.js +1 -1
- package/dist/core/injection-policy.d.ts +83 -1
- package/dist/core/injection-policy.js +61 -3
- package/dist/core/integration-main-backend.js +4 -0
- package/dist/core/management-md.d.ts +2 -2
- package/dist/core/management-md.js +51 -13
- package/dist/core/morning/orchestrator.d.ts +2 -2
- package/dist/core/morning/orchestrator.js +2 -2
- package/dist/core/notification-gate.d.ts +64 -0
- package/dist/core/notification-gate.js +51 -0
- package/dist/core/notification-rate-limit.d.ts +40 -0
- package/dist/core/notification-rate-limit.js +50 -0
- package/dist/core/policy-files.d.ts +1 -1
- package/dist/core/policy-files.js +2 -2
- package/dist/core/pre-pass-freshness.d.ts +4 -4
- package/dist/core/retention.d.ts +5 -0
- package/dist/core/retention.js +20 -4
- package/dist/core/review-context.d.ts +1 -1
- package/dist/core/review-context.js +10 -5
- package/dist/core/roadmap-write-lock.d.ts +2 -1
- package/dist/core/roadmap-write-lock.js +15 -10
- package/dist/core/routine-acquisition-plan.d.ts +47 -1
- package/dist/core/routine-acquisition-plan.js +78 -20
- package/dist/core/routine-fetch-window-retry.js +7 -4
- package/dist/core/routine-fetch-window-runner.d.ts +39 -3
- package/dist/core/routine-fetch-window-runner.js +264 -13
- package/dist/core/routine-windows.d.ts +2 -2
- package/dist/core/routine-windows.js +8 -5
- package/dist/core/scheduler.d.ts +175 -16
- package/dist/core/scheduler.js +559 -102
- package/dist/core/signal-detector.d.ts +51 -1
- package/dist/core/signal-detector.js +321 -24
- package/dist/core/skills-compiler-denied-tools.js +2 -2
- package/dist/core/skills-compiler-skill-index.d.ts +2 -2
- package/dist/core/skills-compiler-skill-index.js +2 -2
- package/dist/core/skills-compiler-variants.d.ts +1 -1
- package/dist/core/skills-compiler-variants.js +8 -0
- package/dist/core/skills-compiler.d.ts +29 -26
- package/dist/core/skills-compiler.js +117 -81
- package/dist/core/skills-manifest.d.ts +37 -0
- package/dist/core/skills-manifest.js +73 -2
- package/dist/core/sleep-inhibitor.d.ts +79 -0
- package/dist/core/sleep-inhibitor.js +132 -0
- package/dist/core/slim-system-prompt-loader.d.ts +77 -0
- package/dist/core/slim-system-prompt-loader.js +141 -0
- package/dist/core/spawn-gates.d.ts +126 -0
- package/dist/core/spawn-gates.js +180 -0
- package/dist/core/today-direct-writer.d.ts +60 -14
- package/dist/core/today-direct-writer.js +90 -13
- package/dist/core/today-write-lock.d.ts +4 -2
- package/dist/core/today-write-lock.js +30 -20
- package/dist/core/wake-detector.d.ts +55 -0
- package/dist/core/wake-detector.js +80 -0
- package/dist/core/wiki/compile-lock.d.ts +1 -1
- package/dist/core/wiki/compile-lock.js +1 -1
- package/dist/core/wiki/wiki-fts.js +13 -6
- package/dist/core/workdir.js +15 -6
- package/dist/db/activity-scan-signals.d.ts +77 -0
- package/dist/db/activity-scan-signals.js +378 -0
- package/dist/db/agents-store.d.ts +28 -0
- package/dist/db/agents-store.js +62 -0
- package/dist/db/background-task-clarifications-store.d.ts +81 -0
- package/dist/db/background-task-clarifications-store.js +152 -0
- package/dist/db/background-task-store.d.ts +207 -0
- package/dist/db/background-task-store.js +380 -0
- package/dist/db/browser-history-store.d.ts +39 -6
- package/dist/db/browser-history-store.js +51 -7
- package/dist/db/browser-task-clarifications-store.d.ts +12 -0
- package/dist/db/browser-task-clarifications-store.js +35 -5
- package/dist/db/browser-task-store.d.ts +3 -0
- package/dist/db/browser-task-store.js +29 -4
- package/dist/db/deferred-dm.d.ts +86 -0
- package/dist/db/deferred-dm.js +199 -0
- package/dist/db/feedback-signals-store.d.ts +77 -0
- package/dist/db/feedback-signals-store.js +144 -0
- package/dist/db/migrations.js +380 -0
- package/dist/db/observations.d.ts +2 -2
- package/dist/db/observations.js +3 -3
- package/dist/db/schema.js +260 -22
- package/dist/db/voice-transcripts-store.d.ts +1 -1
- package/dist/index.js +86 -29
- package/dist/messaging/browser-task-mcp-notifier.d.ts +12 -70
- package/dist/messaging/browser-task-mcp-notifier.js +30 -151
- package/dist/messaging/browser-task-screenshot-attachment.d.ts +15 -0
- package/dist/messaging/browser-task-screenshot-attachment.js +63 -0
- package/dist/observers/delegated-sync-worker.d.ts +6 -6
- package/dist/observers/delegated-sync-worker.js +10 -10
- package/dist/observers/git-delegated-cron.d.ts +1 -1
- package/dist/observers/git-delegated-cron.js +2 -2
- package/dist/observers/github-poller-classifier.d.ts +3 -3
- package/dist/observers/github-poller-classifier.js +3 -3
- package/dist/observers/imminent-event-scheduler.d.ts +1 -1
- package/dist/observers/imminent-event-scheduler.js +1 -1
- package/dist/observers/mail-poller.d.ts +1 -0
- package/dist/observers/mail-poller.js +42 -3
- package/dist/observers/observation-summarizer/summarizer-client.d.ts +2 -2
- package/dist/observers/observation-summarizer/summarizer-client.js +2 -2
- package/dist/observers/observation-summarizer/worker.d.ts +2 -2
- package/dist/observers/observation-summarizer/worker.js +4 -4
- package/dist/observers/obsidian-watcher.d.ts +1 -1
- package/dist/observers/obsidian-watcher.js +1 -1
- package/dist/safety/agent-write-tracker.d.ts +4 -4
- package/dist/safety/agent-write-tracker.js +4 -4
- package/dist/safety/always-disallowed.d.ts +1 -1
- package/dist/safety/always-disallowed.js +39 -0
- package/dist/safety/audit.d.ts +43 -5
- package/dist/safety/audit.js +86 -18
- package/dist/safety/risk-classifier.d.ts +6 -0
- package/dist/safety/risk-classifier.js +97 -18
- package/dist/scheduler/activity-scan-gate.d.ts +86 -0
- package/dist/scheduler/activity-scan-gate.js +132 -0
- package/dist/services/background-task/background-task-budget.d.ts +80 -0
- package/dist/services/background-task/background-task-budget.js +91 -0
- package/dist/services/background-task/background-task-driver.d.ts +105 -0
- package/dist/services/background-task/background-task-driver.js +416 -0
- package/dist/services/background-task/background-task-runner.d.ts +96 -0
- package/dist/services/background-task/background-task-runner.js +673 -0
- package/dist/services/background-task/background-task-tools.d.ts +84 -0
- package/dist/services/background-task/background-task-tools.js +247 -0
- package/dist/services/background-task/background-task-transition-events.d.ts +43 -0
- package/dist/services/background-task/background-task-transition-events.js +54 -0
- package/dist/services/browser-history/automation/egress-denylist.d.ts +1 -1
- package/dist/services/browser-history/automation/egress-denylist.js +34 -8
- package/dist/services/browser-history/lifecycle/platform.js +44 -2
- package/dist/services/browser-history/managed-chromium/sandbox-launcher.js +0 -1
- package/dist/services/browser-task/browser-task-runner.js +53 -8
- package/dist/services/mcp/probe.js +30 -8
- package/dist/services/observations-batch.d.ts +1 -1
- package/dist/services/observations-batch.js +2 -2
- package/dist/settings/runtime-settings.d.ts +45 -12
- package/dist/settings/runtime-settings.js +215 -40
- package/dist/settings/settings-store.js +11 -3
- package/package.json +4 -4
|
@@ -44,11 +44,12 @@
|
|
|
44
44
|
* does not own the manager's lifecycle.
|
|
45
45
|
*/
|
|
46
46
|
import { EventPriority, createEvent, formatSqliteDatetime, getAgentDayDateStr, isBackendId, isKnowledgeImportEvent, isRoutineEvent, resolveProcessKey, } from "@aitne/shared";
|
|
47
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
47
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
48
48
|
import { join } from "node:path";
|
|
49
49
|
import { randomUUID } from "node:crypto";
|
|
50
|
-
import { CONTEXT_RELATIVE_PATHS } from "./context-paths.js";
|
|
50
|
+
import { CONTEXT_RELATIVE_PATHS, agentLessonsPath } from "./context-paths.js";
|
|
51
51
|
import { getContextDir } from "../config.js";
|
|
52
|
+
import { ensureTodaySkeleton } from "./today-direct-writer.js";
|
|
52
53
|
import { cleanupSessionWorkdir, ensureBackendMaterialized, } from "./workdir.js";
|
|
53
54
|
import { readIntegrations, readIntegrationState } from "../db/integrations-store.js";
|
|
54
55
|
import { refreshInterestsReflection } from "../services/browser-history/refresh-interests-reflection.js";
|
|
@@ -60,6 +61,13 @@ import { getRepository, getRepositoryByLocalPath, recordManagementInitDone, reco
|
|
|
60
61
|
import { runRepositoryManagementInit, runRepositoryManagementScan, } from "./repository-management-docs.js";
|
|
61
62
|
import { routineWindowKeyFromEvent } from "./routine-fetch-window-runner.js";
|
|
62
63
|
import { routineHasWindows } from "./routine-windows.js";
|
|
64
|
+
import { buildFeedbackWorksheet, gatherFeedbackWorksheetScopes, lessonCapsForScope, GLOBAL_LESSON_ENTRY_CAP, PER_AGENT_LESSON_ENTRY_CAP, } from "./feedback/consolidation-prep.js";
|
|
65
|
+
import { buildSelfPerformanceBlock, gatherSelfPerformanceData, summarizeLessonStoreUtilization, } from "./feedback/self-performance-prep.js";
|
|
66
|
+
import { TUNING_PENDING_CYCLE_STATE_KEY, buildTuningRecommendations, createPendingTuningCycle, gatherFailingRecurringSchedules, renderTuningRecommendationsBlock, } from "./feedback/tuning-recommender.js";
|
|
67
|
+
import { readRuntimeState, writeRuntimeState } from "../db/runtime-state.js";
|
|
68
|
+
import { buildRegeneralizationWorksheet, } from "./feedback/regeneralization-prep.js";
|
|
69
|
+
import { isSafeAgentSlug, scopeStoreFile, } from "./feedback/scope-parser.js";
|
|
70
|
+
import { feedbackRetentionCutoff, sweepConsumedFeedbackSignals, } from "../db/feedback-signals-store.js";
|
|
63
71
|
import { morningRoutineRanToday } from "../bootstrap/schedule-helpers.js";
|
|
64
72
|
import { resolveAgentId } from "./agents/agent-id-resolver.js";
|
|
65
73
|
import { loadEffectiveDefinition } from "./agents/effective-definition.js";
|
|
@@ -198,6 +206,33 @@ export const REFRESH_ARCHITECTURE_ALLOWED_TOOLS = [
|
|
|
198
206
|
"Bash(curl http://localhost:8321/api/repositories/*/architecture-section*)",
|
|
199
207
|
"Bash(jq *)",
|
|
200
208
|
];
|
|
209
|
+
/**
|
|
210
|
+
* BACKGROUND_TASK_RUNNER_DESIGN.md §4.5 / §4.5-bis / Phase 4 — the active
|
|
211
|
+
* delivery turn is a NO-TOOL DM turn. Its sole job is to weave the
|
|
212
|
+
* already-injected artifact (`taskContext.task_delivery.report` carries the
|
|
213
|
+
* full verbatim result) into the live conversation; the agent's reply text
|
|
214
|
+
* IS the DM (recorded by the result processor) and any deliverable files
|
|
215
|
+
* are attached by the daemon, not by the agent. So the turn needs no tools
|
|
216
|
+
* at all — and an EMPTY override structurally prevents it from taking
|
|
217
|
+
* "further action" (spawning another task, writing memory, sending mail)
|
|
218
|
+
* during what should be a pure phrasing turn. Follow-up turns ("what did it
|
|
219
|
+
* find?") are ordinary DM turns and keep the full envelope, including the
|
|
220
|
+
* `GET /api/background-task/:id` read affordance.
|
|
221
|
+
*/
|
|
222
|
+
export const TASK_DELIVERY_TURN_ALLOWED_TOOLS = [];
|
|
223
|
+
/**
|
|
224
|
+
* True when `taskCtx` is a synthetic `scheduled.dm` event minted by the
|
|
225
|
+
* task-delivery handler (`createScheduledDmDeliveryEvent`) — the only place
|
|
226
|
+
* that sets the `task_delivery` block. Used to pin the no-tool clamp above.
|
|
227
|
+
* Keying off this structural marker (not `event.source`) is fail-safe: the
|
|
228
|
+
* clamp only ever NARROWS the envelope, so a false positive degrades a turn
|
|
229
|
+
* to no-tool rather than widening anything.
|
|
230
|
+
*/
|
|
231
|
+
export function isTaskDeliveryTurn(taskCtx) {
|
|
232
|
+
return (!!taskCtx
|
|
233
|
+
&& typeof taskCtx === "object"
|
|
234
|
+
&& "task_delivery" in taskCtx);
|
|
235
|
+
}
|
|
201
236
|
/**
|
|
202
237
|
* Backends that honor the per-execute `allowedToolsOverride` clamp end-to-
|
|
203
238
|
* end. Claude consumes the list verbatim through the SDK's `dontAsk` +
|
|
@@ -209,6 +244,14 @@ export const REFRESH_ARCHITECTURE_ALLOWED_TOOLS = [
|
|
|
209
244
|
* envelope; the operator sees an `agent_actions` row of action_type
|
|
210
245
|
* `scheduled_task_clamp_unsupported` and a clear log line.
|
|
211
246
|
*
|
|
247
|
+
* Exception — the Phase-4 task-delivery turn (`isTaskDeliveryTurn`)
|
|
248
|
+
* consumes this same set but DEGRADES rather than refuses: when the
|
|
249
|
+
* resolved backend can't enforce `[]` it still runs the delivery turn
|
|
250
|
+
* with the default envelope (logged), because failing to deliver the
|
|
251
|
+
* owner's result is worse than running the phrasing turn untooled. The
|
|
252
|
+
* clamp is keyed off `binding.main.backendId`, so — as with the refuse
|
|
253
|
+
* path — a runtime fallback to a non-claude backend is not re-evaluated.
|
|
254
|
+
*
|
|
212
255
|
* Add a backend here only after verifying its core threads
|
|
213
256
|
* `allowedToolsOverride` through to its concrete deny enforcement layer
|
|
214
257
|
* — NOT just into the CLI flag set.
|
|
@@ -227,6 +270,7 @@ export class ScheduledTaskRunner {
|
|
|
227
270
|
morningRoutine;
|
|
228
271
|
fetchWindowRunner;
|
|
229
272
|
roadmapWriteLock;
|
|
273
|
+
todayWriteLock;
|
|
230
274
|
writeTracker;
|
|
231
275
|
getConfiguredServices;
|
|
232
276
|
getActiveMailAccounts;
|
|
@@ -243,6 +287,7 @@ export class ScheduledTaskRunner {
|
|
|
243
287
|
this.morningRoutine = deps.morningRoutine;
|
|
244
288
|
this.fetchWindowRunner = deps.fetchWindowRunner;
|
|
245
289
|
this.roadmapWriteLock = deps.roadmapWriteLock;
|
|
290
|
+
this.todayWriteLock = deps.todayWriteLock;
|
|
246
291
|
this.writeTracker = deps.writeTracker;
|
|
247
292
|
this.getConfiguredServices = deps.getConfiguredServices;
|
|
248
293
|
this.getActiveMailAccounts = deps.getActiveMailAccounts;
|
|
@@ -454,6 +499,23 @@ export class ScheduledTaskRunner {
|
|
|
454
499
|
this.markScheduledTaskCompleted(event);
|
|
455
500
|
return;
|
|
456
501
|
}
|
|
502
|
+
// BACKGROUND_TASK_RUNNER_DESIGN.md §4.5 / Phase 4 — pin the no-tool
|
|
503
|
+
// clamp on the active delivery turn. Mutually exclusive with the
|
|
504
|
+
// refresh-architecture clamp (a delivery turn is never that process
|
|
505
|
+
// key). Unlike the safety-critical refresh/curation clamps, this is a
|
|
506
|
+
// HARDENING: when the resolved backend can't enforce a per-execute
|
|
507
|
+
// clamp we DEGRADE (deliver with the default envelope) rather than
|
|
508
|
+
// refuse — failing to deliver the owner's result would be the worse
|
|
509
|
+
// outcome. The degrade is logged per the "no silent drops" posture.
|
|
510
|
+
const taskDeliveryToolClamp = isTaskDeliveryTurn(taskCtx)
|
|
511
|
+
? TOOL_CLAMP_SUPPORTING_BACKENDS.has(binding.main.backendId)
|
|
512
|
+
? TASK_DELIVERY_TURN_ALLOWED_TOOLS
|
|
513
|
+
: undefined
|
|
514
|
+
: undefined;
|
|
515
|
+
if (isTaskDeliveryTurn(taskCtx) && taskDeliveryToolClamp === undefined) {
|
|
516
|
+
logger.info({ backendId: binding.main.backendId, correlationId: event.correlationId }, "task.delivery active turn: resolved backend does not honor the per-execute tool clamp; delivering with the default DM envelope (degraded, not refused)");
|
|
517
|
+
}
|
|
518
|
+
const effectiveAllowedToolsOverride = refreshArchitectureOverride ?? taskDeliveryToolClamp;
|
|
457
519
|
// AGENT_DEFINITIONS_DESIGN.md §4.2 — fold the firing Agent's
|
|
458
520
|
// `tools.skills` onto the process-key skill bundle. `undefined` for every
|
|
459
521
|
// non-Agent firing (managed task, git project doc, automation trigger) and
|
|
@@ -467,8 +529,8 @@ export class ScheduledTaskRunner {
|
|
|
467
529
|
requestedTier,
|
|
468
530
|
preResolvedBinding: binding,
|
|
469
531
|
reassemblePrompt,
|
|
470
|
-
...(
|
|
471
|
-
? { allowedToolsOverride:
|
|
532
|
+
...(effectiveAllowedToolsOverride
|
|
533
|
+
? { allowedToolsOverride: effectiveAllowedToolsOverride }
|
|
472
534
|
: {}),
|
|
473
535
|
...(agentSkillOverride
|
|
474
536
|
? {
|
|
@@ -801,7 +863,7 @@ export class ScheduledTaskRunner {
|
|
|
801
863
|
// row insert (today.md is fresh, audit row is missing);
|
|
802
864
|
// - the user manually edits today.md to the current date (CLAUDE.md
|
|
803
865
|
// calls this out as a documented edit path).
|
|
804
|
-
// In both, the
|
|
866
|
+
// In both, the activity_scan pre-routine gate (`morningRoutineRanToday`)
|
|
805
867
|
// would keep refusing to run because the audit row is absent, the
|
|
806
868
|
// pre-routine gate kept enqueuing wake rows, and each wake row was
|
|
807
869
|
// fast-path completed here — never producing the audit row that would
|
|
@@ -837,6 +899,15 @@ export class ScheduledTaskRunner {
|
|
|
837
899
|
// carries the previous attempt so executeMorningRoutine → scheduleMorningRetry
|
|
838
900
|
// can increment properly. correlationId tracks back to the original
|
|
839
901
|
// cron morning_routine for log correlation.
|
|
902
|
+
//
|
|
903
|
+
// `agentId` is carried over from the wake event (stamped there by
|
|
904
|
+
// `Dispatcher.beginAgentExecution` via `task_context.agent_id` /
|
|
905
|
+
// `task_context.routine`): the cron path's RoutineEvent gets the stamp
|
|
906
|
+
// directly, so dropping it here would make run-now / retry firings
|
|
907
|
+
// ignore the morning-routine Agent's overrides + lesson injection while
|
|
908
|
+
// cron firings honour them — the same manual-vs-cron divergence class
|
|
909
|
+
// AGENT_DEFINITIONS_KNOWN_LIMITATIONS §1 fixed for user Agents.
|
|
910
|
+
const wakeAgentId = event.data?.agentId;
|
|
840
911
|
const synthEvent = {
|
|
841
912
|
...createEvent({
|
|
842
913
|
type: "routine.morning_routine",
|
|
@@ -848,12 +919,15 @@ export class ScheduledTaskRunner {
|
|
|
848
919
|
priority: retryCount > 0 ? EventPriority.NORMAL : EventPriority.HIGH,
|
|
849
920
|
correlationId: taskCtx.originalCorrelationId ?? event.correlationId,
|
|
850
921
|
data: {
|
|
922
|
+
...(typeof wakeAgentId === "string" && wakeAgentId.length > 0
|
|
923
|
+
? { agentId: wakeAgentId }
|
|
924
|
+
: {}),
|
|
851
925
|
...(retryCount > 0 ? { retryCount, isRetry: true } : {}),
|
|
852
926
|
...(Array.isArray(taskCtx.postCatchupRoutines)
|
|
853
927
|
? { postCatchupRoutines: taskCtx.postCatchupRoutines }
|
|
854
928
|
: {}),
|
|
855
|
-
...(taskCtx.
|
|
856
|
-
? {
|
|
929
|
+
...(taskCtx.postCatchupActivityScan === true
|
|
930
|
+
? { postCatchupActivityScan: true }
|
|
857
931
|
: {}),
|
|
858
932
|
...(typeof taskCtx.source === "string"
|
|
859
933
|
? { queuedSource: taskCtx.source }
|
|
@@ -1061,7 +1135,7 @@ export class ScheduledTaskRunner {
|
|
|
1061
1135
|
try {
|
|
1062
1136
|
// docs/design/appendices/routine-data-acquisition.md Phase 4 / D4 — pre-pass for
|
|
1063
1137
|
// routine events whose ProcessKey appears in `ROUTINE_WINDOWS`
|
|
1064
|
-
// (today_refresh, evening_review, weekly_review). The
|
|
1138
|
+
// (today_refresh, evening_review, weekly_review). The activity_scan
|
|
1065
1139
|
// and morning_routine dispatch paths attach their own
|
|
1066
1140
|
// `fetchReportBlock` upstream (D2 / D3); we honour an existing
|
|
1067
1141
|
// attachment to avoid double-spawning the fetcher. `monthly_review`
|
|
@@ -1085,6 +1159,81 @@ export class ScheduledTaskRunner {
|
|
|
1085
1159
|
};
|
|
1086
1160
|
}
|
|
1087
1161
|
}
|
|
1162
|
+
// Deterministic pre-step for `routine.today_refresh`. rotateDayFiles()
|
|
1163
|
+
// intentionally leaves today.md absent at the day boundary for the
|
|
1164
|
+
// morning routine to recreate; when that routine has not yet run (or
|
|
1165
|
+
// failed — e.g. a quota/budget death with no fallback), today.md is
|
|
1166
|
+
// missing and this section-only refresh would 404 on its PATCH and
|
|
1167
|
+
// fall back to budget-burning full-file PUTs against the strict
|
|
1168
|
+
// validateTodayContent schema (the "Refresh Today does nothing"
|
|
1169
|
+
// symptom). Guarantee the canonical skeleton exists (lock-aware,
|
|
1170
|
+
// absent-only) so the refresh session has a valid PATCH target; full
|
|
1171
|
+
// creation/repair stays the morning routine's job. See
|
|
1172
|
+
// ensureTodaySkeleton. Skipped when no today-write-lock was wired.
|
|
1173
|
+
if (this.todayWriteLock
|
|
1174
|
+
&& isRoutineEvent(effectiveEvent)
|
|
1175
|
+
&& effectiveEvent.routine === "today_refresh") {
|
|
1176
|
+
// Failure-isolated like the sibling pre-steps below: this seed is a
|
|
1177
|
+
// best-effort convenience so the refresh has a valid PATCH target.
|
|
1178
|
+
// A throw here (e.g. getContextDir / serializer-lock internals) must
|
|
1179
|
+
// NOT abort the whole today_refresh — that would reproduce the exact
|
|
1180
|
+
// "Refresh Today does nothing" symptom the skeleton was added to
|
|
1181
|
+
// prevent. The morning routine remains responsible for real repair.
|
|
1182
|
+
try {
|
|
1183
|
+
await ensureTodaySkeleton({
|
|
1184
|
+
contextDir: getContextDir(this.config, this.db),
|
|
1185
|
+
todayWriteLock: this.todayWriteLock,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
catch (err) {
|
|
1189
|
+
logger.warn({ err }, "Failed to seed today.md skeleton for today_refresh");
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
// FEEDBACK_LEARNING_LOOP_DESIGN.md §4 — deterministic consolidation
|
|
1193
|
+
// pre-step for `routine.evening_review`. Runs synchronously BEFORE
|
|
1194
|
+
// `contextBuilder.build` so the `<feedback_worksheet>` (unconsumed
|
|
1195
|
+
// signals grouped by scope + per-candidate promotion verdicts +
|
|
1196
|
+
// eviction ranking + consume ids) lands in the review session's
|
|
1197
|
+
// context. Failure-isolated inside `prepareFeedbackWorksheet`: a throw
|
|
1198
|
+
// there is swallowed and the review still ships. Skipped when feedback
|
|
1199
|
+
// learning is off or no signals pend (no empty block in the prompt).
|
|
1200
|
+
if (isRoutineEvent(effectiveEvent)
|
|
1201
|
+
&& effectiveEvent.routine === "evening_review") {
|
|
1202
|
+
const worksheetBlock = this.prepareFeedbackWorksheet();
|
|
1203
|
+
if (worksheetBlock) {
|
|
1204
|
+
effectiveEvent = {
|
|
1205
|
+
...effectiveEvent,
|
|
1206
|
+
data: {
|
|
1207
|
+
...effectiveEvent.data,
|
|
1208
|
+
feedbackWorksheetBlock: worksheetBlock,
|
|
1209
|
+
},
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
// FEEDBACK_LEARNING_LOOP_DESIGN.md §4 "Monthly re-generalization" /
|
|
1214
|
+
// Phase 5 — deterministic pre-step for `routine.monthly_review`. Re-reads
|
|
1215
|
+
// the consolidated lesson stores (global + per-agent) and stamps a
|
|
1216
|
+
// `<feedback_regeneralization>` block so the monthly session collapses
|
|
1217
|
+
// same-theme lessons into higher-level principles. Distinct from the
|
|
1218
|
+
// nightly evening-review pass: it carries no signals and consumes
|
|
1219
|
+
// nothing — it only surfaces existing lessons ranked for collapse.
|
|
1220
|
+
// Failure-isolated inside `prepareRegeneralizationWorksheet`; skipped
|
|
1221
|
+
// when feedback learning is off or no scope has enough lessons. Rides the
|
|
1222
|
+
// same `monthlyReviewEnabled` kill switch as the rest of the monthly
|
|
1223
|
+
// routine (the routine never dispatches while it is off).
|
|
1224
|
+
if (isRoutineEvent(effectiveEvent)
|
|
1225
|
+
&& effectiveEvent.routine === "monthly_review") {
|
|
1226
|
+
const regeneralizationBlock = this.prepareRegeneralizationWorksheet();
|
|
1227
|
+
if (regeneralizationBlock) {
|
|
1228
|
+
effectiveEvent = {
|
|
1229
|
+
...effectiveEvent,
|
|
1230
|
+
data: {
|
|
1231
|
+
...effectiveEvent.data,
|
|
1232
|
+
regeneralizationBlock,
|
|
1233
|
+
},
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1088
1237
|
// WEEKLY_INTERESTS_REFLECTION_PLAN.md §10.4 — deterministic
|
|
1089
1238
|
// pre-hook for `routine.weekly_review`. Runs synchronously
|
|
1090
1239
|
// BEFORE `contextBuilder.build` so the freshly-refreshed
|
|
@@ -1097,6 +1246,36 @@ export class ScheduledTaskRunner {
|
|
|
1097
1246
|
if (isRoutineEvent(effectiveEvent)
|
|
1098
1247
|
&& effectiveEvent.routine === "weekly_review") {
|
|
1099
1248
|
this.runWeeklyInterestsReflectionPreHook(effectiveEvent);
|
|
1249
|
+
// SELF_TUNING_REVIEW_CYCLE_DESIGN.md §3.1 + §3.2 / Phases 1–2 —
|
|
1250
|
+
// deterministic Measure + Recommend pre-steps for
|
|
1251
|
+
// `routine.weekly_review`. Run synchronously BEFORE
|
|
1252
|
+
// `contextBuilder.build` so the `<self_performance>` block (7-day
|
|
1253
|
+
// per-routine run/cost/duration aggregates + fetch_window empty-run
|
|
1254
|
+
// rates + hourly-gate stage distribution + notification reaction
|
|
1255
|
+
// breakdown + self-tuning ledger) and the `<tuning_recommendations>`
|
|
1256
|
+
// block (Phase 2 rule-table output for the Phase 3c verdict step)
|
|
1257
|
+
// land in the weekly session's context. Both share one gather pass;
|
|
1258
|
+
// each is independently failure-isolated inside
|
|
1259
|
+
// `prepareSelfTuningBlocks` — a throw is swallowed and the review
|
|
1260
|
+
// still ships. Either block is omitted entirely when empty (no
|
|
1261
|
+
// empty XML in the prompt).
|
|
1262
|
+
const selfTuning = this.prepareSelfTuningBlocks();
|
|
1263
|
+
if (selfTuning.selfPerformanceBlock || selfTuning.tuningRecommendationsBlock) {
|
|
1264
|
+
effectiveEvent = {
|
|
1265
|
+
...effectiveEvent,
|
|
1266
|
+
data: {
|
|
1267
|
+
...effectiveEvent.data,
|
|
1268
|
+
...(selfTuning.selfPerformanceBlock
|
|
1269
|
+
? { selfPerformanceBlock: selfTuning.selfPerformanceBlock }
|
|
1270
|
+
: {}),
|
|
1271
|
+
...(selfTuning.tuningRecommendationsBlock
|
|
1272
|
+
? {
|
|
1273
|
+
tuningRecommendationsBlock: selfTuning.tuningRecommendationsBlock,
|
|
1274
|
+
}
|
|
1275
|
+
: {}),
|
|
1276
|
+
},
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1100
1279
|
}
|
|
1101
1280
|
const context = await this.contextBuilder.build(effectiveEvent);
|
|
1102
1281
|
const processKey = resolveProcessKey(effectiveEvent);
|
|
@@ -1277,6 +1456,299 @@ export class ScheduledTaskRunner {
|
|
|
1277
1456
|
this.appendWeeklyInterestsJournalLine(`interest reflection failed: ${message}`);
|
|
1278
1457
|
}
|
|
1279
1458
|
}
|
|
1459
|
+
/**
|
|
1460
|
+
* FEEDBACK_LEARNING_LOOP_DESIGN.md §4 — the deterministic consolidation
|
|
1461
|
+
* pre-step. Reads unconsumed `feedback_signals` (user + agent + `agent:<slug>`
|
|
1462
|
+
* scope as of Phase 4), reads each lessons store's current contents, and
|
|
1463
|
+
* composes the `<feedback_worksheet>` block via the pure `core/feedback/*`
|
|
1464
|
+
* modules. Returns the block string, or `null` when feedback learning is off,
|
|
1465
|
+
* nothing pends, or anything throws — so the caller simply skips stamping and
|
|
1466
|
+
* the evening review proceeds unchanged.
|
|
1467
|
+
*
|
|
1468
|
+
* The DB read + per-scope file read live here (the FS-/DB-heavy dispatcher
|
|
1469
|
+
* is coverage-excluded); the byte-deterministic worksheet composition lives
|
|
1470
|
+
* in `buildFeedbackWorksheet`, which is 100% unit-tested.
|
|
1471
|
+
*/
|
|
1472
|
+
prepareFeedbackWorksheet() {
|
|
1473
|
+
if (this.config.feedbackLearningEnabled === false)
|
|
1474
|
+
return null;
|
|
1475
|
+
try {
|
|
1476
|
+
// Nightly retention close-out (§4 step 6 / §6): drop signal rows that
|
|
1477
|
+
// were consumed past the retention horizon so the raw table stays
|
|
1478
|
+
// bounded. Only touches already-consumed rows — an unconsolidated
|
|
1479
|
+
// signal is never lost to retention. Runs once per Evening Review.
|
|
1480
|
+
// Guarded against a missing/NaN retention knob so a config gap degrades
|
|
1481
|
+
// to "skip the sweep", not "throw and abandon the whole consolidation".
|
|
1482
|
+
// The guard + cutoff math live in the pure, 100%-covered
|
|
1483
|
+
// `feedbackRetentionCutoff` helper (returns null → skip).
|
|
1484
|
+
const retentionCutoff = feedbackRetentionCutoff(this.config.feedbackSignalRetentionDays, Date.now());
|
|
1485
|
+
if (retentionCutoff) {
|
|
1486
|
+
sweepConsumedFeedbackSignals(this.db, retentionCutoff);
|
|
1487
|
+
}
|
|
1488
|
+
const groups = gatherFeedbackWorksheetScopes(this.db, {
|
|
1489
|
+
// Phase 4 adds `agent_slug` — per-agent (`agent:<slug>`) signals captured
|
|
1490
|
+
// by the explicit route + behavioral sink now fold into
|
|
1491
|
+
// `policies/agents/<slug>/lessons.md`. Per-scope-type fetch (not a global
|
|
1492
|
+
// LIMIT) keeps an `agent_slug` backlog from starving user/agent.
|
|
1493
|
+
scopeTypes: ["user", "agent", "agent_slug"],
|
|
1494
|
+
});
|
|
1495
|
+
if (groups.length === 0)
|
|
1496
|
+
return null;
|
|
1497
|
+
const contextDir = getContextDir(this.config, this.db);
|
|
1498
|
+
const byteCaps = {
|
|
1499
|
+
global: this.config.feedbackLessonMaxBytesGlobal,
|
|
1500
|
+
perAgent: this.config.feedbackLessonMaxBytesPerAgent,
|
|
1501
|
+
};
|
|
1502
|
+
const scopes = [];
|
|
1503
|
+
for (const group of groups) {
|
|
1504
|
+
const caps = lessonCapsForScope(group.scope, byteCaps);
|
|
1505
|
+
let existingFileMd = null;
|
|
1506
|
+
if (caps) {
|
|
1507
|
+
const rel = scopeStoreFile(group.scope);
|
|
1508
|
+
if (rel) {
|
|
1509
|
+
const full = join(contextDir, rel);
|
|
1510
|
+
// Per-scope read isolation (FEEDBACK_LEARNING_LOOP_DESIGN.md §11
|
|
1511
|
+
// v1.9 nuance #3, now closed). A *missing* file is the normal
|
|
1512
|
+
// first-write case (existingFileMd stays null → the LLM PUTs a
|
|
1513
|
+
// fresh store); but a *present-but-unreadable* file (EACCES, a
|
|
1514
|
+
// mid-write truncation) must NOT forfeit the whole night's
|
|
1515
|
+
// consolidation. Skip just this scope: its signals stay unconsumed
|
|
1516
|
+
// and retry next Evening Review, the file is left untouched, and the
|
|
1517
|
+
// user/agent scopes that read cleanly still consolidate. Crucially
|
|
1518
|
+
// we do NOT fall back to existingFileMd=null on a read error — that
|
|
1519
|
+
// would make the LLM PUT a fresh file and destroy the existing
|
|
1520
|
+
// lessons.
|
|
1521
|
+
try {
|
|
1522
|
+
existingFileMd = existsSync(full)
|
|
1523
|
+
? readFileSync(full, "utf-8")
|
|
1524
|
+
: null;
|
|
1525
|
+
}
|
|
1526
|
+
catch (err) {
|
|
1527
|
+
logger.warn({ err, path: rel }, "Skipping feedback scope — lessons file present but unreadable; its signals will retry next Evening Review");
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
scopes.push({
|
|
1533
|
+
scope: group.scope,
|
|
1534
|
+
signals: group.signals,
|
|
1535
|
+
existingFileMd,
|
|
1536
|
+
caps,
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
if (scopes.length === 0)
|
|
1540
|
+
return null;
|
|
1541
|
+
const result = buildFeedbackWorksheet(scopes, {
|
|
1542
|
+
promotionThreshold: this.config.feedbackPromotionThreshold,
|
|
1543
|
+
nowIso: new Date().toISOString(),
|
|
1544
|
+
staleDays: this.config.feedbackLessonStaleDays,
|
|
1545
|
+
});
|
|
1546
|
+
return result?.block ?? null;
|
|
1547
|
+
}
|
|
1548
|
+
catch (err) {
|
|
1549
|
+
logger.warn({ err }, "Failed to prepare feedback worksheet");
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* SELF_TUNING_REVIEW_CYCLE_DESIGN.md §3.1 + §3.2 / Phases 1–2 — the
|
|
1555
|
+
* deterministic Measure + Recommend pre-steps. Computes the 7-day window +
|
|
1556
|
+
* 7-day-prior baseline SQL aggregates over `agent_actions` /
|
|
1557
|
+
* `notification_log` / the `runtime_state.self_tuning:*` ledger, reads each
|
|
1558
|
+
* lesson store's byte pressure (§3.5), and composes the
|
|
1559
|
+
* `<self_performance>` block via the pure
|
|
1560
|
+
* `core/feedback/self-performance-prep.ts` module. The same gathered data
|
|
1561
|
+
* then feeds the Phase 2 rule table (`core/feedback/tuning-recommender.ts`):
|
|
1562
|
+
* the resulting pending cycle is persisted to
|
|
1563
|
+
* `runtime_state.self_tuning.pending_cycle` — overwriting (and thereby
|
|
1564
|
+
* expiring, §3.4 single-use ids) the previous cycle even when this week
|
|
1565
|
+
* produced zero recommendations — and rendered as the
|
|
1566
|
+
* `<tuning_recommendations>` block for the Phase 3c verdict step.
|
|
1567
|
+
*
|
|
1568
|
+
* Either field is `null` when there is nothing to inject or its step threw —
|
|
1569
|
+
* the caller simply skips stamping and the weekly review proceeds
|
|
1570
|
+
* unchanged. The Recommend step is failure-isolated from the Measure step:
|
|
1571
|
+
* a recommender throw never drops the `<self_performance>` block.
|
|
1572
|
+
*
|
|
1573
|
+
* The DB handle + lesson-file FS reads live here (the dispatcher is
|
|
1574
|
+
* coverage-excluded); the byte-deterministic aggregation, rule table, and
|
|
1575
|
+
* rendering live in the pure modules, which are 100% unit-tested. Unlike
|
|
1576
|
+
* the consolidation/re-generalization pre-steps this is NOT gated on
|
|
1577
|
+
* `feedbackLearningEnabled` — it measures core daemon telemetry, not the
|
|
1578
|
+
* lesson loop; nor on `selfTuningEnabled` — that flag gates Phase 3
|
|
1579
|
+
* *actuation* only, while recommendation generation + verdict recording IS
|
|
1580
|
+
* the Phase 2 shadow period (§7). An unreadable lesson store only drops the
|
|
1581
|
+
* `<lesson_stores>` rows / the R5 input, never the whole block.
|
|
1582
|
+
*/
|
|
1583
|
+
prepareSelfTuningBlocks() {
|
|
1584
|
+
try {
|
|
1585
|
+
const now = new Date();
|
|
1586
|
+
const data = gatherSelfPerformanceData(this.db, { now });
|
|
1587
|
+
const lessonStores = [];
|
|
1588
|
+
const contextDir = getContextDir(this.config, this.db);
|
|
1589
|
+
const readStoreFile = (rel) => {
|
|
1590
|
+
const full = join(contextDir, rel);
|
|
1591
|
+
if (!existsSync(full))
|
|
1592
|
+
return null;
|
|
1593
|
+
try {
|
|
1594
|
+
return readFileSync(full, "utf-8");
|
|
1595
|
+
}
|
|
1596
|
+
catch (err) {
|
|
1597
|
+
logger.warn({ err, path: rel }, "Skipping lesson store in self-performance block — file present but unreadable");
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
const globalMd = readStoreFile(CONTEXT_RELATIVE_PATHS.agentLessons);
|
|
1602
|
+
if (globalMd !== null) {
|
|
1603
|
+
lessonStores.push(summarizeLessonStoreUtilization("agent", globalMd, this.config.feedbackLessonMaxBytesGlobal));
|
|
1604
|
+
}
|
|
1605
|
+
const agentsDir = join(contextDir, "policies", "agents");
|
|
1606
|
+
if (existsSync(agentsDir)) {
|
|
1607
|
+
for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
1608
|
+
if (!entry.isDirectory() || !isSafeAgentSlug(entry.name))
|
|
1609
|
+
continue;
|
|
1610
|
+
const md = readStoreFile(agentLessonsPath(entry.name));
|
|
1611
|
+
if (md === null)
|
|
1612
|
+
continue;
|
|
1613
|
+
lessonStores.push(summarizeLessonStoreUtilization(`agent:${entry.name}`, md, this.config.feedbackLessonMaxBytesPerAgent));
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
const selfPerformanceBlock = buildSelfPerformanceBlock(data, {
|
|
1617
|
+
generatedAt: now.toISOString(),
|
|
1618
|
+
lessonStores,
|
|
1619
|
+
});
|
|
1620
|
+
// Phase 2 Recommend step — isolated so a recommender failure cannot
|
|
1621
|
+
// cost the weekly session its Measure block.
|
|
1622
|
+
let tuningRecommendationsBlock = null;
|
|
1623
|
+
try {
|
|
1624
|
+
const recommendations = buildTuningRecommendations({
|
|
1625
|
+
data,
|
|
1626
|
+
knobs: {
|
|
1627
|
+
activityScanPrePassFreshnessMinutes: this.config.activityScanPrePassFreshnessMinutes,
|
|
1628
|
+
activityScanLowSignalPendingCeiling: this.config.activityScanLowSignalPendingCeiling,
|
|
1629
|
+
feedbackLessonMaxBytesGlobal: this.config.feedbackLessonMaxBytesGlobal,
|
|
1630
|
+
},
|
|
1631
|
+
lessonStores,
|
|
1632
|
+
failingSchedules: gatherFailingRecurringSchedules(this.db),
|
|
1633
|
+
now,
|
|
1634
|
+
});
|
|
1635
|
+
// Read the outgoing blob before overwriting: a SAME-day re-run
|
|
1636
|
+
// (manual `!run` / crash retry) regenerates the same cycle id and
|
|
1637
|
+
// ids, and verdicts already recorded against them must survive —
|
|
1638
|
+
// otherwise the re-run session re-verdicts judged ids and
|
|
1639
|
+
// double-posts rejection self_critique signals. A prior-day blob
|
|
1640
|
+
// is ignored inside createPendingTuningCycle (§3.4 expiry).
|
|
1641
|
+
const previousCycle = readRuntimeState(this.db, TUNING_PENDING_CYCLE_STATE_KEY);
|
|
1642
|
+
const cycle = createPendingTuningCycle(recommendations, now.toISOString(), previousCycle);
|
|
1643
|
+
// Always persist — an empty cycle still expires last week's
|
|
1644
|
+
// single-use verdict ids (§3.4).
|
|
1645
|
+
writeRuntimeState(this.db, TUNING_PENDING_CYCLE_STATE_KEY, cycle);
|
|
1646
|
+
// Phase 3 — the block's `mode` attribute tells the weekly session
|
|
1647
|
+
// whether apply verdicts actuate (`live`) or are recorded only
|
|
1648
|
+
// (`shadow`), so the task-flow never has to guess the flag state.
|
|
1649
|
+
tuningRecommendationsBlock = renderTuningRecommendationsBlock(cycle, {
|
|
1650
|
+
mode: this.config.selfTuningEnabled === true ? "live" : "shadow",
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
catch (err) {
|
|
1654
|
+
logger.warn({ err }, "Failed to prepare tuning recommendations");
|
|
1655
|
+
}
|
|
1656
|
+
return { selfPerformanceBlock, tuningRecommendationsBlock };
|
|
1657
|
+
}
|
|
1658
|
+
catch (err) {
|
|
1659
|
+
logger.warn({ err }, "Failed to prepare self-performance block");
|
|
1660
|
+
return { selfPerformanceBlock: null, tuningRecommendationsBlock: null };
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* FEEDBACK_LEARNING_LOOP_DESIGN.md §4 "Monthly re-generalization" / Phase 5 —
|
|
1665
|
+
* the deterministic monthly pre-step. Enumerates the consolidated lesson
|
|
1666
|
+
* stores on disk (the global `policies/agent-lessons.md` plus every per-agent
|
|
1667
|
+
* `policies/agents/<slug>/lessons.md`), reads their contents, and composes a
|
|
1668
|
+
* `<feedback_regeneralization>` block via the pure
|
|
1669
|
+
* `buildRegeneralizationWorksheet`. Returns the block, or `null` when feedback
|
|
1670
|
+
* learning is off, no store holds enough lessons to collapse, or anything
|
|
1671
|
+
* throws — so the caller simply skips stamping and the monthly review
|
|
1672
|
+
* proceeds unchanged.
|
|
1673
|
+
*
|
|
1674
|
+
* The FS enumeration lives here (the dispatcher is coverage-excluded); the
|
|
1675
|
+
* byte-deterministic composition lives in `buildRegeneralizationWorksheet`,
|
|
1676
|
+
* which is 100% unit-tested.
|
|
1677
|
+
*/
|
|
1678
|
+
prepareRegeneralizationWorksheet() {
|
|
1679
|
+
if (this.config.feedbackLearningEnabled === false)
|
|
1680
|
+
return null;
|
|
1681
|
+
try {
|
|
1682
|
+
const contextDir = getContextDir(this.config, this.db);
|
|
1683
|
+
const scopes = [];
|
|
1684
|
+
// Per-file read isolation (FEEDBACK_LEARNING_LOOP_DESIGN.md §11 v1.9
|
|
1685
|
+
// nuance #3, now closed): one present-but-unreadable lessons file must
|
|
1686
|
+
// skip only that scope, not abandon the whole monthly collapse. A scope
|
|
1687
|
+
// that fails to read is simply not surfaced for re-generalization this
|
|
1688
|
+
// month; the file is left untouched.
|
|
1689
|
+
const readScopeFile = (rel) => {
|
|
1690
|
+
const full = join(contextDir, rel);
|
|
1691
|
+
if (!existsSync(full))
|
|
1692
|
+
return null;
|
|
1693
|
+
try {
|
|
1694
|
+
return readFileSync(full, "utf-8");
|
|
1695
|
+
}
|
|
1696
|
+
catch (err) {
|
|
1697
|
+
logger.warn({ err, path: rel }, "Skipping re-generalization scope — lessons file present but unreadable");
|
|
1698
|
+
return null;
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
// Global `agent`-scope store.
|
|
1702
|
+
const globalRel = CONTEXT_RELATIVE_PATHS.agentLessons;
|
|
1703
|
+
const globalMd = readScopeFile(globalRel);
|
|
1704
|
+
if (globalMd !== null) {
|
|
1705
|
+
scopes.push({
|
|
1706
|
+
scope: { kind: "agent" },
|
|
1707
|
+
storeFile: globalRel,
|
|
1708
|
+
existingFileMd: globalMd,
|
|
1709
|
+
caps: {
|
|
1710
|
+
capBytes: this.config.feedbackLessonMaxBytesGlobal,
|
|
1711
|
+
maxEntries: GLOBAL_LESSON_ENTRY_CAP,
|
|
1712
|
+
},
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
// Per-agent `agent:<slug>` stores under `policies/agents/<slug>/lessons.md`.
|
|
1716
|
+
// The slug is the directory name; the same `isSafeAgentSlug` guard the
|
|
1717
|
+
// inject + consolidate sides apply rejects any unsafe directory name so a
|
|
1718
|
+
// hand-created `..`-style dir never reaches the worksheet.
|
|
1719
|
+
const agentsDir = join(contextDir, "policies", "agents");
|
|
1720
|
+
if (existsSync(agentsDir)) {
|
|
1721
|
+
for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
1722
|
+
if (!entry.isDirectory() || !isSafeAgentSlug(entry.name))
|
|
1723
|
+
continue;
|
|
1724
|
+
const rel = agentLessonsPath(entry.name);
|
|
1725
|
+
const md = readScopeFile(rel);
|
|
1726
|
+
if (md === null)
|
|
1727
|
+
continue;
|
|
1728
|
+
scopes.push({
|
|
1729
|
+
scope: { kind: "agent_slug", ref: entry.name },
|
|
1730
|
+
storeFile: rel,
|
|
1731
|
+
existingFileMd: md,
|
|
1732
|
+
caps: {
|
|
1733
|
+
capBytes: this.config.feedbackLessonMaxBytesPerAgent,
|
|
1734
|
+
maxEntries: PER_AGENT_LESSON_ENTRY_CAP,
|
|
1735
|
+
},
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (scopes.length === 0)
|
|
1740
|
+
return null;
|
|
1741
|
+
const result = buildRegeneralizationWorksheet(scopes, {
|
|
1742
|
+
nowIso: new Date().toISOString(),
|
|
1743
|
+
staleDays: this.config.feedbackLessonStaleDays,
|
|
1744
|
+
});
|
|
1745
|
+
return result?.block ?? null;
|
|
1746
|
+
}
|
|
1747
|
+
catch (err) {
|
|
1748
|
+
logger.warn({ err }, "Failed to prepare feedback re-generalization worksheet");
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1280
1752
|
/**
|
|
1281
1753
|
* Append a single bullet under `## Weekly interests reflection`
|
|
1282
1754
|
* inside `context/journal/agent.md`, creating the section (and the
|