@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
1
2
|
import type { AgentConfig } from "../config.js";
|
|
2
3
|
/**
|
|
3
4
|
* Raw signal entry that gets appended to user/profile.md ## Raw Signals section.
|
|
@@ -31,6 +32,7 @@ interface RawSignal {
|
|
|
31
32
|
*/
|
|
32
33
|
export declare class SignalDetector {
|
|
33
34
|
private readonly config;
|
|
35
|
+
private readonly deps;
|
|
34
36
|
/** Track pending notifications for ignore detection */
|
|
35
37
|
private readonly pendingNotifications;
|
|
36
38
|
/** Rolling dedup cache: signal key → expiry timestamp */
|
|
@@ -41,7 +43,9 @@ export declare class SignalDetector {
|
|
|
41
43
|
private ignoreCheckInterval;
|
|
42
44
|
/** API base URL for Context File API (self-referencing) */
|
|
43
45
|
private readonly apiBaseUrl;
|
|
44
|
-
constructor(config: AgentConfig
|
|
46
|
+
constructor(config: AgentConfig, deps?: {
|
|
47
|
+
db?: Database.Database;
|
|
48
|
+
});
|
|
45
49
|
/** Start the signal detector (periodic ignore check) */
|
|
46
50
|
start(): void;
|
|
47
51
|
/** Stop the signal detector */
|
|
@@ -67,9 +71,33 @@ export declare class SignalDetector {
|
|
|
67
71
|
*/
|
|
68
72
|
onUserMessage(params: {
|
|
69
73
|
platform: string;
|
|
74
|
+
channel?: string;
|
|
70
75
|
content: string;
|
|
71
76
|
responseToNotificationId?: string;
|
|
72
77
|
}): void;
|
|
78
|
+
/**
|
|
79
|
+
* Record a positive action correlated with a notification. This is the
|
|
80
|
+
* narrow "acted" path: callers must have an actual task/action observation,
|
|
81
|
+
* never silence, before invoking it.
|
|
82
|
+
*
|
|
83
|
+
* NOTE — intentional Phase-1.5 seam, no production caller yet (by design).
|
|
84
|
+
* Firing `acted` deterministically needs the notification's subject
|
|
85
|
+
* `(source, ref)` in `pendingNotifications` to match a later observation,
|
|
86
|
+
* but that subject never reaches `trackNotification`: entity-related
|
|
87
|
+
* proactive DMs are re-authored by an agent turn and delivered via
|
|
88
|
+
* `POST /api/agent/notify`, which carries only message/platform/priority
|
|
89
|
+
* (no subject), and the direct `NotificationManager.send()` paths carry
|
|
90
|
+
* `data:{}`. So this stays correct + silence-safe but dormant — do NOT
|
|
91
|
+
* wire it from a silence- or substring-heuristic source (that re-opens the
|
|
92
|
+
* sign-inversion the promotion gate exists to kill). The loop is complete
|
|
93
|
+
* without it (`explicit` + `replied` drive promotion). See
|
|
94
|
+
* FEEDBACK_LEARNING_LOOP_DESIGN.md §11 v1.9#2 + v1.11.
|
|
95
|
+
*/
|
|
96
|
+
onNotificationActed(params: {
|
|
97
|
+
notificationId: string;
|
|
98
|
+
actionRef?: string;
|
|
99
|
+
detail?: string;
|
|
100
|
+
}): void;
|
|
73
101
|
/** Check for messages that have been ignored (no response within threshold) */
|
|
74
102
|
private checkIgnoredMessages;
|
|
75
103
|
/**
|
|
@@ -92,5 +120,27 @@ export declare class SignalDetector {
|
|
|
92
120
|
static normalizeDedupKey(signal: RawSignal): string;
|
|
93
121
|
/** Remove expired entries from the dedup cache */
|
|
94
122
|
private pruneExpiredDedup;
|
|
123
|
+
/**
|
|
124
|
+
* Precision matters more than recall here: a match marks the reply
|
|
125
|
+
* `corrected` (valence `correction`), and the promotion gate treats any
|
|
126
|
+
* correction as an authoritative directive that promotes a lesson on
|
|
127
|
+
* FIRST occurrence — one false positive writes a bogus standing
|
|
128
|
+
* directive into `policies/agent-lessons.md`. So the stop/don't pattern
|
|
129
|
+
* requires imperative shape (sentence-initial, modulo a couple of
|
|
130
|
+
* politeness tokens) paired with an agent-action verb, instead of the
|
|
131
|
+
* old `\b(stop|no)\b.*\b(do|that|…)\b` / bare `don't` catch-alls that
|
|
132
|
+
* matched benign replies like "No worries, that sounds great" or
|
|
133
|
+
* "I don't have anything else today".
|
|
134
|
+
*/
|
|
135
|
+
private static isCorrectionContent;
|
|
136
|
+
private findPendingNotificationForReply;
|
|
137
|
+
private resolvePendingNotificationId;
|
|
138
|
+
private lookupNotificationMetadata;
|
|
139
|
+
private resolveAgentIdForNotification;
|
|
140
|
+
private recordNotificationOutcome;
|
|
141
|
+
private updateNotificationReaction;
|
|
142
|
+
private buildOutcomeSummary;
|
|
143
|
+
private sanitizeEvidence;
|
|
144
|
+
private static parseTrackedNotificationId;
|
|
95
145
|
}
|
|
96
146
|
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { formatSqliteDatetime } from "@aitne/shared";
|
|
1
|
+
import { formatSqliteDatetime, redactSensitiveString } from "@aitne/shared";
|
|
2
|
+
import { hasFeedbackSignalForAction, recordFeedbackSignal, } from "../db/feedback-signals-store.js";
|
|
2
3
|
import { createLogger } from "../logging.js";
|
|
4
|
+
import { resolveAgentId } from "./agents/agent-id-resolver.js";
|
|
3
5
|
const logger = createLogger("signal-detector");
|
|
4
6
|
/**
|
|
5
7
|
* Maximum number of Raw Signal entries kept in user/profile.md. Prevents
|
|
@@ -13,6 +15,26 @@ const MAX_RAW_SIGNALS = 20;
|
|
|
13
15
|
* this window are suppressed.
|
|
14
16
|
*/
|
|
15
17
|
const DEDUP_TTL_MS = 10 * 60 * 1000;
|
|
18
|
+
/**
|
|
19
|
+
* Reaction emoji that signal disapproval. A 👎 must not be recorded as a
|
|
20
|
+
* positive behavioral signal — the consolidation LLM only sees the row's
|
|
21
|
+
* summary, so a mis-valenced reaction would count as positive corroboration
|
|
22
|
+
* for the very notification the owner disliked. Conservative set: ambiguous
|
|
23
|
+
* emoji (😢, 😕, …) stay positive-by-default rather than guessing.
|
|
24
|
+
*/
|
|
25
|
+
const NEGATIVE_REACTION_EMOJI = new Set([
|
|
26
|
+
"👎",
|
|
27
|
+
"❌",
|
|
28
|
+
"🚫",
|
|
29
|
+
"🛑",
|
|
30
|
+
"😠",
|
|
31
|
+
"😡",
|
|
32
|
+
"💢",
|
|
33
|
+
]);
|
|
34
|
+
/** Strip skin-tone modifiers + variation selectors so 👎🏽 matches 👎. */
|
|
35
|
+
function normalizeReactionEmoji(emoji) {
|
|
36
|
+
return emoji.replace(/[\u{1F3FB}-\u{1F3FF}\u{FE0E}\u{FE0F}]/gu, "").trim();
|
|
37
|
+
}
|
|
16
38
|
/**
|
|
17
39
|
* SignalDetector — collects implicit user feedback signals (no LLM required).
|
|
18
40
|
*
|
|
@@ -37,6 +59,7 @@ const DEDUP_TTL_MS = 10 * 60 * 1000;
|
|
|
37
59
|
*/
|
|
38
60
|
export class SignalDetector {
|
|
39
61
|
config;
|
|
62
|
+
deps;
|
|
40
63
|
/** Track pending notifications for ignore detection */
|
|
41
64
|
pendingNotifications = new Map();
|
|
42
65
|
/** Rolling dedup cache: signal key → expiry timestamp */
|
|
@@ -47,8 +70,9 @@ export class SignalDetector {
|
|
|
47
70
|
ignoreCheckInterval = null;
|
|
48
71
|
/** API base URL for Context File API (self-referencing) */
|
|
49
72
|
apiBaseUrl;
|
|
50
|
-
constructor(config) {
|
|
73
|
+
constructor(config, deps = {}) {
|
|
51
74
|
this.config = config;
|
|
75
|
+
this.deps = deps;
|
|
52
76
|
this.ignoreThresholdMs = 30 * 60 * 1000; // 30 minutes
|
|
53
77
|
this.apiBaseUrl = `http://localhost:${config.apiPort}`;
|
|
54
78
|
}
|
|
@@ -71,10 +95,17 @@ export class SignalDetector {
|
|
|
71
95
|
* Called by NotificationManager after successful delivery.
|
|
72
96
|
*/
|
|
73
97
|
trackNotification(notificationId, platform, content) {
|
|
98
|
+
const metadata = this.lookupNotificationMetadata(notificationId);
|
|
74
99
|
this.pendingNotifications.set(notificationId, {
|
|
75
100
|
sentAt: Date.now(),
|
|
76
|
-
platform,
|
|
77
|
-
|
|
101
|
+
// Single fallback to the delivered platform here, not redundantly inside
|
|
102
|
+
// the lookup — the tracked id's suffix is the delivery platform anyway.
|
|
103
|
+
platform: metadata.platform ?? platform,
|
|
104
|
+
channel: metadata.channel,
|
|
105
|
+
content: (metadata.contentSummary ?? content).slice(0, 100), // Truncate for signal log
|
|
106
|
+
dispatchId: metadata.dispatchId,
|
|
107
|
+
notificationType: metadata.notificationType,
|
|
108
|
+
agentId: metadata.agentId,
|
|
78
109
|
});
|
|
79
110
|
}
|
|
80
111
|
/**
|
|
@@ -85,12 +116,26 @@ export class SignalDetector {
|
|
|
85
116
|
const { platform, emoji, responseTimeMs } = params;
|
|
86
117
|
// Remove from pending (user responded)
|
|
87
118
|
if (params.notificationId) {
|
|
88
|
-
this.
|
|
119
|
+
const pendingId = this.resolvePendingNotificationId(params.notificationId);
|
|
120
|
+
this.pendingNotifications.delete(pendingId);
|
|
121
|
+
const negative = NEGATIVE_REACTION_EMOJI.has(normalizeReactionEmoji(emoji));
|
|
122
|
+
this.recordNotificationOutcome({
|
|
123
|
+
notificationId: pendingId,
|
|
124
|
+
reaction: "replied",
|
|
125
|
+
valence: negative ? "negative" : "positive",
|
|
126
|
+
emoji,
|
|
127
|
+
evidence: {
|
|
128
|
+
emoji,
|
|
129
|
+
responseTimeMs,
|
|
130
|
+
weight: 0.5,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
89
133
|
}
|
|
90
134
|
const signal = {
|
|
91
135
|
timestamp: formatSqliteDatetime(new Date()),
|
|
92
136
|
type: "reaction",
|
|
93
|
-
detail: `${emoji} on ${platform}
|
|
137
|
+
detail: `${emoji} on ${platform}`
|
|
138
|
+
+ `${responseTimeMs ? ` (${Math.round(responseTimeMs / 1000)}s)` : ""}`,
|
|
94
139
|
};
|
|
95
140
|
void this.appendSignal(signal);
|
|
96
141
|
}
|
|
@@ -100,29 +145,68 @@ export class SignalDetector {
|
|
|
100
145
|
*/
|
|
101
146
|
onUserMessage(params) {
|
|
102
147
|
const { content, responseToNotificationId } = params;
|
|
148
|
+
const pendingNotificationId = responseToNotificationId
|
|
149
|
+
? this.resolvePendingNotificationId(responseToNotificationId)
|
|
150
|
+
: this.findPendingNotificationForReply(params.platform, params.channel);
|
|
103
151
|
// Remove from pending (user responded)
|
|
104
|
-
if (
|
|
105
|
-
this.pendingNotifications.delete(
|
|
152
|
+
if (pendingNotificationId) {
|
|
153
|
+
this.pendingNotifications.delete(pendingNotificationId);
|
|
106
154
|
}
|
|
107
155
|
// Detect correction instructions
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
156
|
+
const isCorrection = SignalDetector.isCorrectionContent(content);
|
|
157
|
+
if (pendingNotificationId) {
|
|
158
|
+
this.recordNotificationOutcome({
|
|
159
|
+
notificationId: pendingNotificationId,
|
|
160
|
+
reaction: isCorrection ? "corrected" : "replied",
|
|
161
|
+
valence: isCorrection ? "correction" : "positive",
|
|
162
|
+
evidence: {
|
|
163
|
+
excerpt: content.slice(0, 160),
|
|
164
|
+
weight: isCorrection ? 1.0 : 0.5,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (isCorrection) {
|
|
169
|
+
const signal = {
|
|
170
|
+
timestamp: formatSqliteDatetime(new Date()),
|
|
171
|
+
type: "correction",
|
|
172
|
+
detail: `"${content.slice(0, 60)}"`,
|
|
173
|
+
};
|
|
174
|
+
void this.appendSignal(signal);
|
|
175
|
+
return; // Only log the first matching correction
|
|
124
176
|
}
|
|
125
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Record a positive action correlated with a notification. This is the
|
|
180
|
+
* narrow "acted" path: callers must have an actual task/action observation,
|
|
181
|
+
* never silence, before invoking it.
|
|
182
|
+
*
|
|
183
|
+
* NOTE — intentional Phase-1.5 seam, no production caller yet (by design).
|
|
184
|
+
* Firing `acted` deterministically needs the notification's subject
|
|
185
|
+
* `(source, ref)` in `pendingNotifications` to match a later observation,
|
|
186
|
+
* but that subject never reaches `trackNotification`: entity-related
|
|
187
|
+
* proactive DMs are re-authored by an agent turn and delivered via
|
|
188
|
+
* `POST /api/agent/notify`, which carries only message/platform/priority
|
|
189
|
+
* (no subject), and the direct `NotificationManager.send()` paths carry
|
|
190
|
+
* `data:{}`. So this stays correct + silence-safe but dormant — do NOT
|
|
191
|
+
* wire it from a silence- or substring-heuristic source (that re-opens the
|
|
192
|
+
* sign-inversion the promotion gate exists to kill). The loop is complete
|
|
193
|
+
* without it (`explicit` + `replied` drive promotion). See
|
|
194
|
+
* FEEDBACK_LEARNING_LOOP_DESIGN.md §11 v1.9#2 + v1.11.
|
|
195
|
+
*/
|
|
196
|
+
onNotificationActed(params) {
|
|
197
|
+
const pendingId = this.resolvePendingNotificationId(params.notificationId);
|
|
198
|
+
this.pendingNotifications.delete(pendingId);
|
|
199
|
+
this.recordNotificationOutcome({
|
|
200
|
+
notificationId: pendingId,
|
|
201
|
+
reaction: "acted",
|
|
202
|
+
valence: "positive",
|
|
203
|
+
evidence: {
|
|
204
|
+
actionRef: params.actionRef,
|
|
205
|
+
detail: params.detail,
|
|
206
|
+
weight: 0.5,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
126
210
|
/** Check for messages that have been ignored (no response within threshold) */
|
|
127
211
|
checkIgnoredMessages() {
|
|
128
212
|
const now = Date.now();
|
|
@@ -136,6 +220,16 @@ export class SignalDetector {
|
|
|
136
220
|
detail: `${info.platform}: "${info.content}" unread for ${Math.round(elapsed / 60000)}min`,
|
|
137
221
|
};
|
|
138
222
|
void this.appendSignal(signal);
|
|
223
|
+
this.recordNotificationOutcome({
|
|
224
|
+
notificationId: id,
|
|
225
|
+
reaction: "ignored",
|
|
226
|
+
valence: "neutral",
|
|
227
|
+
evidence: {
|
|
228
|
+
elapsedMs: elapsed,
|
|
229
|
+
weight: 0.25,
|
|
230
|
+
initiatesLesson: false,
|
|
231
|
+
},
|
|
232
|
+
});
|
|
139
233
|
toRemove.push(id);
|
|
140
234
|
}
|
|
141
235
|
}
|
|
@@ -211,4 +305,207 @@ export class SignalDetector {
|
|
|
211
305
|
}
|
|
212
306
|
}
|
|
213
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Precision matters more than recall here: a match marks the reply
|
|
310
|
+
* `corrected` (valence `correction`), and the promotion gate treats any
|
|
311
|
+
* correction as an authoritative directive that promotes a lesson on
|
|
312
|
+
* FIRST occurrence — one false positive writes a bogus standing
|
|
313
|
+
* directive into `policies/agent-lessons.md`. So the stop/don't pattern
|
|
314
|
+
* requires imperative shape (sentence-initial, modulo a couple of
|
|
315
|
+
* politeness tokens) paired with an agent-action verb, instead of the
|
|
316
|
+
* old `\b(stop|no)\b.*\b(do|that|…)\b` / bare `don't` catch-alls that
|
|
317
|
+
* matched benign replies like "No worries, that sounds great" or
|
|
318
|
+
* "I don't have anything else today".
|
|
319
|
+
*/
|
|
320
|
+
static isCorrectionContent(content) {
|
|
321
|
+
const correctionPatterns = [
|
|
322
|
+
/\b(shorter|brief|briefly|concise|concisely|less verbose|too long|too verbose)\b/i,
|
|
323
|
+
/\b(more detail|elaborate|expand)\b/i,
|
|
324
|
+
/\bin (english|spanish|french|german|portuguese|italian|chinese|japanese|korean|arabic|hindi|russian)\b/i,
|
|
325
|
+
/\b(bullet points|bulleted list)\b/i,
|
|
326
|
+
// Imperative "stop/don't/no more <agent action>" at the start of the
|
|
327
|
+
// reply: optional politeness tokens, a negation/stop verb, then an
|
|
328
|
+
// agent-action word within the same clause.
|
|
329
|
+
/^\W*(?:(?:please|pls|just|hey|ok|okay|can you|could you|you can|maybe)\s+){0,2}(?:stop|quit|don['’]t|do not|never|no more)\b[^.!?\n]{0,40}?\b(?:notify(?:ing)?|notifications?|messag(?:e|es|ing)|remind(?:er|ers|ing)?|send(?:ing)?|ping(?:ing)?|dm(?:s|ing)?|post(?:ing)?|repeat(?:ing)?|do(?:ing)?\s+(?:that|this))\b/i,
|
|
330
|
+
];
|
|
331
|
+
return correctionPatterns.some((pattern) => pattern.test(content));
|
|
332
|
+
}
|
|
333
|
+
findPendingNotificationForReply(platform, channel) {
|
|
334
|
+
const now = Date.now();
|
|
335
|
+
let candidate = null;
|
|
336
|
+
for (const [id, info] of this.pendingNotifications) {
|
|
337
|
+
if (info.platform !== platform)
|
|
338
|
+
continue;
|
|
339
|
+
if (channel && info.channel && info.channel !== channel)
|
|
340
|
+
continue;
|
|
341
|
+
if (now - info.sentAt > this.ignoreThresholdMs)
|
|
342
|
+
continue;
|
|
343
|
+
if (candidate === null || info.sentAt > candidate.sentAt) {
|
|
344
|
+
candidate = { id, sentAt: info.sentAt };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return candidate?.id ?? null;
|
|
348
|
+
}
|
|
349
|
+
resolvePendingNotificationId(notificationId) {
|
|
350
|
+
if (this.pendingNotifications.has(notificationId))
|
|
351
|
+
return notificationId;
|
|
352
|
+
for (const id of this.pendingNotifications.keys()) {
|
|
353
|
+
if (id.startsWith(`${notificationId}:`))
|
|
354
|
+
return id;
|
|
355
|
+
}
|
|
356
|
+
return notificationId;
|
|
357
|
+
}
|
|
358
|
+
lookupNotificationMetadata(notificationId) {
|
|
359
|
+
const parsed = SignalDetector.parseTrackedNotificationId(notificationId);
|
|
360
|
+
if (!this.deps.db || !parsed.dispatchId) {
|
|
361
|
+
return {
|
|
362
|
+
dispatchId: parsed.dispatchId,
|
|
363
|
+
platform: parsed.platform,
|
|
364
|
+
channel: null,
|
|
365
|
+
notificationType: null,
|
|
366
|
+
contentSummary: null,
|
|
367
|
+
agentId: null,
|
|
368
|
+
rowId: null,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const platformValue = parsed.platform;
|
|
372
|
+
const platformPredicate = platformValue ? "AND platform = ?" : "";
|
|
373
|
+
const values = platformValue
|
|
374
|
+
? [parsed.dispatchId, platformValue]
|
|
375
|
+
: [parsed.dispatchId];
|
|
376
|
+
const row = this.deps.db
|
|
377
|
+
.prepare(`SELECT id, dispatch_id, platform, delivery_channel, notification_type, content_summary
|
|
378
|
+
FROM notification_log
|
|
379
|
+
WHERE dispatch_id = ? ${platformPredicate}
|
|
380
|
+
ORDER BY id DESC
|
|
381
|
+
LIMIT 1`)
|
|
382
|
+
.get(...values);
|
|
383
|
+
const notificationType = row?.notification_type ?? null;
|
|
384
|
+
return {
|
|
385
|
+
dispatchId: row?.dispatch_id ?? parsed.dispatchId,
|
|
386
|
+
platform: row?.platform ?? parsed.platform,
|
|
387
|
+
channel: row?.delivery_channel ?? null,
|
|
388
|
+
notificationType,
|
|
389
|
+
contentSummary: row?.content_summary ?? null,
|
|
390
|
+
agentId: this.resolveAgentIdForNotification(notificationType),
|
|
391
|
+
rowId: row?.id ?? null,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
resolveAgentIdForNotification(notificationType) {
|
|
395
|
+
if (!this.deps.db || !notificationType?.startsWith("routine."))
|
|
396
|
+
return null;
|
|
397
|
+
// Runs only after a successful notification_log read on the same db, so a
|
|
398
|
+
// connection failure would already have surfaced upstream; the registry
|
|
399
|
+
// lookup + agents existence check are deterministic over a valid handle.
|
|
400
|
+
return resolveAgentId(this.deps.db, {
|
|
401
|
+
routine: notificationType.slice("routine.".length),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
recordNotificationOutcome(params) {
|
|
405
|
+
const db = this.deps.db;
|
|
406
|
+
if (this.config.feedbackLearningEnabled === false || !db)
|
|
407
|
+
return;
|
|
408
|
+
// One guard for the whole behavioral-capture path: the reaction backfill,
|
|
409
|
+
// dedup probe, and signal insert all touch the same connection, so a single
|
|
410
|
+
// catch keeps a DB hiccup from crashing the background detector loop
|
|
411
|
+
// without leaving the reaction column write unguarded.
|
|
412
|
+
try {
|
|
413
|
+
const metadata = this.lookupNotificationMetadata(params.notificationId);
|
|
414
|
+
const dispatchId = metadata.dispatchId;
|
|
415
|
+
if (!dispatchId)
|
|
416
|
+
return;
|
|
417
|
+
this.updateNotificationReaction(db, dispatchId, metadata.platform, params.reaction);
|
|
418
|
+
if (hasFeedbackSignalForAction(db, {
|
|
419
|
+
source: "behavioral",
|
|
420
|
+
actionKind: "notification",
|
|
421
|
+
actionRef: dispatchId,
|
|
422
|
+
valence: params.valence,
|
|
423
|
+
userReaction: params.reaction,
|
|
424
|
+
})) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const scopeType = metadata.agentId ? "agent_slug" : "agent";
|
|
428
|
+
const summary = this.buildOutcomeSummary(params.reaction, metadata, {
|
|
429
|
+
valence: params.valence,
|
|
430
|
+
emoji: params.emoji,
|
|
431
|
+
});
|
|
432
|
+
const evidence = this.sanitizeEvidence({
|
|
433
|
+
...params.evidence,
|
|
434
|
+
userReaction: params.reaction,
|
|
435
|
+
notificationLogId: metadata.rowId,
|
|
436
|
+
notificationType: metadata.notificationType,
|
|
437
|
+
platform: metadata.platform,
|
|
438
|
+
contentSummary: metadata.contentSummary,
|
|
439
|
+
});
|
|
440
|
+
recordFeedbackSignal(db, {
|
|
441
|
+
source: "behavioral",
|
|
442
|
+
valence: params.valence,
|
|
443
|
+
scopeType,
|
|
444
|
+
scopeRef: metadata.agentId,
|
|
445
|
+
actionKind: "notification",
|
|
446
|
+
actionRef: dispatchId,
|
|
447
|
+
agentId: metadata.agentId,
|
|
448
|
+
summary,
|
|
449
|
+
evidence,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
catch (err) {
|
|
453
|
+
logger.warn({ err, notificationId: params.notificationId, reaction: params.reaction }, "Failed to record notification outcome");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
updateNotificationReaction(db, dispatchId, platform, reaction) {
|
|
457
|
+
if (platform) {
|
|
458
|
+
db.prepare(`UPDATE notification_log
|
|
459
|
+
SET user_reaction = ?, reacted_at = CURRENT_TIMESTAMP
|
|
460
|
+
WHERE dispatch_id = ? AND platform = ?`).run(reaction, dispatchId, platform);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
db.prepare(`UPDATE notification_log
|
|
464
|
+
SET user_reaction = ?, reacted_at = CURRENT_TIMESTAMP
|
|
465
|
+
WHERE dispatch_id = ?`).run(reaction, dispatchId);
|
|
466
|
+
}
|
|
467
|
+
buildOutcomeSummary(reaction, metadata, opts = {}) {
|
|
468
|
+
const content = metadata.contentSummary
|
|
469
|
+
? ` "${metadata.contentSummary}"`
|
|
470
|
+
: "";
|
|
471
|
+
const notificationType = metadata.notificationType
|
|
472
|
+
? ` (${metadata.notificationType})`
|
|
473
|
+
: "";
|
|
474
|
+
const emojiNote = opts.emoji ? ` (${opts.emoji})` : "";
|
|
475
|
+
const raw = reaction === "ignored"
|
|
476
|
+
? `Owner did not respond to notification${content}${notificationType}`
|
|
477
|
+
: reaction === "corrected"
|
|
478
|
+
? `Owner corrected notification${content}${notificationType}`
|
|
479
|
+
: reaction === "acted"
|
|
480
|
+
? `Owner acted on notification${content}${notificationType}`
|
|
481
|
+
: opts.valence === "negative"
|
|
482
|
+
? `Owner reacted negatively${emojiNote} to notification${content}${notificationType}`
|
|
483
|
+
: opts.emoji
|
|
484
|
+
? `Owner reacted${emojiNote} to notification${content}${notificationType}`
|
|
485
|
+
: `Owner responded to notification${content}${notificationType}`;
|
|
486
|
+
return redactSensitiveString(raw.replace(/\s+/g, " ").trim()).slice(0, 280);
|
|
487
|
+
}
|
|
488
|
+
sanitizeEvidence(value) {
|
|
489
|
+
const out = {};
|
|
490
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
491
|
+
if (entry === undefined)
|
|
492
|
+
continue;
|
|
493
|
+
if (typeof entry === "string") {
|
|
494
|
+
out[key] = redactSensitiveString(entry.replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 500));
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
out[key] = entry;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return out;
|
|
501
|
+
}
|
|
502
|
+
static parseTrackedNotificationId(notificationId) {
|
|
503
|
+
const idx = notificationId.indexOf(":");
|
|
504
|
+
if (idx <= 0)
|
|
505
|
+
return { dispatchId: notificationId || null, platform: null };
|
|
506
|
+
return {
|
|
507
|
+
dispatchId: notificationId.slice(0, idx),
|
|
508
|
+
platform: notificationId.slice(idx + 1) || null,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
214
511
|
}
|
|
@@ -124,7 +124,7 @@ export function buildSameBackendDenyBlock(integrations, sessionBackend) {
|
|
|
124
124
|
"## Denied tools (per-integration)",
|
|
125
125
|
"",
|
|
126
126
|
"The user has restricted the following connector tools for this session.",
|
|
127
|
-
"Do NOT invoke them in any flow — including
|
|
127
|
+
"Do NOT invoke them in any flow — including activity scan, morning routine,",
|
|
128
128
|
"or DM responses. If a workflow appears to require one, stop and tell the",
|
|
129
129
|
"user the tool is denied.",
|
|
130
130
|
];
|
|
@@ -157,7 +157,7 @@ function appendCliDenyBlock(content, namespacedDenied) {
|
|
|
157
157
|
"## Denied tools (do not invoke)",
|
|
158
158
|
"",
|
|
159
159
|
"The user has restricted these connector tools for this integration. Do",
|
|
160
|
-
"NOT invoke them in any flow — including
|
|
160
|
+
"NOT invoke them in any flow — including activity scan, morning routine,",
|
|
161
161
|
"or DM responses. If a workflow appears to require one, stop and tell",
|
|
162
162
|
"the user the tool is denied.",
|
|
163
163
|
"",
|
|
@@ -75,8 +75,8 @@ export declare function refreshSkillIndexBlock(sessionDir: string, backendId: Ba
|
|
|
75
75
|
* @deprecated DELEGATED-PROXY-API-DESIGN.md §11 — after Phase D no in-tree
|
|
76
76
|
* skill uses `{{> base }}`: mail / external-services unified their bodies
|
|
77
77
|
* during Phase D, and the lone surviving `notion/SKILL.base.md` was
|
|
78
|
-
* removed in Phase 9 (its
|
|
79
|
-
* `routine.
|
|
78
|
+
* removed in Phase 9 (its activity-scan read-only constraint moved to
|
|
79
|
+
* `routine.activity_scan.md` task-flow where the rule applies in every
|
|
80
80
|
* integration mode, including same-backend delegated where the skill
|
|
81
81
|
* body is dropped). The helper is retained for future composition needs;
|
|
82
82
|
* remove the call sites in `materializeClaudeSession` /
|
|
@@ -203,8 +203,8 @@ export function refreshSkillIndexBlock(sessionDir, backendId) {
|
|
|
203
203
|
* @deprecated DELEGATED-PROXY-API-DESIGN.md §11 — after Phase D no in-tree
|
|
204
204
|
* skill uses `{{> base }}`: mail / external-services unified their bodies
|
|
205
205
|
* during Phase D, and the lone surviving `notion/SKILL.base.md` was
|
|
206
|
-
* removed in Phase 9 (its
|
|
207
|
-
* `routine.
|
|
206
|
+
* removed in Phase 9 (its activity-scan read-only constraint moved to
|
|
207
|
+
* `routine.activity_scan.md` task-flow where the rule applies in every
|
|
208
208
|
* integration mode, including same-backend delegated where the skill
|
|
209
209
|
* body is dropped). The helper is retained for future composition needs;
|
|
210
210
|
* remove the call sites in `materializeClaudeSession` /
|
|
@@ -113,5 +113,5 @@ export declare const SKILL_DESCRIPTION_MAX_LENGTH = 280;
|
|
|
113
113
|
* MUST use {@link skillBodyTouchesReadSensitive} to keep the
|
|
114
114
|
* "literal prefix" decision encapsulated.
|
|
115
115
|
*/
|
|
116
|
-
export declare const READ_SENSITIVE_API_PREFIXES: readonly ["/api/apple-calendar", "/api/books", "/api/browser-task", "/api/calendar", "/api/context", "/api/entities", "/api/mail", "/api/mcp/servers", "/api/notion", "/api/observations", "/api/obsidian", "/api/receipts", "/api/travel-bookings"];
|
|
116
|
+
export declare const READ_SENSITIVE_API_PREFIXES: readonly ["/api/apple-calendar", "/api/background-task", "/api/books", "/api/browser-task", "/api/calendar", "/api/context", "/api/entities", "/api/mail", "/api/mcp/servers", "/api/notion", "/api/observations", "/api/obsidian", "/api/receipts", "/api/travel-bookings"];
|
|
117
117
|
export declare function skillBodyTouchesReadSensitive(skillBody: string): boolean;
|
|
@@ -311,6 +311,14 @@ export const SKILL_DESCRIPTION_MAX_LENGTH = 280;
|
|
|
311
311
|
*/
|
|
312
312
|
export const READ_SENSITIVE_API_PREFIXES = [
|
|
313
313
|
"/api/apple-calendar",
|
|
314
|
+
// BACKGROUND_TASK_RUNNER_DESIGN.md §7 — list / detail reads on the
|
|
315
|
+
// generic detached-task surface. The artifact JSON (`report` / `brief`
|
|
316
|
+
// / `draft` / `significance`) carries the user's research / audit
|
|
317
|
+
// content, so the GETs are ReadSensitive (the POST spawn + clarify +
|
|
318
|
+
// cancel are Autonomous). A Codex DM session referencing the
|
|
319
|
+
// `background-task` skill body gets the read-token banner so its
|
|
320
|
+
// follow-up `GET /:id` read doesn't 401 silently.
|
|
321
|
+
"/api/background-task",
|
|
314
322
|
"/api/books",
|
|
315
323
|
// BROWSER_TASK_REDESIGN_PLAN.md §3 — list / detail / events / screenshot
|
|
316
324
|
// reads on the open-ended browser sub-agent surface. The screenshot
|
|
@@ -2,6 +2,7 @@ import type DatabaseNs from "better-sqlite3";
|
|
|
2
2
|
import { type BackendId, type IntegrationKey, type IntegrationState, type ProcessKey } from "@aitne/shared";
|
|
3
3
|
import type { MailAccount } from "../services/mail/provider.js";
|
|
4
4
|
import { type SkillCompilerFile } from "./skills-compiler-tree.js";
|
|
5
|
+
export declare const SLIM_CLI_SKILL_SETS: Partial<Record<ProcessKey, readonly string[]>>;
|
|
5
6
|
interface SessionPromptBundleParams {
|
|
6
7
|
backendId: BackendId;
|
|
7
8
|
sessionDir: string;
|
|
@@ -334,39 +335,41 @@ export declare class SkillsCompiler {
|
|
|
334
335
|
private spliceCurationAnchorsInSkill;
|
|
335
336
|
/**
|
|
336
337
|
* docs/design/appendices/skills-unification.md Phase 1 item 15 — the slim path does NOT
|
|
337
|
-
* emit a `<skill-index>` block or the skill-discovery preamble. The
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* the
|
|
341
|
-
*
|
|
342
|
-
* fetcher to scan for skills before executing the acquisition plan.
|
|
338
|
+
* emit a `<skill-index>` block or the skill-discovery preamble. The slim
|
|
339
|
+
* system prompt is a self-contained operational contract and its copied
|
|
340
|
+
* skills are referenced inline by the dispatched user prompt / task-flow.
|
|
341
|
+
* Adding the index would mis-signal the agent to scan for skills before
|
|
342
|
+
* executing its mechanical task.
|
|
343
343
|
*
|
|
344
|
-
*
|
|
345
|
-
* materializer for `
|
|
344
|
+
* fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
|
|
345
|
+
* F4 — slim instruction-file materializer for a `SLIM_CLI_SKILL_SETS` process
|
|
346
|
+
* key on Codex / Gemini CLI.
|
|
346
347
|
*
|
|
347
|
-
* Mirrors the Claude SDK's
|
|
348
|
-
* `agent-assets/system-prompts
|
|
349
|
-
*
|
|
350
|
-
* GEMINI.md and copy only the
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
* (`routine-fetch-window-runner.ts:reassemblePrompt`) covers the
|
|
354
|
-
* per-attempt call shape, so `mail` / `notion` / `external-services` /
|
|
355
|
-
* `attach` skill bodies are deliberately omitted.
|
|
348
|
+
* Mirrors the Claude SDK's slim-systemPrompt swap (the same
|
|
349
|
+
* `agent-assets/system-prompts/<key>.md` template is the single source of
|
|
350
|
+
* truth, read through the shared `loadSlimSystemPrompt` registry): write the
|
|
351
|
+
* slim body verbatim as AGENTS.md / GEMINI.md and copy only the key's
|
|
352
|
+
* `SLIM_CLI_SKILL_SETS` bundle — the wide manifest's other slugs are restated
|
|
353
|
+
* by the dispatched user prompt / task-flow, so their bodies are omitted.
|
|
356
354
|
*
|
|
357
|
-
* No safety preamble / character / behavioral-rules / daemon-API
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
* (absolute-block list, Codex sandbox, Gemini admin TOML) still applies
|
|
362
|
-
* unchanged at runtime.
|
|
355
|
+
* No safety preamble / character / behavioral-rules / daemon-API sections —
|
|
356
|
+
* the slim template restates the only rules the session needs. The
|
|
357
|
+
* destructive-action policy layer (absolute-block list, Codex sandbox,
|
|
358
|
+
* Gemini admin TOML) still applies unchanged at runtime.
|
|
363
359
|
*
|
|
364
360
|
* The `<mcp-servers>` section is appended downstream by
|
|
365
361
|
* `services/mcp/session-materializer.ts:appendMcpSection` exactly as on
|
|
366
|
-
* the wide path
|
|
367
|
-
* that section without further changes here.
|
|
362
|
+
* the wide path.
|
|
368
363
|
*/
|
|
369
|
-
private
|
|
364
|
+
private materializeSlimCliSession;
|
|
365
|
+
/**
|
|
366
|
+
* Copy one built-in skill dir into a slim CLI session, applying the same
|
|
367
|
+
* adaptation pipeline the wide CLI path runs (brand tokens, partial /
|
|
368
|
+
* reference includes, integration-mode filter, tool-deny prose, curation
|
|
369
|
+
* anchors). Frontmatter stays intact (skills-unification.md §R6). A
|
|
370
|
+
* no-op when the source SKILL.md is absent.
|
|
371
|
+
*/
|
|
372
|
+
private copySlimSkillDir;
|
|
370
373
|
private materializeCliSession;
|
|
371
374
|
}
|
|
372
375
|
export {};
|