@ouro.bot/cli 0.1.0-alpha.4 → 0.1.0-alpha.400
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/README.md +208 -184
- package/SerpentGuide.ouro/agent.json +82 -0
- package/SerpentGuide.ouro/psyche/SOUL.md +25 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/assets/ouroboros.png +0 -0
- package/changelog.json +2610 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +58 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +417 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +176 -130
- package/dist/heart/core.js +872 -255
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +397 -0
- package/dist/heart/daemon/agent-discovery.js +157 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +213 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +599 -0
- package/dist/heart/daemon/cli-exec.js +3616 -0
- package/dist/heart/daemon/cli-help.js +396 -0
- package/dist/heart/daemon/cli-parse.js +1118 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +560 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +29 -673
- package/dist/heart/daemon/daemon-entry.js +370 -8
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +235 -0
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +813 -19
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +419 -0
- package/dist/heart/daemon/health-monitor.js +79 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +206 -0
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +209 -0
- package/dist/heart/daemon/launchd.js +171 -0
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +105 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/process-manager.js +202 -1
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +216 -0
- package/dist/heart/daemon/run-hooks.js +39 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +191 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +355 -0
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +307 -0
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +237 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/daemon/up-progress.js +135 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +181 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-scheduler.js +371 -0
- package/dist/heart/hatch/hatch-animation.js +35 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -135
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/hatch/specialist-orchestrator.js +129 -0
- package/dist/heart/hatch/specialist-prompt.js +102 -0
- package/dist/heart/hatch/specialist-tools.js +304 -0
- package/dist/heart/identity.js +246 -58
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http-hooks.js +64 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +232 -0
- package/dist/heart/outlook/outlook-http-static.js +99 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +28 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +195 -0
- package/dist/heart/outlook/readers/agent-machine.js +359 -0
- package/dist/heart/outlook/readers/continuity-readers.js +332 -0
- package/dist/heart/outlook/readers/runtime-readers.js +660 -0
- package/dist/heart/outlook/readers/sessions.js +232 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-attempt.js +133 -0
- package/dist/heart/provider-binding-resolver.js +239 -0
- package/dist/heart/provider-credentials.js +383 -0
- package/dist/heart/provider-failover.js +266 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +237 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +186 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +202 -50
- package/dist/heart/providers/azure.js +103 -12
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +29 -7
- package/dist/heart/providers/openai-codex.js +39 -29
- package/dist/heart/runtime-credentials.js +181 -0
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +855 -0
- package/dist/heart/session-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +117 -33
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +351 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +301 -0
- package/dist/heart/{daemon → versioning}/ouro-uti.js +11 -2
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/versioning/staged-restart.js +146 -0
- package/dist/heart/versioning/update-checker.js +113 -0
- package/dist/heart/versioning/update-hooks.js +142 -0
- package/dist/heart/versioning/wrapper-publish-guard.js +86 -0
- package/dist/mind/bundle-manifest.js +77 -1
- package/dist/mind/context.js +141 -94
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +84 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +64 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +38 -1
- package/dist/mind/friends/store-file.js +58 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +10 -2
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1111 -117
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +22 -3
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/contract.js +5 -5
- package/dist/nerves/coverage/file-completeness.js +83 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-BAcU08c-.css +1 -0
- package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +17 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +461 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +79 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +301 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +220 -13
- package/dist/repertoire/coding/spawner.js +58 -12
- package/dist/repertoire/coding/tools.js +209 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +107 -0
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +371 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -79
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +45 -707
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +182 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +739 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +64 -61
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +149 -98
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +382 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/bluebubbles/client.js +685 -0
- package/dist/senses/bluebubbles/entry.js +70 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1620 -0
- package/dist/senses/bluebubbles/media.js +389 -0
- package/dist/senses/bluebubbles/model.js +282 -0
- package/dist/senses/bluebubbles/mutation-log.js +116 -0
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/bluebubbles/session-cleanup.js +72 -0
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +605 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +768 -264
- package/dist/senses/commands.js +66 -3
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +636 -86
- package/dist/senses/pipeline.js +602 -0
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +205 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +844 -197
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +38 -6
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +110 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +101 -0
- package/skills/travel-planning.md +134 -0
- package/AdoptionSpecialist.ouro/agent.json +0 -20
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +0 -22
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/dist/mind/associative-recall.js +0 -197
- package/subagents/README.md +0 -73
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.vaultUnlockReplaceRecoverFix = vaultUnlockReplaceRecoverFix;
|
|
37
|
+
exports.resolveVaultUnlockStore = resolveVaultUnlockStore;
|
|
38
|
+
exports.readVaultUnlockSecret = readVaultUnlockSecret;
|
|
39
|
+
exports.storeVaultUnlockSecret = storeVaultUnlockSecret;
|
|
40
|
+
exports.getVaultUnlockStatus = getVaultUnlockStatus;
|
|
41
|
+
const node_child_process_1 = require("node:child_process");
|
|
42
|
+
const crypto = __importStar(require("node:crypto"));
|
|
43
|
+
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const os = __importStar(require("node:os"));
|
|
45
|
+
const path = __importStar(require("node:path"));
|
|
46
|
+
const runtime_1 = require("../nerves/runtime");
|
|
47
|
+
const VAULT_UNLOCK_SERVICE = "ouro.vault";
|
|
48
|
+
const PLAINTEXT_UNLOCK_DIR = path.join(".ouro-cli", "vault-unlock");
|
|
49
|
+
const WINDOWS_DPAPI_UNLOCK_DIR = path.join(".ouro-cli", "vault-unlock-dpapi");
|
|
50
|
+
const SUPPORTED_STORES = ["auto", "macos-keychain", "windows-dpapi", "linux-secret-service", "plaintext-file"];
|
|
51
|
+
function platform(deps) {
|
|
52
|
+
return deps.platform ?? process.platform;
|
|
53
|
+
}
|
|
54
|
+
function spawnSync(deps) {
|
|
55
|
+
return deps.spawnSync ?? node_child_process_1.spawnSync;
|
|
56
|
+
}
|
|
57
|
+
function homeDir(deps) {
|
|
58
|
+
return deps.homeDir ?? os.homedir();
|
|
59
|
+
}
|
|
60
|
+
function vaultKey(config) {
|
|
61
|
+
return `${config.serverUrl}:${config.email}`;
|
|
62
|
+
}
|
|
63
|
+
function vaultLabel(config) {
|
|
64
|
+
return `${config.email} at ${config.serverUrl}`;
|
|
65
|
+
}
|
|
66
|
+
function plaintextUnlockPath(config, deps) {
|
|
67
|
+
const digest = crypto.createHash("sha256").update(vaultKey(config)).digest("hex").slice(0, 24);
|
|
68
|
+
return path.join(homeDir(deps), PLAINTEXT_UNLOCK_DIR, `${digest}.secret`);
|
|
69
|
+
}
|
|
70
|
+
function windowsDpapiUnlockPath(config, deps) {
|
|
71
|
+
const digest = crypto.createHash("sha256").update(vaultKey(config)).digest("hex").slice(0, 24);
|
|
72
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
73
|
+
const baseDir = localAppData && platform(deps) === "win32"
|
|
74
|
+
? path.join(localAppData, "Ouro")
|
|
75
|
+
: path.join(homeDir(deps), WINDOWS_DPAPI_UNLOCK_DIR);
|
|
76
|
+
return path.join(baseDir, "vault-unlock", `${digest}.dpapi`);
|
|
77
|
+
}
|
|
78
|
+
function commandExists(command, deps) {
|
|
79
|
+
const result = spawnSync(deps)(command, ["--version"], { encoding: "utf8" });
|
|
80
|
+
const code = result.error?.code;
|
|
81
|
+
return code !== "ENOENT";
|
|
82
|
+
}
|
|
83
|
+
function missingSecureStoreMessage(config) {
|
|
84
|
+
const agentPart = config.agentName ? ` for ${config.agentName}` : "";
|
|
85
|
+
return [
|
|
86
|
+
`No supported secure local secret store was found on this machine${agentPart}.`,
|
|
87
|
+
"",
|
|
88
|
+
`Ouro knows the credential vault is ${vaultLabel(config)}, but it cannot cache the vault unlock secret here yet.`,
|
|
89
|
+
"",
|
|
90
|
+
"On macOS, Ouro uses Keychain automatically.",
|
|
91
|
+
"On Windows, Ouro uses a CurrentUser DPAPI-encrypted local file automatically.",
|
|
92
|
+
"On Linux/WSL, install and configure Secret Service/libsecret, or choose the explicit plaintext fallback on a trusted machine.",
|
|
93
|
+
"",
|
|
94
|
+
config.agentName
|
|
95
|
+
? `Run \`ouro vault unlock --agent ${config.agentName} --store plaintext-file\` to store the vault unlock secret in a chmod 0600 local file.`
|
|
96
|
+
: "Run `ouro vault unlock --store plaintext-file` to store the vault unlock secret in a chmod 0600 local file.",
|
|
97
|
+
].join("\n");
|
|
98
|
+
}
|
|
99
|
+
function vaultUnlockReplaceRecoverFix(agentName, nextStep = "Then run 'ouro up' again.") {
|
|
100
|
+
return [
|
|
101
|
+
`Run 'ouro vault unlock --agent ${agentName}' if you have the saved vault unlock secret.`,
|
|
102
|
+
`If this agent predates vault auth or nobody saved the unlock secret, run 'ouro vault replace --agent ${agentName}' to create a new empty vault, then re-auth/re-enter credentials.`,
|
|
103
|
+
`If you do have a local JSON credential export, run 'ouro vault recover --agent ${agentName} --from <json>' to create the agent vault and import it.`,
|
|
104
|
+
nextStep,
|
|
105
|
+
].join(" ");
|
|
106
|
+
}
|
|
107
|
+
function lostUnlockSecretGuidance(config) {
|
|
108
|
+
if (!config.agentName) {
|
|
109
|
+
return "If nobody saved that unlock secret, run `ouro vault replace --agent <agent>` to create a new empty vault and re-enter credentials. If you do have a local JSON credential export, run `ouro vault recover --agent <agent> --from <json>` to import it.";
|
|
110
|
+
}
|
|
111
|
+
return [
|
|
112
|
+
`If nobody saved that unlock secret, run \`ouro vault replace --agent ${config.agentName}\` to create a new empty vault and re-enter credentials.`,
|
|
113
|
+
`If you do have a local JSON credential export, run \`ouro vault recover --agent ${config.agentName} --from <json>\` to import it.`,
|
|
114
|
+
].join(" ");
|
|
115
|
+
}
|
|
116
|
+
function lockedMessage(config, store) {
|
|
117
|
+
const agentPart = config.agentName ? ` for ${config.agentName}` : "";
|
|
118
|
+
const command = config.agentName
|
|
119
|
+
? `ouro vault unlock --agent ${config.agentName}${store.kind === "plaintext-file" ? " --store plaintext-file" : ""}`
|
|
120
|
+
: `ouro vault unlock${store.kind === "plaintext-file" ? " --store plaintext-file" : ""}`;
|
|
121
|
+
return [
|
|
122
|
+
`Ouro credential vault is locked on this machine${agentPart}.`,
|
|
123
|
+
"",
|
|
124
|
+
`Vault: ${vaultLabel(config)}`,
|
|
125
|
+
`Local unlock store: ${store.kind} (${store.location})`,
|
|
126
|
+
"",
|
|
127
|
+
"Provider credentials are still stored in the agent vault.",
|
|
128
|
+
"This computer does not currently have usable local unlock material for that vault.",
|
|
129
|
+
"This can happen on a new computer, after a local profile or hostname migration, or if the local unlock entry was removed.",
|
|
130
|
+
"",
|
|
131
|
+
`Run \`${command}\` and enter the saved agent vault unlock secret from the human/operator who controls that vault.`,
|
|
132
|
+
lostUnlockSecretGuidance(config),
|
|
133
|
+
].join("\n");
|
|
134
|
+
}
|
|
135
|
+
function validateStoreKind(store) {
|
|
136
|
+
const requested = store ?? "auto";
|
|
137
|
+
if (!SUPPORTED_STORES.includes(requested)) {
|
|
138
|
+
throw new Error(`unknown vault unlock store '${requested}'. Use auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file.`);
|
|
139
|
+
}
|
|
140
|
+
return requested;
|
|
141
|
+
}
|
|
142
|
+
function resolveVaultUnlockStore(config, deps = {}) {
|
|
143
|
+
const requested = validateStoreKind(deps.store);
|
|
144
|
+
const currentPlatform = platform(deps);
|
|
145
|
+
if (requested === "macos-keychain") {
|
|
146
|
+
if (currentPlatform !== "darwin") {
|
|
147
|
+
throw new Error(`macos-keychain unlock store is only available on macOS; this machine is ${currentPlatform}.`);
|
|
148
|
+
}
|
|
149
|
+
return { kind: "macos-keychain", secure: true, location: "macOS Keychain" };
|
|
150
|
+
}
|
|
151
|
+
if (requested === "linux-secret-service") {
|
|
152
|
+
if (currentPlatform !== "linux") {
|
|
153
|
+
throw new Error(`linux-secret-service unlock store is only available on Linux/WSL; this machine is ${currentPlatform}.`);
|
|
154
|
+
}
|
|
155
|
+
if (!commandExists("secret-tool", deps)) {
|
|
156
|
+
throw new Error("linux-secret-service unlock store requires the `secret-tool` command from libsecret.");
|
|
157
|
+
}
|
|
158
|
+
return { kind: "linux-secret-service", secure: true, location: "Secret Service via secret-tool" };
|
|
159
|
+
}
|
|
160
|
+
if (requested === "windows-dpapi") {
|
|
161
|
+
if (currentPlatform !== "win32") {
|
|
162
|
+
throw new Error(`windows-dpapi unlock store is only available on Windows; this machine is ${currentPlatform}.`);
|
|
163
|
+
}
|
|
164
|
+
return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(config, deps) };
|
|
165
|
+
}
|
|
166
|
+
if (requested === "plaintext-file") {
|
|
167
|
+
return { kind: "plaintext-file", secure: false, location: plaintextUnlockPath(config, deps) };
|
|
168
|
+
}
|
|
169
|
+
if (currentPlatform === "darwin") {
|
|
170
|
+
return { kind: "macos-keychain", secure: true, location: "macOS Keychain" };
|
|
171
|
+
}
|
|
172
|
+
if (currentPlatform === "win32") {
|
|
173
|
+
return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(config, deps) };
|
|
174
|
+
}
|
|
175
|
+
if (currentPlatform === "linux" && commandExists("secret-tool", deps)) {
|
|
176
|
+
return { kind: "linux-secret-service", secure: true, location: "Secret Service via secret-tool" };
|
|
177
|
+
}
|
|
178
|
+
throw new Error(missingSecureStoreMessage(config));
|
|
179
|
+
}
|
|
180
|
+
function readFromMacosKeychain(config, deps) {
|
|
181
|
+
const result = spawnSync(deps)("security", [
|
|
182
|
+
"find-generic-password",
|
|
183
|
+
"-s",
|
|
184
|
+
VAULT_UNLOCK_SERVICE,
|
|
185
|
+
"-a",
|
|
186
|
+
vaultKey(config),
|
|
187
|
+
"-w",
|
|
188
|
+
], { encoding: "utf8" });
|
|
189
|
+
const secret = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
190
|
+
return result.status === 0 && secret ? secret : null;
|
|
191
|
+
}
|
|
192
|
+
function writeToMacosKeychain(config, secret, deps) {
|
|
193
|
+
const result = spawnSync(deps)("security", [
|
|
194
|
+
"add-generic-password",
|
|
195
|
+
"-U",
|
|
196
|
+
"-s",
|
|
197
|
+
VAULT_UNLOCK_SERVICE,
|
|
198
|
+
"-a",
|
|
199
|
+
vaultKey(config),
|
|
200
|
+
"-w",
|
|
201
|
+
secret,
|
|
202
|
+
], { encoding: "utf8" });
|
|
203
|
+
if (result.status !== 0) {
|
|
204
|
+
const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
|
|
205
|
+
throw new Error(`failed to store vault unlock secret in macOS Keychain${stderr ? `: ${stderr}` : ""}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function readFromLinuxSecretService(config, deps) {
|
|
209
|
+
const result = spawnSync(deps)("secret-tool", [
|
|
210
|
+
"lookup",
|
|
211
|
+
"service",
|
|
212
|
+
VAULT_UNLOCK_SERVICE,
|
|
213
|
+
"account",
|
|
214
|
+
vaultKey(config),
|
|
215
|
+
], { encoding: "utf8" });
|
|
216
|
+
const secret = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
217
|
+
return result.status === 0 && secret ? secret : null;
|
|
218
|
+
}
|
|
219
|
+
function writeToLinuxSecretService(config, secret, deps) {
|
|
220
|
+
const result = spawnSync(deps)("secret-tool", [
|
|
221
|
+
"store",
|
|
222
|
+
"--label",
|
|
223
|
+
`Ouro credential vault ${vaultLabel(config)}`,
|
|
224
|
+
"service",
|
|
225
|
+
VAULT_UNLOCK_SERVICE,
|
|
226
|
+
"account",
|
|
227
|
+
vaultKey(config),
|
|
228
|
+
], { encoding: "utf8", input: secret });
|
|
229
|
+
if (result.status !== 0) {
|
|
230
|
+
const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
|
|
231
|
+
throw new Error(`failed to store vault unlock secret in Linux Secret Service${stderr ? `: ${stderr}` : ""}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function runWindowsDpapi(mode, payload, deps) {
|
|
235
|
+
const script = `
|
|
236
|
+
$ErrorActionPreference = "Stop"
|
|
237
|
+
$inputJson = [Console]::In.ReadToEnd()
|
|
238
|
+
$payload = $inputJson | ConvertFrom-Json
|
|
239
|
+
Add-Type -AssemblyName System.Security
|
|
240
|
+
if ($payload.mode -eq "protect") {
|
|
241
|
+
$bytes = [Text.Encoding]::UTF8.GetBytes([string]$payload.secret)
|
|
242
|
+
$protected = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
|
|
243
|
+
[Console]::Out.Write([Convert]::ToBase64String($protected))
|
|
244
|
+
} elseif ($payload.mode -eq "unprotect") {
|
|
245
|
+
$protected = [Convert]::FromBase64String([string]$payload.ciphertext)
|
|
246
|
+
$bytes = [Security.Cryptography.ProtectedData]::Unprotect($protected, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
|
|
247
|
+
[Console]::Out.Write([Text.Encoding]::UTF8.GetString($bytes))
|
|
248
|
+
} else {
|
|
249
|
+
throw "unknown DPAPI mode"
|
|
250
|
+
}
|
|
251
|
+
`;
|
|
252
|
+
const result = spawnSync(deps)("powershell.exe", [
|
|
253
|
+
"-NoProfile",
|
|
254
|
+
"-NonInteractive",
|
|
255
|
+
"-ExecutionPolicy",
|
|
256
|
+
"Bypass",
|
|
257
|
+
"-Command",
|
|
258
|
+
script,
|
|
259
|
+
], {
|
|
260
|
+
encoding: "utf8",
|
|
261
|
+
input: JSON.stringify({ mode, ...payload }),
|
|
262
|
+
});
|
|
263
|
+
if (result.status !== 0) {
|
|
264
|
+
const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
|
|
265
|
+
const error = result.error instanceof Error ? result.error.message : stderr;
|
|
266
|
+
throw new Error(`Windows DPAPI ${mode} failed${error ? `: ${error}` : ""}`);
|
|
267
|
+
}
|
|
268
|
+
return typeof result.stdout === "string" ? result.stdout : "";
|
|
269
|
+
}
|
|
270
|
+
function readFromWindowsDpapi(config, deps) {
|
|
271
|
+
const filePath = windowsDpapiUnlockPath(config, deps);
|
|
272
|
+
if (!fs.existsSync(filePath))
|
|
273
|
+
return null;
|
|
274
|
+
const ciphertext = fs.readFileSync(filePath, "utf8").trim();
|
|
275
|
+
if (!ciphertext)
|
|
276
|
+
return null;
|
|
277
|
+
const secret = runWindowsDpapi("unprotect", { ciphertext }, deps).trim();
|
|
278
|
+
return secret || null;
|
|
279
|
+
}
|
|
280
|
+
function writeToWindowsDpapi(config, secret, deps) {
|
|
281
|
+
const filePath = windowsDpapiUnlockPath(config, deps);
|
|
282
|
+
const ciphertext = runWindowsDpapi("protect", { secret }, deps).trim();
|
|
283
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
284
|
+
fs.writeFileSync(filePath, `${ciphertext}\n`, "utf8");
|
|
285
|
+
}
|
|
286
|
+
function readFromPlaintextFile(config, deps) {
|
|
287
|
+
const filePath = plaintextUnlockPath(config, deps);
|
|
288
|
+
if (!fs.existsSync(filePath))
|
|
289
|
+
return null;
|
|
290
|
+
if (platform(deps) !== "win32") {
|
|
291
|
+
const mode = fs.statSync(filePath).mode & 0o777;
|
|
292
|
+
if ((mode & 0o077) !== 0) {
|
|
293
|
+
throw new Error(`refusing to read plaintext vault unlock file at ${filePath} because permissions are too broad; run chmod 600 ${filePath}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const secret = fs.readFileSync(filePath, "utf8").trim();
|
|
297
|
+
return secret || null;
|
|
298
|
+
}
|
|
299
|
+
function writeToPlaintextFile(config, secret, deps) {
|
|
300
|
+
const filePath = plaintextUnlockPath(config, deps);
|
|
301
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
302
|
+
fs.writeFileSync(filePath, secret, { encoding: "utf8", mode: 0o600 });
|
|
303
|
+
if (platform(deps) !== "win32") {
|
|
304
|
+
fs.chmodSync(path.dirname(filePath), 0o700);
|
|
305
|
+
fs.chmodSync(filePath, 0o600);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function readFromStore(config, store, deps) {
|
|
309
|
+
if (store.kind === "macos-keychain")
|
|
310
|
+
return readFromMacosKeychain(config, deps);
|
|
311
|
+
if (store.kind === "windows-dpapi")
|
|
312
|
+
return readFromWindowsDpapi(config, deps);
|
|
313
|
+
if (store.kind === "linux-secret-service")
|
|
314
|
+
return readFromLinuxSecretService(config, deps);
|
|
315
|
+
return readFromPlaintextFile(config, deps);
|
|
316
|
+
}
|
|
317
|
+
function writeToStore(config, store, secret, deps) {
|
|
318
|
+
if (store.kind === "macos-keychain") {
|
|
319
|
+
writeToMacosKeychain(config, secret, deps);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (store.kind === "linux-secret-service") {
|
|
323
|
+
writeToLinuxSecretService(config, secret, deps);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (store.kind === "windows-dpapi") {
|
|
327
|
+
writeToWindowsDpapi(config, secret, deps);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
writeToPlaintextFile(config, secret, deps);
|
|
331
|
+
}
|
|
332
|
+
function readVaultUnlockSecret(config, deps = {}) {
|
|
333
|
+
const store = resolveVaultUnlockStore(config, deps);
|
|
334
|
+
const secret = readFromStore(config, store, deps);
|
|
335
|
+
if (!secret) {
|
|
336
|
+
throw new Error(lockedMessage(config, store));
|
|
337
|
+
}
|
|
338
|
+
(0, runtime_1.emitNervesEvent)({
|
|
339
|
+
component: "repertoire",
|
|
340
|
+
event: "repertoire.vault_unlock_loaded",
|
|
341
|
+
message: "loaded vault unlock material from local store",
|
|
342
|
+
meta: { store: store.kind, secure: store.secure, hasAgentName: !!config.agentName },
|
|
343
|
+
});
|
|
344
|
+
return { secret, store };
|
|
345
|
+
}
|
|
346
|
+
function storeVaultUnlockSecret(config, secret, deps = {}) {
|
|
347
|
+
const trimmed = secret.trim();
|
|
348
|
+
if (!trimmed) {
|
|
349
|
+
throw new Error("vault unlock secret is required");
|
|
350
|
+
}
|
|
351
|
+
const store = resolveVaultUnlockStore(config, deps);
|
|
352
|
+
writeToStore(config, store, trimmed, deps);
|
|
353
|
+
(0, runtime_1.emitNervesEvent)({
|
|
354
|
+
component: "repertoire",
|
|
355
|
+
event: "repertoire.vault_unlock_stored",
|
|
356
|
+
message: "stored vault unlock material in local store",
|
|
357
|
+
meta: { store: store.kind, secure: store.secure, hasAgentName: !!config.agentName },
|
|
358
|
+
});
|
|
359
|
+
return store;
|
|
360
|
+
}
|
|
361
|
+
function getVaultUnlockStatus(config, deps = {}) {
|
|
362
|
+
try {
|
|
363
|
+
const store = resolveVaultUnlockStore(config, deps);
|
|
364
|
+
const stored = !!readFromStore(config, store, deps);
|
|
365
|
+
return {
|
|
366
|
+
configured: true,
|
|
367
|
+
stored,
|
|
368
|
+
store,
|
|
369
|
+
fix: stored
|
|
370
|
+
? "Vault unlock secret is available on this machine."
|
|
371
|
+
: lockedMessage(config, store),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
return {
|
|
376
|
+
configured: false,
|
|
377
|
+
stored: false,
|
|
378
|
+
error: error instanceof Error ? error.message : String(error),
|
|
379
|
+
fix: error instanceof Error ? error.message : String(error),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Claude Code lifecycle hook handler.
|
|
3
|
+
// Receives events from Claude Code's hooks system (SessionStart, Stop, PostToolUse)
|
|
4
|
+
// and forwards them to the Ouroboros daemon for agent awareness.
|
|
5
|
+
//
|
|
6
|
+
// This module exports handleHookEvent for testability.
|
|
7
|
+
// The actual hook scripts (scripts/claude-code-hook.js) read stdin and call this.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.handleHookEvent = handleHookEvent;
|
|
10
|
+
const socket_client_1 = require("../heart/daemon/socket-client");
|
|
11
|
+
const runtime_1 = require("../nerves/runtime");
|
|
12
|
+
/**
|
|
13
|
+
* Handle a Claude Code lifecycle hook event.
|
|
14
|
+
* Sends the event to the daemon and always exits 0 (hooks must not block the IDE).
|
|
15
|
+
*/
|
|
16
|
+
async function handleHookEvent(hookEvent) {
|
|
17
|
+
(0, runtime_1.emitNervesEvent)({
|
|
18
|
+
component: "daemon",
|
|
19
|
+
event: "daemon.hook_event_received",
|
|
20
|
+
message: "claude code hook event received",
|
|
21
|
+
meta: { hookEvent: hookEvent.event, sessionId: hookEvent.sessionId },
|
|
22
|
+
});
|
|
23
|
+
try {
|
|
24
|
+
await (0, socket_client_1.sendDaemonCommand)(socket_client_1.DEFAULT_DAEMON_SOCKET_PATH, {
|
|
25
|
+
kind: "hook.event",
|
|
26
|
+
event: hookEvent.event,
|
|
27
|
+
sessionId: hookEvent.sessionId,
|
|
28
|
+
toolName: hookEvent.toolName,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Daemon unavailable — silently ignore. Hooks must not block.
|
|
33
|
+
(0, runtime_1.emitNervesEvent)({
|
|
34
|
+
component: "daemon",
|
|
35
|
+
event: "daemon.hook_event_daemon_unavailable",
|
|
36
|
+
message: "daemon unavailable for hook event",
|
|
37
|
+
meta: { hookEvent: hookEvent.event },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return { exitCode: 0 };
|
|
41
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Claude Code stop hook — checks the agent's pending queue and returns
|
|
3
|
+
// any messages as additionalContext for injection into the next turn.
|
|
4
|
+
//
|
|
5
|
+
// This is how the agent can proactively communicate back to the dev tool user:
|
|
6
|
+
// the agent surfaces a message to the pending queue, and the stop hook picks
|
|
7
|
+
// it up and injects it as context in the Claude Code session.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.handleStopHook = handleStopHook;
|
|
10
|
+
const pending_1 = require("../mind/pending");
|
|
11
|
+
const runtime_1 = require("../nerves/runtime");
|
|
12
|
+
/**
|
|
13
|
+
* Check the pending queue for messages from the agent to this dev tool session.
|
|
14
|
+
* Returns accumulated message text as additionalContext.
|
|
15
|
+
*/
|
|
16
|
+
async function handleStopHook(input) {
|
|
17
|
+
(0, runtime_1.emitNervesEvent)({
|
|
18
|
+
component: "daemon",
|
|
19
|
+
event: "daemon.stop_hook_check_start",
|
|
20
|
+
message: "checking pending queue for stop hook",
|
|
21
|
+
meta: { agentName: input.agentName, friendId: input.friendId, sessionId: input.sessionId },
|
|
22
|
+
});
|
|
23
|
+
try {
|
|
24
|
+
const pendingDir = (0, pending_1.getPendingDir)(input.agentName, input.friendId, "mcp", input.sessionId);
|
|
25
|
+
const pending = (0, pending_1.drainPending)(pendingDir);
|
|
26
|
+
if (pending.length === 0) {
|
|
27
|
+
return { additionalContext: "" };
|
|
28
|
+
}
|
|
29
|
+
const text = pending.map((m) => m.content).join("\n\n---\n\n");
|
|
30
|
+
(0, runtime_1.emitNervesEvent)({
|
|
31
|
+
component: "daemon",
|
|
32
|
+
event: "daemon.stop_hook_check_end",
|
|
33
|
+
message: "pending messages found for stop hook",
|
|
34
|
+
meta: { agentName: input.agentName, count: pending.length },
|
|
35
|
+
});
|
|
36
|
+
return { additionalContext: text };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
(0, runtime_1.emitNervesEvent)({
|
|
40
|
+
component: "daemon",
|
|
41
|
+
event: "daemon.stop_hook_check_error",
|
|
42
|
+
message: "error checking pending queue in stop hook",
|
|
43
|
+
meta: { agentName: input.agentName },
|
|
44
|
+
});
|
|
45
|
+
return { additionalContext: "" };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAttentionQueue = buildAttentionQueue;
|
|
4
|
+
exports.dequeueAttentionItem = dequeueAttentionItem;
|
|
5
|
+
exports.attentionQueueEmpty = attentionQueueEmpty;
|
|
6
|
+
exports.buildAttentionQueueSummary = buildAttentionQueueSummary;
|
|
7
|
+
const runtime_1 = require("../nerves/runtime");
|
|
8
|
+
// ── Queue construction ───────────────────────────────────────────
|
|
9
|
+
function generateItemId() {
|
|
10
|
+
return Math.random().toString(36).slice(2, 10);
|
|
11
|
+
}
|
|
12
|
+
function originKey(friendId, channel, key) {
|
|
13
|
+
return `${friendId}/${channel}/${key}`;
|
|
14
|
+
}
|
|
15
|
+
function buildAttentionQueue(input) {
|
|
16
|
+
const { drainedPending, outstandingObligations, friendNameResolver, packetResolver } = input;
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
const items = [];
|
|
19
|
+
const enrichPacket = (packetId) => {
|
|
20
|
+
if (!packetId || !packetResolver)
|
|
21
|
+
return {};
|
|
22
|
+
const packet = packetResolver(packetId);
|
|
23
|
+
if (!packet)
|
|
24
|
+
return { packetId };
|
|
25
|
+
return {
|
|
26
|
+
packetId,
|
|
27
|
+
packetKind: packet.kind,
|
|
28
|
+
packetObjective: packet.objective,
|
|
29
|
+
packetSummary: packet.summary,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
// Source 1: drained pending messages with delegatedFrom (current-turn delegations)
|
|
33
|
+
for (const msg of drainedPending) {
|
|
34
|
+
if (!msg.delegatedFrom)
|
|
35
|
+
continue;
|
|
36
|
+
const { friendId, channel, key, bridgeId } = msg.delegatedFrom;
|
|
37
|
+
const oKey = originKey(friendId, channel, key);
|
|
38
|
+
seen.add(oKey);
|
|
39
|
+
const resolvedName = friendNameResolver(friendId);
|
|
40
|
+
items.push({
|
|
41
|
+
id: msg.obligationId ?? generateItemId(),
|
|
42
|
+
friendId,
|
|
43
|
+
friendName: resolvedName ?? friendId,
|
|
44
|
+
channel,
|
|
45
|
+
key,
|
|
46
|
+
...(bridgeId ? { bridgeId } : {}),
|
|
47
|
+
delegatedContent: msg.content,
|
|
48
|
+
...(msg.obligationId ? { obligationId: msg.obligationId } : {}),
|
|
49
|
+
...enrichPacket(msg.packetId),
|
|
50
|
+
source: "drained",
|
|
51
|
+
timestamp: msg.timestamp,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Source 2: outstanding obligations (crash recovery)
|
|
55
|
+
for (const obligation of outstandingObligations) {
|
|
56
|
+
const { friendId, channel, key, bridgeId } = obligation.origin;
|
|
57
|
+
const oKey = originKey(friendId, channel, key);
|
|
58
|
+
if (seen.has(oKey))
|
|
59
|
+
continue; // deduplicate: prefer drained version
|
|
60
|
+
seen.add(oKey);
|
|
61
|
+
const resolvedName = friendNameResolver(friendId);
|
|
62
|
+
items.push({
|
|
63
|
+
id: obligation.id,
|
|
64
|
+
friendId,
|
|
65
|
+
friendName: resolvedName ?? friendId,
|
|
66
|
+
channel,
|
|
67
|
+
key,
|
|
68
|
+
...(bridgeId ? { bridgeId } : {}),
|
|
69
|
+
delegatedContent: obligation.delegatedContent,
|
|
70
|
+
obligationId: obligation.id,
|
|
71
|
+
...enrichPacket(obligation.packetId),
|
|
72
|
+
source: "obligation-recovery",
|
|
73
|
+
timestamp: obligation.createdAt,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Sort FIFO (oldest first)
|
|
77
|
+
items.sort((a, b) => a.timestamp - b.timestamp);
|
|
78
|
+
(0, runtime_1.emitNervesEvent)({
|
|
79
|
+
event: "senses.attention_queue_built",
|
|
80
|
+
component: "senses",
|
|
81
|
+
message: `attention queue built with ${items.length} item(s)`,
|
|
82
|
+
meta: {
|
|
83
|
+
drainedCount: items.filter((i) => i.source === "drained").length,
|
|
84
|
+
recoveredCount: items.filter((i) => i.source === "obligation-recovery").length,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
return items;
|
|
88
|
+
}
|
|
89
|
+
// ── Queue operations ─────────────────────────────────────────────
|
|
90
|
+
function dequeueAttentionItem(queue, id) {
|
|
91
|
+
const index = queue.findIndex((item) => item.id === id);
|
|
92
|
+
if (index === -1)
|
|
93
|
+
return null;
|
|
94
|
+
return queue.splice(index, 1)[0];
|
|
95
|
+
}
|
|
96
|
+
function attentionQueueEmpty(queue) {
|
|
97
|
+
return queue.length === 0;
|
|
98
|
+
}
|
|
99
|
+
// ── Queue visibility ─────────────────────────────────────────────
|
|
100
|
+
const CONTENT_PREVIEW_MAX = 80;
|
|
101
|
+
function buildAttentionQueueSummary(queue) {
|
|
102
|
+
if (queue.length === 0)
|
|
103
|
+
return "";
|
|
104
|
+
const lines = ["[internal: held work items — not messages to send]"];
|
|
105
|
+
for (const item of queue) {
|
|
106
|
+
if (item.packetKind && item.packetObjective) {
|
|
107
|
+
lines.push(`- [${item.id}] ${item.friendName} -> ${item.packetKind}: ${item.packetObjective}`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const preview = item.delegatedContent.length > CONTENT_PREVIEW_MAX
|
|
111
|
+
? `${item.delegatedContent.slice(0, CONTENT_PREVIEW_MAX - 3)}...`
|
|
112
|
+
: item.delegatedContent;
|
|
113
|
+
lines.push(`- [${item.id}] ${item.friendName} asked: "${preview}"`);
|
|
114
|
+
}
|
|
115
|
+
return lines.join("\n");
|
|
116
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cacheBlueBubblesAttachment = cacheBlueBubblesAttachment;
|
|
4
|
+
exports.lookupBlueBubblesAttachment = lookupBlueBubblesAttachment;
|
|
5
|
+
exports.resetBlueBubblesAttachmentCache = resetBlueBubblesAttachmentCache;
|
|
6
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
7
|
+
/**
|
|
8
|
+
* Bounded process-local cache of recently-seen BlueBubbles attachment summaries.
|
|
9
|
+
*
|
|
10
|
+
* Populated at attachment-hydration time so the `describe_image` tool can
|
|
11
|
+
* look up a guid → summary later in the same turn (or a few turns later)
|
|
12
|
+
* and re-download the bytes on demand. Intentionally NOT persisted — per
|
|
13
|
+
* planning doc D4, session storage holds only the VLM description text,
|
|
14
|
+
* never the raw image bytes. The cache is cleared on daemon restart, which
|
|
15
|
+
* matches the product expectation that "describe_image works on recent
|
|
16
|
+
* messages in this session".
|
|
17
|
+
*
|
|
18
|
+
* Bounded at MAX_CACHED_ATTACHMENTS entries; oldest entries evict first
|
|
19
|
+
* when the limit is hit.
|
|
20
|
+
*/
|
|
21
|
+
const MAX_CACHED_ATTACHMENTS = 50;
|
|
22
|
+
const cache = new Map();
|
|
23
|
+
function cacheBlueBubblesAttachment(summary) {
|
|
24
|
+
const guid = summary.guid?.trim();
|
|
25
|
+
if (!guid)
|
|
26
|
+
return;
|
|
27
|
+
// Re-insert to move to end (LRU behavior via Map insertion order).
|
|
28
|
+
if (cache.has(guid))
|
|
29
|
+
cache.delete(guid);
|
|
30
|
+
cache.set(guid, { ...summary });
|
|
31
|
+
while (cache.size > MAX_CACHED_ATTACHMENTS) {
|
|
32
|
+
// cache.size > 0 here, so keys().next().value is always defined.
|
|
33
|
+
const oldestKey = cache.keys().next().value;
|
|
34
|
+
cache.delete(oldestKey);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function lookupBlueBubblesAttachment(guid) {
|
|
38
|
+
const trimmed = guid?.trim();
|
|
39
|
+
if (!trimmed)
|
|
40
|
+
return undefined;
|
|
41
|
+
return cache.get(trimmed);
|
|
42
|
+
}
|
|
43
|
+
function resetBlueBubblesAttachmentCache() {
|
|
44
|
+
cache.clear();
|
|
45
|
+
}
|
|
46
|
+
/* v8 ignore start — module-level observability event */
|
|
47
|
+
(0, runtime_1.emitNervesEvent)({
|
|
48
|
+
component: "senses",
|
|
49
|
+
event: "senses.bluebubbles_attachment_cache_loaded",
|
|
50
|
+
message: "bluebubbles attachment cache module loaded",
|
|
51
|
+
meta: { maxEntries: MAX_CACHED_ATTACHMENTS },
|
|
52
|
+
});
|
|
53
|
+
/* v8 ignore stop */
|