@ouro.bot/cli 0.1.0-alpha.29 → 0.1.0-alpha.290
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 +188 -190
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
- package/changelog.json +1756 -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 +203 -0
- package/dist/arc/presence.js +185 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +37 -2
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +230 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +138 -0
- package/dist/heart/auth/auth-flow.js +456 -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 +147 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +63 -30
- package/dist/heart/core.js +669 -195
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +149 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +574 -0
- package/dist/heart/daemon/cli-exec.js +1932 -0
- package/dist/heart/daemon/cli-parse.js +762 -0
- package/dist/heart/daemon/cli-render.js +503 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +29 -1171
- package/dist/heart/daemon/daemon-entry.js +235 -3
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +706 -57
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +46 -9
- 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 +1 -1
- package/dist/heart/daemon/process-manager.js +180 -0
- package/dist/heart/daemon/pulse.js +463 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +101 -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 +72 -3
- 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/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -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/{daemon → hatch}/hatch-flow.js +30 -120
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
- package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
- package/dist/heart/identity.js +163 -60
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http.js +439 -0
- package/dist/heart/outlook/outlook-read.js +1618 -0
- package/dist/heart/outlook/outlook-render.js +1032 -0
- package/dist/heart/outlook/outlook-types.js +7 -0
- package/dist/heart/outlook/outlook-view.js +194 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +162 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +169 -46
- package/dist/heart/providers/azure.js +98 -11
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +136 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +23 -5
- package/dist/heart/providers/openai-codex.js +33 -22
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/start-of-turn-packet.js +339 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/sync.js +277 -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 +358 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mind/associative-recall.js +126 -66
- package/dist/mind/bundle-manifest.js +8 -1
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +68 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +14 -1
- package/dist/mind/friends/channel.js +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +37 -0
- package/dist/mind/friends/store-file.js +58 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/prompt.js +885 -109
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit.js +1 -1
- package/dist/nerves/coverage/file-completeness.js +56 -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-Ck8agNeO.js +61 -0
- package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +319 -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 +197 -30
- package/dist/repertoire/coding/manager.js +149 -2
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +515 -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 +375 -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 +14 -23
- 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 +11 -28
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +316 -0
- package/dist/repertoire/tools-base.js +45 -771
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +978 -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-memory.js +347 -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 +12 -62
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +110 -0
- package/dist/repertoire/tools.js +148 -130
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +241 -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 → bluebubbles/client.js} +143 -9
- package/dist/senses/bluebubbles/entry.js +13 -0
- package/dist/senses/bluebubbles/inbound-log.js +110 -0
- package/dist/senses/bluebubbles/index.js +1410 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +122 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +29 -16
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
- 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 +595 -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 +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +588 -246
- package/dist/senses/commands.js +65 -1
- 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 +633 -86
- package/dist/senses/pipeline.js +560 -0
- package/dist/senses/shared-turn.js +195 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams.js +664 -165
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +14 -7
- 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 +81 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -421
- package/dist/senses/debug-activity.js +0 -108
- package/subagents/README.md +0 -73
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /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/monty.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-serpent.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -33,15 +33,32 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.downloadBlueBubblesAttachment = exports.VLM_TEXT_WRAPPERS = void 0;
|
|
36
37
|
exports.hydrateBlueBubblesAttachments = hydrateBlueBubblesAttachments;
|
|
37
38
|
const node_child_process_1 = require("node:child_process");
|
|
38
39
|
const fs = __importStar(require("node:fs/promises"));
|
|
39
40
|
const os = __importStar(require("node:os"));
|
|
40
41
|
const path = __importStar(require("node:path"));
|
|
41
|
-
const runtime_1 = require("
|
|
42
|
-
const
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const identity_1 = require("../../heart/identity");
|
|
44
|
+
const model_capabilities_1 = require("../../heart/model-capabilities");
|
|
45
|
+
const image_normalize_1 = require("../../heart/attachments/image-normalize");
|
|
46
|
+
const store_1 = require("../../heart/attachments/store");
|
|
47
|
+
const types_1 = require("../../heart/attachments/types");
|
|
48
|
+
const materialize_1 = require("../../heart/attachments/materialize");
|
|
49
|
+
const attachment_download_1 = require("./attachment-download");
|
|
50
|
+
// Pin the exact wrapper strings so tests and code read from one source.
|
|
51
|
+
exports.VLM_TEXT_WRAPPERS = {
|
|
52
|
+
description: (desc) => `[image description: ${desc}]`,
|
|
53
|
+
failure: (reason) => `[image description failed: ${reason}]`,
|
|
54
|
+
};
|
|
55
|
+
// D3 prompt template. Included here (not templated out) because the contract
|
|
56
|
+
// is load-bearing — see planning doc section D3 and AX-4.
|
|
57
|
+
function buildVlmPrompt(userText) {
|
|
58
|
+
const body = userText ?? "";
|
|
59
|
+
return `User message: "${body}"\n\nDescribe this image in detail, focusing on anything relevant to what the user said above.\nInclude any text visible in the image verbatim.`;
|
|
60
|
+
}
|
|
43
61
|
const AUDIO_EXTENSIONS = new Set([".mp3", ".wav", ".m4a", ".caf", ".ogg"]);
|
|
44
|
-
const IMAGE_EXTENSIONS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".heic", ".heif"]);
|
|
45
62
|
const AUDIO_EXTENSION_BY_CONTENT_TYPE = {
|
|
46
63
|
"audio/wav": ".wav",
|
|
47
64
|
"audio/x-wav": ".wav",
|
|
@@ -65,31 +82,19 @@ const AUDIO_INPUT_FORMAT_BY_EXTENSION = {
|
|
|
65
82
|
const WHISPER_CPP_FORMULA = "whisper-cpp";
|
|
66
83
|
const WHISPER_CPP_MODEL_NAME = "ggml-base.en.bin";
|
|
67
84
|
const WHISPER_CPP_MODEL_URL = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${WHISPER_CPP_MODEL_NAME}`;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
// Lazy — getAgentToolsRoot() requires identity to be resolved, which isn't
|
|
86
|
+
// true at module-load time in most unit test contexts. Tests that only touch
|
|
87
|
+
// the image/VLM path shouldn't break because the audio path asked for a
|
|
88
|
+
// tools root they don't need.
|
|
89
|
+
function whisperCppPaths() {
|
|
90
|
+
const toolsDir = path.join((0, identity_1.getAgentToolsRoot)(), "whisper-cpp");
|
|
91
|
+
const modelsDir = path.join(toolsDir, "models");
|
|
92
|
+
const modelPath = path.join(modelsDir, WHISPER_CPP_MODEL_NAME);
|
|
93
|
+
return { toolsDir, modelsDir, modelPath };
|
|
76
94
|
}
|
|
77
95
|
function describeAttachment(attachment) {
|
|
78
96
|
return attachment.transferName?.trim() || attachment.guid?.trim() || "attachment";
|
|
79
97
|
}
|
|
80
|
-
function inferContentType(attachment, responseType) {
|
|
81
|
-
const normalizedResponseType = responseType?.split(";")[0]?.trim().toLowerCase();
|
|
82
|
-
if (normalizedResponseType) {
|
|
83
|
-
return normalizedResponseType;
|
|
84
|
-
}
|
|
85
|
-
return attachment.mimeType?.trim().toLowerCase() || undefined;
|
|
86
|
-
}
|
|
87
|
-
function isImageAttachment(attachment, contentType) {
|
|
88
|
-
if (contentType?.startsWith("image/"))
|
|
89
|
-
return true;
|
|
90
|
-
const extension = path.extname(attachment.transferName ?? "").toLowerCase();
|
|
91
|
-
return IMAGE_EXTENSIONS.has(extension);
|
|
92
|
-
}
|
|
93
98
|
function isAudioAttachment(attachment, contentType) {
|
|
94
99
|
if (contentType?.startsWith("audio/"))
|
|
95
100
|
return true;
|
|
@@ -169,12 +174,13 @@ async function resolveWhisperCppBinary(timeoutMs) {
|
|
|
169
174
|
return candidate;
|
|
170
175
|
}
|
|
171
176
|
async function ensureWhisperCppModel(timeoutMs, fetchImpl) {
|
|
177
|
+
const { modelsDir, modelPath } = whisperCppPaths();
|
|
172
178
|
try {
|
|
173
|
-
await fs.access(
|
|
174
|
-
return
|
|
179
|
+
await fs.access(modelPath);
|
|
180
|
+
return modelPath;
|
|
175
181
|
}
|
|
176
182
|
catch {
|
|
177
|
-
await fs.mkdir(
|
|
183
|
+
await fs.mkdir(modelsDir, { recursive: true });
|
|
178
184
|
const response = await fetchImpl(WHISPER_CPP_MODEL_URL, {
|
|
179
185
|
method: "GET",
|
|
180
186
|
signal: AbortSignal.timeout(Math.max(timeoutMs, 300_000)),
|
|
@@ -182,8 +188,8 @@ async function ensureWhisperCppModel(timeoutMs, fetchImpl) {
|
|
|
182
188
|
if (!response.ok) {
|
|
183
189
|
throw new Error(`failed to download whisper.cpp model: HTTP ${response.status}`);
|
|
184
190
|
}
|
|
185
|
-
await fs.writeFile(
|
|
186
|
-
return
|
|
191
|
+
await fs.writeFile(modelPath, Buffer.from(await response.arrayBuffer()));
|
|
192
|
+
return modelPath;
|
|
187
193
|
}
|
|
188
194
|
}
|
|
189
195
|
async function convertAudioForWhisperCpp(sourcePath, outputPath, timeoutMs) {
|
|
@@ -235,59 +241,105 @@ async function transcribeAudioWithWhisperCpp(params, modelFetchImpl = fetch) {
|
|
|
235
241
|
await fs.rm(workDir, { recursive: true, force: true }).catch(() => undefined);
|
|
236
242
|
}
|
|
237
243
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (!guid) {
|
|
241
|
-
throw new Error("attachment guid missing");
|
|
242
|
-
}
|
|
243
|
-
if (typeof attachment.totalBytes === "number" && attachment.totalBytes > MAX_ATTACHMENT_BYTES) {
|
|
244
|
-
throw new Error(`attachment exceeds ${MAX_ATTACHMENT_BYTES} byte limit`);
|
|
245
|
-
}
|
|
246
|
-
const url = buildBlueBubblesApiUrl(config.serverUrl, `/api/v1/attachment/${encodeURIComponent(guid)}/download`, config.password);
|
|
247
|
-
const response = await fetchImpl(url, {
|
|
248
|
-
method: "GET",
|
|
249
|
-
signal: AbortSignal.timeout(channelConfig.requestTimeoutMs),
|
|
250
|
-
});
|
|
251
|
-
if (!response.ok) {
|
|
252
|
-
throw new Error(`HTTP ${response.status}`);
|
|
253
|
-
}
|
|
254
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
255
|
-
if (buffer.length > MAX_ATTACHMENT_BYTES) {
|
|
256
|
-
throw new Error(`attachment exceeds ${MAX_ATTACHMENT_BYTES} byte limit`);
|
|
257
|
-
}
|
|
258
|
-
return {
|
|
259
|
-
buffer,
|
|
260
|
-
contentType: inferContentType(attachment, response.headers.get("content-type")),
|
|
261
|
-
};
|
|
262
|
-
}
|
|
244
|
+
var attachment_download_2 = require("./attachment-download");
|
|
245
|
+
Object.defineProperty(exports, "downloadBlueBubblesAttachment", { enumerable: true, get: function () { return attachment_download_2.downloadBlueBubblesAttachment; } });
|
|
263
246
|
async function hydrateBlueBubblesAttachments(attachments, config, channelConfig, deps = {}) {
|
|
264
|
-
(0, runtime_1.emitNervesEvent)({
|
|
265
|
-
component: "senses",
|
|
266
|
-
event: "senses.bluebubbles_media_hydrate",
|
|
267
|
-
message: "hydrating bluebubbles attachments",
|
|
268
|
-
meta: {
|
|
269
|
-
attachmentCount: attachments.length,
|
|
270
|
-
preferAudioInput: deps.preferAudioInput ?? false,
|
|
271
|
-
},
|
|
272
|
-
});
|
|
273
247
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
274
248
|
const modelFetchImpl = deps.modelFetchImpl ?? fetch;
|
|
275
249
|
const transcribeAudio = deps.transcribeAudio ?? ((params) => transcribeAudioWithWhisperCpp(params, modelFetchImpl));
|
|
276
250
|
const preferAudioInput = deps.preferAudioInput ?? false;
|
|
251
|
+
const chatModel = deps.chatModel;
|
|
252
|
+
const visionCapable = chatModel ? (0, model_capabilities_1.getModelCapabilities)(chatModel).vision === true : true;
|
|
253
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
254
|
+
const agentRoot = (0, identity_1.getAgentRoot)(agentName);
|
|
277
255
|
const inputParts = [];
|
|
278
256
|
const transcriptAdditions = [];
|
|
279
257
|
const notices = [];
|
|
280
258
|
for (const attachment of attachments) {
|
|
281
259
|
const name = describeAttachment(attachment);
|
|
260
|
+
const initialRecord = attachment.guid?.trim()
|
|
261
|
+
? (0, store_1.rememberRecentAttachment)(agentName, (0, types_1.buildBlueBubblesAttachmentRecord)(attachment), agentRoot)
|
|
262
|
+
: null;
|
|
282
263
|
try {
|
|
283
|
-
const downloaded = await
|
|
264
|
+
const downloaded = await (0, attachment_download_1.downloadBlueBubblesAttachment)(attachment, config, channelConfig, fetchImpl);
|
|
284
265
|
const base64 = downloaded.buffer.toString("base64");
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
266
|
+
const byteCount = downloaded.buffer.length;
|
|
267
|
+
const persistedRecord = initialRecord && initialRecord.source === "bluebubbles"
|
|
268
|
+
? await (0, materialize_1.persistBlueBubblesAttachmentSource)(agentName, initialRecord, {
|
|
269
|
+
buffer: downloaded.buffer,
|
|
270
|
+
mimeType: downloaded.contentType,
|
|
271
|
+
byteCount,
|
|
272
|
+
}, agentRoot)
|
|
273
|
+
: null;
|
|
274
|
+
if ((0, attachment_download_1.isBlueBubblesImageAttachment)(attachment, downloaded.contentType)) {
|
|
275
|
+
const mimeType = downloaded.contentType ?? "application/octet-stream";
|
|
276
|
+
if (visionCapable) {
|
|
277
|
+
inputParts.push({
|
|
278
|
+
type: "image_url",
|
|
279
|
+
image_url: {
|
|
280
|
+
url: `data:${mimeType};base64,${base64}`,
|
|
281
|
+
detail: "auto",
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
(0, runtime_1.emitNervesEvent)({
|
|
285
|
+
level: "warn",
|
|
286
|
+
component: "senses",
|
|
287
|
+
event: "senses.bluebubbles_media_hydrate",
|
|
288
|
+
message: "bluebubbles media hydrate",
|
|
289
|
+
meta: {
|
|
290
|
+
attachmentGuid: attachment.guid,
|
|
291
|
+
mimeType,
|
|
292
|
+
byteCount,
|
|
293
|
+
hydrationPath: "native-passthrough",
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
if (!deps.vlmDescribe) {
|
|
300
|
+
throw new Error("no VLM describer configured — wire a vlmDescribe dep or configure a vision-capable chat model");
|
|
301
|
+
}
|
|
302
|
+
if (!persistedRecord?.sourceData.localPath) {
|
|
303
|
+
throw new Error("image attachment could not be persisted for normalization");
|
|
304
|
+
}
|
|
305
|
+
const normalized = await (0, image_normalize_1.normalizeImageForVision)({
|
|
306
|
+
attachment: persistedRecord,
|
|
307
|
+
sourcePath: persistedRecord.sourceData.localPath,
|
|
308
|
+
agentName,
|
|
309
|
+
agentRoot,
|
|
310
|
+
});
|
|
311
|
+
const normalizedBuffer = await fs.readFile(normalized.path);
|
|
312
|
+
const normalizedMime = normalized.mimeType ?? persistedRecord.mimeType ?? "image/jpeg";
|
|
313
|
+
const dataUrl = `data:${normalizedMime};base64,${normalizedBuffer.toString("base64")}`;
|
|
314
|
+
const description = await deps.vlmDescribe({
|
|
315
|
+
prompt: buildVlmPrompt(deps.userText),
|
|
316
|
+
imageDataUrl: dataUrl,
|
|
317
|
+
attachmentGuid: attachment.guid,
|
|
318
|
+
mimeType: normalizedMime,
|
|
319
|
+
chatModel,
|
|
320
|
+
});
|
|
321
|
+
inputParts.push({
|
|
322
|
+
type: "text",
|
|
323
|
+
text: exports.VLM_TEXT_WRAPPERS.description(description),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
catch (vlmError) {
|
|
327
|
+
const reason = vlmError instanceof Error ? vlmError.message : String(vlmError);
|
|
328
|
+
inputParts.push({
|
|
329
|
+
type: "text",
|
|
330
|
+
text: exports.VLM_TEXT_WRAPPERS.failure(reason),
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
(0, runtime_1.emitNervesEvent)({
|
|
334
|
+
level: "warn",
|
|
335
|
+
component: "senses",
|
|
336
|
+
event: "senses.bluebubbles_media_hydrate",
|
|
337
|
+
message: "bluebubbles media hydrate",
|
|
338
|
+
meta: {
|
|
339
|
+
attachmentGuid: attachment.guid,
|
|
340
|
+
mimeType: persistedRecord?.mimeType ?? mimeType,
|
|
341
|
+
byteCount,
|
|
342
|
+
hydrationPath: "vlm-describe",
|
|
291
343
|
},
|
|
292
344
|
});
|
|
293
345
|
continue;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.normalizeBlueBubblesEvent = normalizeBlueBubblesEvent;
|
|
4
|
-
const runtime_1 = require("
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
const render_1 = require("../../heart/attachments/render");
|
|
6
|
+
const types_1 = require("../../heart/attachments/types");
|
|
5
7
|
function asRecord(value) {
|
|
6
8
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
7
9
|
? value
|
|
@@ -39,6 +41,7 @@ function extractChatIdentifierFromGuid(chatGuid) {
|
|
|
39
41
|
return parts.length >= 3 ? parts[2]?.trim() || undefined : undefined;
|
|
40
42
|
}
|
|
41
43
|
function buildChatRef(data, threadOriginatorGuid) {
|
|
44
|
+
void threadOriginatorGuid;
|
|
42
45
|
const chats = Array.isArray(data.chats) ? data.chats : [];
|
|
43
46
|
const chat = asRecord(chats[0]) ?? null;
|
|
44
47
|
const chatGuid = readString(chat, "guid");
|
|
@@ -48,12 +51,18 @@ function buildChatRef(data, threadOriginatorGuid) {
|
|
|
48
51
|
const displayName = readString(chat, "displayName")?.trim() || undefined;
|
|
49
52
|
const style = readNumber(chat, "style");
|
|
50
53
|
const isGroup = style === 43 || (chatGuid?.includes(";+;") ?? false) || Boolean(displayName);
|
|
51
|
-
const
|
|
54
|
+
const sessionKey = chatGuid?.trim()
|
|
52
55
|
? `chat:${chatGuid.trim()}`
|
|
53
56
|
: `chat_identifier:${(chatIdentifier ?? "unknown").trim()}`;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
// Extract participant handles from chat.participants (when available from BB API)
|
|
58
|
+
const rawParticipants = Array.isArray(chat?.participants) ? chat.participants : [];
|
|
59
|
+
const participantHandles = rawParticipants
|
|
60
|
+
.map((p) => {
|
|
61
|
+
const rec = asRecord(p);
|
|
62
|
+
const addr = readString(rec, "address") ?? readString(rec, "id");
|
|
63
|
+
return addr ? normalizeHandle(addr) : "";
|
|
64
|
+
})
|
|
65
|
+
.filter(Boolean);
|
|
57
66
|
return {
|
|
58
67
|
chatGuid: chatGuid?.trim() || undefined,
|
|
59
68
|
chatIdentifier: chatIdentifier?.trim() || undefined,
|
|
@@ -63,6 +72,7 @@ function buildChatRef(data, threadOriginatorGuid) {
|
|
|
63
72
|
sendTarget: chatGuid?.trim()
|
|
64
73
|
? { kind: "chat_guid", value: chatGuid.trim() }
|
|
65
74
|
: { kind: "chat_identifier", value: (chatIdentifier ?? "unknown").trim() },
|
|
75
|
+
participantHandles,
|
|
66
76
|
};
|
|
67
77
|
}
|
|
68
78
|
function extractSender(data, chat) {
|
|
@@ -99,18 +109,14 @@ function extractAttachments(data) {
|
|
|
99
109
|
function formatAttachmentText(attachments) {
|
|
100
110
|
if (attachments.length === 0)
|
|
101
111
|
return "";
|
|
112
|
+
const renderable = attachments
|
|
113
|
+
.filter((attachment) => typeof attachment.guid === "string" && attachment.guid.trim().length > 0)
|
|
114
|
+
.map((attachment) => (0, types_1.buildBlueBubblesAttachmentRecord)(attachment));
|
|
115
|
+
if (renderable.length > 0) {
|
|
116
|
+
return (0, render_1.renderAttachmentBlock)(renderable);
|
|
117
|
+
}
|
|
102
118
|
const [first] = attachments;
|
|
103
|
-
|
|
104
|
-
const label = mime.startsWith("image/")
|
|
105
|
-
? "image attachment"
|
|
106
|
-
: mime.startsWith("audio/")
|
|
107
|
-
? "audio attachment"
|
|
108
|
-
: "attachment";
|
|
109
|
-
const name = first.transferName ? `: ${first.transferName}` : "";
|
|
110
|
-
const dimensions = typeof first.width === "number" && typeof first.height === "number" && first.width > 0 && first.height > 0
|
|
111
|
-
? ` (${first.width}x${first.height})`
|
|
112
|
-
: "";
|
|
113
|
-
return `[${label}${name}${dimensions}]`;
|
|
119
|
+
return `[attachment: ${first.transferName?.trim() || "unknown"}]`;
|
|
114
120
|
}
|
|
115
121
|
function formatMessageText(data, attachments) {
|
|
116
122
|
const text = readString(data, "text")?.trim() ?? "";
|
|
@@ -119,6 +125,13 @@ function formatMessageText(data, attachments) {
|
|
|
119
125
|
if (balloonBundleId === "com.apple.messages.URLBalloonProvider") {
|
|
120
126
|
return `${text}\n[link preview attached]`;
|
|
121
127
|
}
|
|
128
|
+
// B2 fix: when text and attachments both exist, append the attachment
|
|
129
|
+
// marker so downstream senses can see the guid/filename. Previously the
|
|
130
|
+
// marker was dropped whenever text was non-empty, which hid images from
|
|
131
|
+
// the agent when the user captioned a screenshot.
|
|
132
|
+
if (attachments.length > 0) {
|
|
133
|
+
return `${text}\n${formatAttachmentText(attachments)}`;
|
|
134
|
+
}
|
|
122
135
|
return text;
|
|
123
136
|
}
|
|
124
137
|
return formatAttachmentText(attachments);
|
|
@@ -35,15 +35,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getBlueBubblesMutationLogPath = getBlueBubblesMutationLogPath;
|
|
37
37
|
exports.recordBlueBubblesMutation = recordBlueBubblesMutation;
|
|
38
|
+
exports.listBlueBubblesRecoveryCandidates = listBlueBubblesRecoveryCandidates;
|
|
38
39
|
const fs = __importStar(require("node:fs"));
|
|
39
|
-
const os = __importStar(require("node:os"));
|
|
40
40
|
const path = __importStar(require("node:path"));
|
|
41
|
-
const runtime_1 = require("
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
41
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
+
const identity_1 = require("../../heart/identity");
|
|
43
|
+
const config_1 = require("../../heart/config");
|
|
45
44
|
function getBlueBubblesMutationLogPath(agentName, sessionKey) {
|
|
46
|
-
return path.join(
|
|
45
|
+
return path.join((0, identity_1.getAgentRoot)(agentName), "state", "senses", "bluebubbles", "mutations", `${(0, config_1.sanitizeKey)(sessionKey)}.ndjson`);
|
|
47
46
|
}
|
|
48
47
|
function recordBlueBubblesMutation(agentName, event) {
|
|
49
48
|
const filePath = getBlueBubblesMutationLogPath(agentName, event.chat.sessionKey);
|
|
@@ -74,3 +73,44 @@ function recordBlueBubblesMutation(agentName, event) {
|
|
|
74
73
|
});
|
|
75
74
|
return filePath;
|
|
76
75
|
}
|
|
76
|
+
function listBlueBubblesRecoveryCandidates(agentName) {
|
|
77
|
+
const rootDir = path.join((0, identity_1.getAgentRoot)(agentName), "state", "senses", "bluebubbles", "mutations");
|
|
78
|
+
let files;
|
|
79
|
+
try {
|
|
80
|
+
files = fs.readdirSync(rootDir);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
const deduped = new Map();
|
|
86
|
+
for (const file of files.filter((entry) => entry.endsWith(".ndjson")).sort()) {
|
|
87
|
+
const filePath = path.join(rootDir, file);
|
|
88
|
+
let raw = "";
|
|
89
|
+
try {
|
|
90
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const line of raw.split("\n")) {
|
|
96
|
+
const trimmed = line.trim();
|
|
97
|
+
if (!trimmed)
|
|
98
|
+
continue;
|
|
99
|
+
try {
|
|
100
|
+
const entry = JSON.parse(trimmed);
|
|
101
|
+
if (typeof entry.messageGuid !== "string"
|
|
102
|
+
|| !entry.messageGuid.trim()
|
|
103
|
+
|| entry.fromMe
|
|
104
|
+
|| entry.shouldNotifyAgent
|
|
105
|
+
|| (entry.mutationType !== "read" && entry.mutationType !== "delivery")) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
deduped.set(entry.messageGuid, entry);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// ignore malformed recovery candidates
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return [...deduped.values()].sort((left, right) => left.recordedAt.localeCompare(right.recordedAt));
|
|
116
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
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.getBlueBubblesRuntimeStatePath = getBlueBubblesRuntimeStatePath;
|
|
37
|
+
exports.readBlueBubblesRuntimeState = readBlueBubblesRuntimeState;
|
|
38
|
+
exports.writeBlueBubblesRuntimeState = writeBlueBubblesRuntimeState;
|
|
39
|
+
const fs = __importStar(require("node:fs"));
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
const identity_1 = require("../../heart/identity");
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const DEFAULT_RUNTIME_STATE = {
|
|
44
|
+
upstreamStatus: "unknown",
|
|
45
|
+
detail: "startup health probe pending",
|
|
46
|
+
pendingRecoveryCount: 0,
|
|
47
|
+
};
|
|
48
|
+
function getBlueBubblesRuntimeStatePath(agentName, agentRoot = (0, identity_1.getAgentRoot)(agentName)) {
|
|
49
|
+
return path.join(agentRoot, "state", "senses", "bluebubbles", "runtime.json");
|
|
50
|
+
}
|
|
51
|
+
function readBlueBubblesRuntimeState(agentName, agentRoot) {
|
|
52
|
+
const filePath = getBlueBubblesRuntimeStatePath(agentName, agentRoot);
|
|
53
|
+
try {
|
|
54
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
return {
|
|
57
|
+
upstreamStatus: parsed.upstreamStatus === "ok" || parsed.upstreamStatus === "error"
|
|
58
|
+
? parsed.upstreamStatus
|
|
59
|
+
: "unknown",
|
|
60
|
+
detail: typeof parsed.detail === "string" && parsed.detail.trim()
|
|
61
|
+
? parsed.detail
|
|
62
|
+
: DEFAULT_RUNTIME_STATE.detail,
|
|
63
|
+
lastCheckedAt: typeof parsed.lastCheckedAt === "string" ? parsed.lastCheckedAt : undefined,
|
|
64
|
+
pendingRecoveryCount: typeof parsed.pendingRecoveryCount === "number" && Number.isFinite(parsed.pendingRecoveryCount)
|
|
65
|
+
? parsed.pendingRecoveryCount
|
|
66
|
+
: 0,
|
|
67
|
+
lastRecoveredAt: typeof parsed.lastRecoveredAt === "string" ? parsed.lastRecoveredAt : undefined,
|
|
68
|
+
lastRecoveredMessageGuid: typeof parsed.lastRecoveredMessageGuid === "string"
|
|
69
|
+
? parsed.lastRecoveredMessageGuid
|
|
70
|
+
: undefined,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return { ...DEFAULT_RUNTIME_STATE };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function writeBlueBubblesRuntimeState(agentName, state, agentRoot) {
|
|
78
|
+
const filePath = getBlueBubblesRuntimeStatePath(agentName, agentRoot);
|
|
79
|
+
try {
|
|
80
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
81
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
(0, runtime_1.emitNervesEvent)({
|
|
85
|
+
level: "warn",
|
|
86
|
+
component: "senses",
|
|
87
|
+
event: "senses.bluebubbles_runtime_state_error",
|
|
88
|
+
message: "failed to write bluebubbles runtime state",
|
|
89
|
+
meta: {
|
|
90
|
+
agentName,
|
|
91
|
+
upstreamStatus: state.upstreamStatus,
|
|
92
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
return filePath;
|
|
96
|
+
}
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
component: "senses",
|
|
99
|
+
event: "senses.bluebubbles_runtime_state_written",
|
|
100
|
+
message: "wrote bluebubbles runtime state",
|
|
101
|
+
meta: {
|
|
102
|
+
agentName,
|
|
103
|
+
upstreamStatus: state.upstreamStatus,
|
|
104
|
+
pendingRecoveryCount: state.pendingRecoveryCount,
|
|
105
|
+
path: filePath,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
return filePath;
|
|
109
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
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.findObsoleteBlueBubblesThreadSessions = findObsoleteBlueBubblesThreadSessions;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
40
|
+
function findObsoleteBlueBubblesThreadSessions(trunkSessionPath) {
|
|
41
|
+
const normalized = trunkSessionPath.trim();
|
|
42
|
+
if (!normalized.endsWith(".json"))
|
|
43
|
+
return [];
|
|
44
|
+
const trunkName = path.basename(normalized);
|
|
45
|
+
if (trunkName.includes("_thread_"))
|
|
46
|
+
return [];
|
|
47
|
+
if (!fs.existsSync(normalized))
|
|
48
|
+
return [];
|
|
49
|
+
const dir = path.dirname(normalized);
|
|
50
|
+
const prefix = trunkName.slice(0, -".json".length);
|
|
51
|
+
const threadLaneFiles = [];
|
|
52
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
53
|
+
if (!entry.endsWith(".json"))
|
|
54
|
+
continue;
|
|
55
|
+
if (!entry.startsWith(`${prefix}_thread_`))
|
|
56
|
+
continue;
|
|
57
|
+
threadLaneFiles.push(path.join(dir, entry));
|
|
58
|
+
}
|
|
59
|
+
if (threadLaneFiles.length > 0) {
|
|
60
|
+
(0, runtime_1.emitNervesEvent)({
|
|
61
|
+
level: "warn",
|
|
62
|
+
component: "senses",
|
|
63
|
+
event: "senses.bluebubbles_thread_lane_artifacts_detected",
|
|
64
|
+
message: "detected obsolete bluebubbles thread-lane sessions",
|
|
65
|
+
meta: {
|
|
66
|
+
sessionPath: normalized,
|
|
67
|
+
artifactCount: threadLaneFiles.length,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return threadLaneFiles;
|
|
72
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BracketedPasteHandler = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Bracketed paste mode handler for the TUI.
|
|
6
|
+
*
|
|
7
|
+
* Detects \x1b[200~ (paste start) and \x1b[201~ (paste end) markers in
|
|
8
|
+
* raw stdin data. Buffers paste content between markers and emits it as
|
|
9
|
+
* a single "paste" event. Non-paste data is emitted as "data" unchanged.
|
|
10
|
+
*
|
|
11
|
+
* Works alongside Ink's stdin handling by intercepting raw data events.
|
|
12
|
+
*/
|
|
13
|
+
const events_1 = require("events");
|
|
14
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
15
|
+
const PASTE_START = "\x1b[200~";
|
|
16
|
+
const PASTE_END = "\x1b[201~";
|
|
17
|
+
class BracketedPasteHandler extends events_1.EventEmitter {
|
|
18
|
+
stdin;
|
|
19
|
+
stdout;
|
|
20
|
+
pasting = false;
|
|
21
|
+
buffer = "";
|
|
22
|
+
dataHandler;
|
|
23
|
+
constructor(stdin, stdout) {
|
|
24
|
+
super();
|
|
25
|
+
this.stdin = stdin;
|
|
26
|
+
this.stdout = stdout;
|
|
27
|
+
// Enable bracketed paste mode
|
|
28
|
+
stdout.write("\x1b[?2004h");
|
|
29
|
+
this.dataHandler = (chunk) => {
|
|
30
|
+
this.processChunk(chunk.toString());
|
|
31
|
+
};
|
|
32
|
+
stdin.prependListener("data", this.dataHandler);
|
|
33
|
+
}
|
|
34
|
+
processChunk(data) {
|
|
35
|
+
let remaining = data;
|
|
36
|
+
while (remaining.length > 0) {
|
|
37
|
+
if (this.pasting) {
|
|
38
|
+
// Look for end marker in buffer + remaining
|
|
39
|
+
const combined = this.buffer + remaining;
|
|
40
|
+
const endIdx = combined.indexOf(PASTE_END);
|
|
41
|
+
if (endIdx !== -1) {
|
|
42
|
+
// Found end marker -- emit paste content
|
|
43
|
+
const pasteContent = combined.slice(0, endIdx);
|
|
44
|
+
(0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.bracketed_paste", message: "bracketed paste received", meta: { length: pasteContent.length } });
|
|
45
|
+
this.emit("paste", pasteContent);
|
|
46
|
+
this.buffer = "";
|
|
47
|
+
this.pasting = false;
|
|
48
|
+
// Process remaining data after end marker
|
|
49
|
+
remaining = combined.slice(endIdx + PASTE_END.length);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// No end marker yet -- buffer everything so far
|
|
53
|
+
this.buffer = combined;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Not pasting -- look for start marker
|
|
57
|
+
const startIdx = remaining.indexOf(PASTE_START);
|
|
58
|
+
if (startIdx !== -1) {
|
|
59
|
+
// Emit any data before the start marker
|
|
60
|
+
const before = remaining.slice(0, startIdx);
|
|
61
|
+
if (before.length > 0) {
|
|
62
|
+
this.emit("data", before);
|
|
63
|
+
}
|
|
64
|
+
// Enter paste mode
|
|
65
|
+
this.pasting = true;
|
|
66
|
+
this.buffer = "";
|
|
67
|
+
remaining = remaining.slice(startIdx + PASTE_START.length);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// No start marker -- emit as regular data
|
|
71
|
+
this.emit("data", remaining);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Disable bracketed paste mode and remove listeners. */
|
|
76
|
+
destroy() {
|
|
77
|
+
this.stdout.write("\x1b[?2004l");
|
|
78
|
+
this.stdin.removeListener("data", this.dataHandler);
|
|
79
|
+
this.removeAllListeners();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.BracketedPasteHandler = BracketedPasteHandler;
|