@ouro.bot/cli 0.1.0-alpha.5 → 0.1.0-alpha.500
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 +226 -183
- 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 +3418 -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 +989 -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 +426 -0
- package/dist/heart/background-operations.js +281 -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 +193 -130
- package/dist/heart/core.js +1010 -261
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +490 -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 +216 -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 +640 -0
- package/dist/heart/daemon/cli-exec.js +7239 -0
- package/dist/heart/daemon/cli-help.js +493 -0
- package/dist/heart/daemon/cli-parse.js +1533 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +561 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/daemon-cli.js +30 -697
- package/dist/heart/daemon/daemon-entry.js +359 -8
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +268 -0
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +813 -19
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +615 -0
- package/dist/heart/daemon/health-monitor.js +92 -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/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +394 -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 +110 -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 +215 -1
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +365 -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 +431 -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 +264 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +524 -0
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-runtime-state.js +100 -0
- package/dist/heart/habits/habit-scheduler.js +372 -0
- package/dist/heart/{daemon → hatch}/hatch-animation.js +10 -3
- package/dist/heart/{daemon → hatch}/hatch-flow.js +54 -136
- 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 +306 -0
- package/dist/heart/identity.js +274 -61
- 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/mail-import-discovery.js +353 -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 +66 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +244 -0
- package/dist/heart/outlook/outlook-http-static.js +103 -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 +31 -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 +382 -0
- package/dist/heart/outlook/readers/continuity-readers.js +336 -0
- package/dist/heart/outlook/readers/mail.js +362 -0
- package/dist/heart/outlook/readers/runtime-readers.js +644 -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 +134 -0
- package/dist/heart/provider-binding-resolver.js +255 -0
- package/dist/heart/provider-credentials.js +424 -0
- package/dist/heart/provider-failover.js +301 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +202 -50
- package/dist/heart/providers/azure.js +104 -13
- 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 +63 -39
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +260 -0
- package/dist/heart/sense-truth.js +68 -0
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +1089 -0
- package/dist/heart/session-playback-cli-main.js +5 -0
- package/dist/heart/session-playback-cli.js +36 -0
- package/dist/heart/session-playback.js +231 -0
- package/dist/heart/session-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +129 -34
- 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 +372 -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 +425 -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 +115 -0
- package/dist/heart/versioning/update-hooks.js +142 -0
- package/dist/heart/versioning/wrapper-publish-guard.js +86 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +606 -0
- package/dist/mailroom/core.js +672 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +426 -0
- package/dist/mailroom/mbox-import.js +382 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +219 -0
- package/dist/mailroom/search-cache.js +182 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mailroom/source-state.js +176 -0
- package/dist/mailroom/thread.js +109 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/bundle-manifest.js +77 -1
- package/dist/mind/context.js +173 -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 +73 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +54 -2
- 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 +1144 -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 +101 -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-BPr5vNuM.css +1 -0
- package/dist/outlook-ui/assets/index-Cm51CY9W.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 +774 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +180 -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 +111 -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 +396 -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 +39 -13
- 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 +49 -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 +381 -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-mail.js +1377 -0
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +749 -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-trip.js +356 -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 +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +561 -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 +73 -0
- package/dist/senses/bluebubbles/inbound-log.js +126 -0
- package/dist/senses/bluebubbles/index.js +1881 -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/processed-log.js +111 -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 +199 -16
- package/dist/senses/inner-dialog.js +640 -91
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +665 -0
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +248 -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 +207 -2
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +146 -0
- package/package.json +47 -6
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +117 -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 +138 -0
- package/AdoptionSpecialist.ouro/agent.json +0 -20
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +0 -22
- package/dist/heart/daemon/specialist-orchestrator.js +0 -160
- package/dist/heart/daemon/specialist-prompt.js +0 -40
- package/dist/heart/daemon/specialist-session.js +0 -142
- package/dist/heart/daemon/specialist-tools.js +0 -128
- 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,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reconstructThread = reconstructThread;
|
|
4
|
+
function normalizeHeaderId(value) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return undefined;
|
|
7
|
+
const trimmed = value.trim();
|
|
8
|
+
return trimmed.length === 0 ? undefined : trimmed;
|
|
9
|
+
}
|
|
10
|
+
function inferKeysForMessage(message) {
|
|
11
|
+
const keys = [];
|
|
12
|
+
const headerId = normalizeHeaderId(message.private.messageId);
|
|
13
|
+
if (headerId)
|
|
14
|
+
keys.push(headerId);
|
|
15
|
+
/* v8 ignore next -- defensive: stored messages always have an id @preserve */
|
|
16
|
+
if (message.id)
|
|
17
|
+
keys.push(message.id);
|
|
18
|
+
return keys;
|
|
19
|
+
}
|
|
20
|
+
function sortByReceivedAtAscending(left, right) {
|
|
21
|
+
return Date.parse(left.receivedAt) - Date.parse(right.receivedAt);
|
|
22
|
+
}
|
|
23
|
+
function reconstructThread(seedMessageId, pool) {
|
|
24
|
+
const seed = pool.find((message) => message.id === seedMessageId)
|
|
25
|
+
?? pool.find((message) => normalizeHeaderId(message.private.messageId) === seedMessageId);
|
|
26
|
+
if (!seed)
|
|
27
|
+
return { rootMessageId: undefined, members: [] };
|
|
28
|
+
const byKey = new Map();
|
|
29
|
+
const allNodes = [];
|
|
30
|
+
for (const message of pool) {
|
|
31
|
+
const node = { message, parents: new Set(), children: new Set() };
|
|
32
|
+
allNodes.push(node);
|
|
33
|
+
for (const key of inferKeysForMessage(message)) {
|
|
34
|
+
/* v8 ignore next -- collision guard: storage id and RFC822 messageId differ in the normal case @preserve */
|
|
35
|
+
if (!byKey.has(key))
|
|
36
|
+
byKey.set(key, node);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const node of allNodes) {
|
|
40
|
+
const parentKeys = new Set();
|
|
41
|
+
const inReplyTo = normalizeHeaderId(node.message.private.inReplyTo);
|
|
42
|
+
if (inReplyTo)
|
|
43
|
+
parentKeys.add(inReplyTo);
|
|
44
|
+
for (const reference of node.message.private.references ?? []) {
|
|
45
|
+
const ref = normalizeHeaderId(reference);
|
|
46
|
+
if (ref)
|
|
47
|
+
parentKeys.add(ref);
|
|
48
|
+
}
|
|
49
|
+
for (const key of parentKeys) {
|
|
50
|
+
const parent = byKey.get(key);
|
|
51
|
+
if (parent && parent !== node) {
|
|
52
|
+
node.parents.add(parent);
|
|
53
|
+
parent.children.add(node);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const seedNode = byKey.get(seed.id);
|
|
58
|
+
const component = new Set();
|
|
59
|
+
const stack = [seedNode];
|
|
60
|
+
while (stack.length > 0) {
|
|
61
|
+
const node = stack.pop();
|
|
62
|
+
if (component.has(node))
|
|
63
|
+
continue;
|
|
64
|
+
component.add(node);
|
|
65
|
+
for (const parent of node.parents)
|
|
66
|
+
if (!component.has(parent))
|
|
67
|
+
stack.push(parent);
|
|
68
|
+
for (const child of node.children)
|
|
69
|
+
if (!component.has(child))
|
|
70
|
+
stack.push(child);
|
|
71
|
+
}
|
|
72
|
+
/* v8 ignore start -- root + topological depth pass: branch shapes vary with thread topology and aren't worth chasing per-branch in tests; correctness is covered by the higher-level reconstruction tests @preserve */
|
|
73
|
+
const componentRoots = [...component].filter((node) => {
|
|
74
|
+
for (const parent of node.parents)
|
|
75
|
+
if (component.has(parent))
|
|
76
|
+
return false;
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
79
|
+
const root = componentRoots
|
|
80
|
+
.sort((left, right) => Date.parse(left.message.receivedAt) - Date.parse(right.message.receivedAt))[0]
|
|
81
|
+
?? seedNode;
|
|
82
|
+
const componentInTimeOrder = [...component].sort((left, right) => Date.parse(left.message.receivedAt) - Date.parse(right.message.receivedAt));
|
|
83
|
+
const depthByNode = new Map();
|
|
84
|
+
for (const node of componentInTimeOrder) {
|
|
85
|
+
let maxParentDepth = -1;
|
|
86
|
+
for (const parent of node.parents) {
|
|
87
|
+
if (!component.has(parent))
|
|
88
|
+
continue;
|
|
89
|
+
const parentDepth = depthByNode.get(parent);
|
|
90
|
+
if (parentDepth !== undefined && parentDepth > maxParentDepth) {
|
|
91
|
+
maxParentDepth = parentDepth;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
depthByNode.set(node, maxParentDepth + 1);
|
|
95
|
+
}
|
|
96
|
+
/* v8 ignore stop */
|
|
97
|
+
const members = [...component]
|
|
98
|
+
.map((node) => node.message)
|
|
99
|
+
.sort(sortByReceivedAtAscending)
|
|
100
|
+
.map((message) => {
|
|
101
|
+
const node = byKey.get(message.id);
|
|
102
|
+
/* v8 ignore next -- fallback: depthByNode is populated for every component node by the topological pass @preserve */
|
|
103
|
+
return { message, depth: depthByNode.get(node) ?? 0 };
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
rootMessageId: normalizeHeaderId(root.message.private.messageId) ?? root.message.id,
|
|
107
|
+
members,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractTravelFactsFromMail = extractTravelFactsFromMail;
|
|
4
|
+
const runtime_1 = require("../nerves/runtime");
|
|
5
|
+
function firstMatch(text, pattern) {
|
|
6
|
+
const match = pattern.exec(text);
|
|
7
|
+
return match?.[1]?.trim() ?? null;
|
|
8
|
+
}
|
|
9
|
+
function flightFact(message) {
|
|
10
|
+
const text = `${message.private.subject}\n${message.private.text}`;
|
|
11
|
+
const flightNumber = firstMatch(text, /\bflight\s+([A-Z]{2}\s?\d{1,4})\b/i);
|
|
12
|
+
const routeMatch = /\b([A-Z]{3})\s*(?:->|to)\s*([A-Z]{3})\b/.exec(text);
|
|
13
|
+
const confirmationCode = firstMatch(text, /confirmation code:\s*([A-Z0-9-]+)/i);
|
|
14
|
+
const departure = firstMatch(text, /departure:\s*([^\n]+)/i);
|
|
15
|
+
if (!flightNumber && !routeMatch && !departure)
|
|
16
|
+
return null;
|
|
17
|
+
const fields = {};
|
|
18
|
+
if (flightNumber)
|
|
19
|
+
fields.flightNumber = flightNumber.toUpperCase().replace(/\s+/, " ");
|
|
20
|
+
if (routeMatch)
|
|
21
|
+
fields.route = `${routeMatch[1]} -> ${routeMatch[2]}`;
|
|
22
|
+
if (departure)
|
|
23
|
+
fields.departure = departure;
|
|
24
|
+
if (confirmationCode)
|
|
25
|
+
fields.confirmationCode = confirmationCode;
|
|
26
|
+
return {
|
|
27
|
+
kind: "flight",
|
|
28
|
+
messageId: message.id,
|
|
29
|
+
subject: message.private.subject,
|
|
30
|
+
source: message.source ?? null,
|
|
31
|
+
ownerEmail: message.ownerEmail ?? null,
|
|
32
|
+
summary: [
|
|
33
|
+
fields.flightNumber ? `flight ${fields.flightNumber}` : "flight",
|
|
34
|
+
fields.route ? fields.route : null,
|
|
35
|
+
fields.departure ? `departing ${fields.departure}` : null,
|
|
36
|
+
].filter(Boolean).join(" "),
|
|
37
|
+
fields,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function lodgingFact(message) {
|
|
41
|
+
const text = `${message.private.subject}\n${message.private.text}`;
|
|
42
|
+
const hotel = firstMatch(text, /hotel:\s*([^\n]+)/i);
|
|
43
|
+
const checkIn = firstMatch(text, /check-?in:\s*([^\n]+)/i);
|
|
44
|
+
const checkOut = firstMatch(text, /check-?out:\s*([^\n]+)/i);
|
|
45
|
+
const confirmationCode = firstMatch(text, /confirmation code:\s*([A-Z0-9-]+)/i);
|
|
46
|
+
if (!hotel && !checkIn && !/hotel|lodging|booking/i.test(message.private.subject))
|
|
47
|
+
return null;
|
|
48
|
+
const fields = {};
|
|
49
|
+
if (hotel)
|
|
50
|
+
fields.hotel = hotel;
|
|
51
|
+
if (checkIn)
|
|
52
|
+
fields.checkIn = checkIn;
|
|
53
|
+
if (checkOut)
|
|
54
|
+
fields.checkOut = checkOut;
|
|
55
|
+
if (confirmationCode)
|
|
56
|
+
fields.confirmationCode = confirmationCode;
|
|
57
|
+
return {
|
|
58
|
+
kind: "lodging",
|
|
59
|
+
messageId: message.id,
|
|
60
|
+
subject: message.private.subject,
|
|
61
|
+
source: message.source ?? null,
|
|
62
|
+
ownerEmail: message.ownerEmail ?? null,
|
|
63
|
+
summary: [
|
|
64
|
+
hotel ?? "lodging",
|
|
65
|
+
checkIn ? `check-in ${checkIn}` : null,
|
|
66
|
+
checkOut ? `check-out ${checkOut}` : null,
|
|
67
|
+
].filter(Boolean).join(" "),
|
|
68
|
+
fields,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function extractTravelFactsFromMail(messages) {
|
|
72
|
+
const facts = messages.flatMap((message) => {
|
|
73
|
+
const results = [];
|
|
74
|
+
const flight = flightFact(message);
|
|
75
|
+
if (flight)
|
|
76
|
+
results.push(flight);
|
|
77
|
+
const lodging = lodgingFact(message);
|
|
78
|
+
if (lodging)
|
|
79
|
+
results.push(lodging);
|
|
80
|
+
return results;
|
|
81
|
+
});
|
|
82
|
+
(0, runtime_1.emitNervesEvent)({
|
|
83
|
+
component: "senses",
|
|
84
|
+
event: "senses.mail_travel_facts_extracted",
|
|
85
|
+
message: "travel facts extracted from mail",
|
|
86
|
+
meta: { messages: messages.length, facts: facts.length },
|
|
87
|
+
});
|
|
88
|
+
return facts;
|
|
89
|
+
}
|
|
@@ -34,6 +34,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.CANONICAL_BUNDLE_MANIFEST = void 0;
|
|
37
|
+
exports.getChangelogPath = getChangelogPath;
|
|
38
|
+
exports.getPackageVersion = getPackageVersion;
|
|
39
|
+
exports.createBundleMeta = createBundleMeta;
|
|
40
|
+
exports.backfillBundleMeta = backfillBundleMeta;
|
|
41
|
+
exports.resetBackfillTracking = resetBackfillTracking;
|
|
37
42
|
exports.isCanonicalBundlePath = isCanonicalBundlePath;
|
|
38
43
|
exports.findNonCanonicalBundlePaths = findNonCanonicalBundlePaths;
|
|
39
44
|
const fs = __importStar(require("fs"));
|
|
@@ -41,18 +46,89 @@ const path = __importStar(require("path"));
|
|
|
41
46
|
const runtime_1 = require("../nerves/runtime");
|
|
42
47
|
exports.CANONICAL_BUNDLE_MANIFEST = [
|
|
43
48
|
{ path: "agent.json", kind: "file" },
|
|
49
|
+
{ path: "bundle-meta.json", kind: "file" },
|
|
44
50
|
{ path: "psyche/SOUL.md", kind: "file" },
|
|
45
51
|
{ path: "psyche/IDENTITY.md", kind: "file" },
|
|
46
52
|
{ path: "psyche/LORE.md", kind: "file" },
|
|
47
53
|
{ path: "psyche/TACIT.md", kind: "file" },
|
|
48
54
|
{ path: "psyche/ASPIRATIONS.md", kind: "file" },
|
|
49
|
-
{ path: "
|
|
55
|
+
{ path: "arc", kind: "dir" },
|
|
56
|
+
{ path: "arc/episodes", kind: "dir" },
|
|
57
|
+
{ path: "arc/obligations", kind: "dir" },
|
|
58
|
+
{ path: "arc/cares", kind: "dir" },
|
|
59
|
+
{ path: "arc/intentions", kind: "dir" },
|
|
60
|
+
{ path: "diary", kind: "dir" },
|
|
61
|
+
{ path: "journal", kind: "dir" },
|
|
50
62
|
{ path: "friends", kind: "dir" },
|
|
63
|
+
{ path: "state", kind: "dir" },
|
|
51
64
|
{ path: "tasks", kind: "dir" },
|
|
52
65
|
{ path: "skills", kind: "dir" },
|
|
53
66
|
{ path: "senses", kind: "dir" },
|
|
54
67
|
{ path: "senses/teams", kind: "dir" },
|
|
55
68
|
];
|
|
69
|
+
function getChangelogPath() {
|
|
70
|
+
const changelogPath = path.resolve(__dirname, "../../changelog.json");
|
|
71
|
+
(0, runtime_1.emitNervesEvent)({
|
|
72
|
+
component: "mind",
|
|
73
|
+
event: "mind.changelog_path_resolved",
|
|
74
|
+
message: "resolved changelog path",
|
|
75
|
+
meta: { path: changelogPath },
|
|
76
|
+
});
|
|
77
|
+
return changelogPath;
|
|
78
|
+
}
|
|
79
|
+
function getPackageVersion() {
|
|
80
|
+
const packageJsonPath = path.resolve(__dirname, "../../package.json");
|
|
81
|
+
const raw = fs.readFileSync(packageJsonPath, "utf-8");
|
|
82
|
+
const parsed = JSON.parse(raw);
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "mind",
|
|
85
|
+
event: "mind.package_version_read",
|
|
86
|
+
message: "read package version",
|
|
87
|
+
meta: { version: parsed.version },
|
|
88
|
+
});
|
|
89
|
+
return parsed.version;
|
|
90
|
+
}
|
|
91
|
+
function createBundleMeta() {
|
|
92
|
+
return {
|
|
93
|
+
runtimeVersion: getPackageVersion(),
|
|
94
|
+
bundleSchemaVersion: 1,
|
|
95
|
+
lastUpdated: new Date().toISOString(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const _backfilledRoots = new Set();
|
|
99
|
+
/**
|
|
100
|
+
* If bundle-meta.json is missing from the agent root, create it with current runtime version.
|
|
101
|
+
* This backfills existing agent bundles that were created before bundle-meta.json was introduced.
|
|
102
|
+
* Only attempts once per bundleRoot per process.
|
|
103
|
+
*/
|
|
104
|
+
function backfillBundleMeta(bundleRoot) {
|
|
105
|
+
if (_backfilledRoots.has(bundleRoot))
|
|
106
|
+
return;
|
|
107
|
+
_backfilledRoots.add(bundleRoot);
|
|
108
|
+
const metaPath = path.join(bundleRoot, "bundle-meta.json");
|
|
109
|
+
try {
|
|
110
|
+
if (fs.existsSync(metaPath)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const meta = createBundleMeta();
|
|
114
|
+
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
115
|
+
(0, runtime_1.emitNervesEvent)({
|
|
116
|
+
component: "mind",
|
|
117
|
+
event: "mind.bundle_meta_backfill",
|
|
118
|
+
message: "backfilled missing bundle-meta.json",
|
|
119
|
+
meta: { bundleRoot },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Non-blocking: if we can't write, that's okay
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Reset the backfill tracking set. Used in tests.
|
|
128
|
+
*/
|
|
129
|
+
function resetBackfillTracking() {
|
|
130
|
+
_backfilledRoots.clear();
|
|
131
|
+
}
|
|
56
132
|
const CANONICAL_FILE_PATHS = new Set(exports.CANONICAL_BUNDLE_MANIFEST
|
|
57
133
|
.filter((entry) => entry.kind === "file")
|
|
58
134
|
.map((entry) => entry.path));
|
package/dist/mind/context.js
CHANGED
|
@@ -33,34 +33,42 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.validateSessionMessages = exports.repairSessionMessages = exports.migrateToolNames = void 0;
|
|
36
37
|
exports.trimMessages = trimMessages;
|
|
37
|
-
exports.validateSessionMessages = validateSessionMessages;
|
|
38
|
-
exports.repairSessionMessages = repairSessionMessages;
|
|
39
38
|
exports.saveSession = saveSession;
|
|
39
|
+
exports.appendSyntheticAssistantMessage = appendSyntheticAssistantMessage;
|
|
40
40
|
exports.loadSession = loadSession;
|
|
41
41
|
exports.postTurn = postTurn;
|
|
42
|
+
exports.postTurnTrim = postTurnTrim;
|
|
43
|
+
exports.postTurnPersist = postTurnPersist;
|
|
44
|
+
exports.deferPostTurnPersist = deferPostTurnPersist;
|
|
42
45
|
exports.deleteSession = deleteSession;
|
|
43
46
|
const config_1 = require("../heart/config");
|
|
47
|
+
const session_events_1 = require("../heart/session-events");
|
|
44
48
|
const runtime_1 = require("../nerves/runtime");
|
|
45
49
|
const fs = __importStar(require("fs"));
|
|
46
50
|
const path = __importStar(require("path"));
|
|
47
51
|
const token_estimate_1 = require("./token-estimate");
|
|
52
|
+
var session_events_2 = require("../heart/session-events");
|
|
53
|
+
Object.defineProperty(exports, "migrateToolNames", { enumerable: true, get: function () { return session_events_2.migrateToolNames; } });
|
|
54
|
+
Object.defineProperty(exports, "repairSessionMessages", { enumerable: true, get: function () { return session_events_2.repairSessionMessages; } });
|
|
55
|
+
Object.defineProperty(exports, "validateSessionMessages", { enumerable: true, get: function () { return session_events_2.validateSessionMessages; } });
|
|
48
56
|
function buildTrimmableBlocks(messages) {
|
|
49
57
|
const blocks = [];
|
|
50
58
|
let i = 0;
|
|
51
59
|
while (i < messages.length) {
|
|
52
60
|
const msg = messages[i];
|
|
53
|
-
if (msg
|
|
61
|
+
if (msg.role === "system") {
|
|
54
62
|
i++;
|
|
55
63
|
continue;
|
|
56
64
|
}
|
|
57
65
|
// Tool coherence block: assistant message with tool_calls + immediately following tool results
|
|
58
|
-
if (msg
|
|
66
|
+
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
|
|
59
67
|
const indices = [i];
|
|
60
68
|
i++;
|
|
61
69
|
while (i < messages.length) {
|
|
62
70
|
const next = messages[i];
|
|
63
|
-
if (next
|
|
71
|
+
if (next.role !== "tool")
|
|
64
72
|
break;
|
|
65
73
|
indices.push(i);
|
|
66
74
|
i++;
|
|
@@ -78,13 +86,13 @@ function buildTrimmableBlocks(messages) {
|
|
|
78
86
|
function getSystemMessageIndices(messages) {
|
|
79
87
|
const indices = [];
|
|
80
88
|
for (let i = 0; i < messages.length; i++) {
|
|
81
|
-
if (messages[i]
|
|
89
|
+
if (messages[i].role === "system")
|
|
82
90
|
indices.push(i);
|
|
83
91
|
}
|
|
84
92
|
return indices;
|
|
85
93
|
}
|
|
86
94
|
function buildTrimmedMessages(messages, kept) {
|
|
87
|
-
return messages.filter((m, idx) => m
|
|
95
|
+
return messages.filter((m, idx) => m.role === "system" || kept.has(idx));
|
|
88
96
|
}
|
|
89
97
|
function trimMessages(messages, maxTokens, contextMargin, actualTokenCount) {
|
|
90
98
|
const targetTokens = Math.floor(maxTokens * (1 - contextMargin / 100));
|
|
@@ -132,7 +140,7 @@ function trimMessages(messages, maxTokens, contextMargin, actualTokenCount) {
|
|
|
132
140
|
let remaining = actualTokenCount;
|
|
133
141
|
const kept = new Set();
|
|
134
142
|
for (let i = 0; i < messages.length; i++) {
|
|
135
|
-
if (messages[i]
|
|
143
|
+
if (messages[i].role !== "system")
|
|
136
144
|
kept.add(i);
|
|
137
145
|
}
|
|
138
146
|
// Drop oldest blocks until we fall under target.
|
|
@@ -146,7 +154,7 @@ function trimMessages(messages, maxTokens, contextMargin, actualTokenCount) {
|
|
|
146
154
|
let trimmed = buildTrimmedMessages(messages, kept);
|
|
147
155
|
// If we're still above budget after dropping everything trimmable, preserve system only.
|
|
148
156
|
if (remaining > targetTokens) {
|
|
149
|
-
trimmed = messages.filter((m) => m
|
|
157
|
+
trimmed = messages.filter((m) => m.role === "system");
|
|
150
158
|
}
|
|
151
159
|
const estimatedAfter = (0, token_estimate_1.estimateTokensForMessages)(trimmed);
|
|
152
160
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -173,106 +181,94 @@ function trimMessages(messages, maxTokens, contextMargin, actualTokenCount) {
|
|
|
173
181
|
* user → assistant (with optional tool calls/results) → user → assistant...
|
|
174
182
|
* Never assistant → assistant without a user in between.
|
|
175
183
|
*/
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (msg.role === "system")
|
|
184
|
-
continue;
|
|
185
|
-
if (msg.role === "tool") {
|
|
186
|
-
sawToolResultSincePrevAssistant = true;
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (msg.role === "assistant" && prevNonToolRole === "assistant") {
|
|
190
|
-
// assistant → tool(s) → assistant is valid (tool call flow)
|
|
191
|
-
if (!(prevAssistantHadToolCalls && sawToolResultSincePrevAssistant)) {
|
|
192
|
-
violations.push(`back-to-back assistant at index ${i}`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
prevAssistantHadToolCalls = msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;
|
|
196
|
-
sawToolResultSincePrevAssistant = false;
|
|
197
|
-
prevNonToolRole = msg.role;
|
|
198
|
-
}
|
|
199
|
-
return violations;
|
|
184
|
+
function denormalizeContinuityState(state) {
|
|
185
|
+
if (!state.mustResolveBeforeHandoff && typeof state.lastFriendActivityAt !== "string")
|
|
186
|
+
return undefined;
|
|
187
|
+
return {
|
|
188
|
+
...(state.mustResolveBeforeHandoff ? { mustResolveBeforeHandoff: true } : {}),
|
|
189
|
+
...(typeof state.lastFriendActivityAt === "string" ? { lastFriendActivityAt: state.lastFriendActivityAt } : {}),
|
|
190
|
+
};
|
|
200
191
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
component: "mind",
|
|
225
|
-
message: "repaired session invariant violations",
|
|
226
|
-
meta: { violations },
|
|
192
|
+
function writeSessionEnvelope(filePath, envelope) {
|
|
193
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
194
|
+
fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
|
|
195
|
+
}
|
|
196
|
+
function saveSession(filePath, messages, lastUsage, state) {
|
|
197
|
+
const existing = (0, session_events_1.loadSessionEnvelopeFile)(filePath);
|
|
198
|
+
const previousMessages = existing ? (0, session_events_1.projectProviderMessages)(existing) : [];
|
|
199
|
+
const currentIngressTimes = messages.map(session_events_1.getIngressTime);
|
|
200
|
+
const sanitized = (0, session_events_1.sanitizeProviderMessages)(messages);
|
|
201
|
+
const { envelope } = (0, session_events_1.buildCanonicalSessionEnvelope)({
|
|
202
|
+
existing,
|
|
203
|
+
previousMessages,
|
|
204
|
+
currentMessages: sanitized,
|
|
205
|
+
trimmedMessages: sanitized,
|
|
206
|
+
currentIngressTimes,
|
|
207
|
+
recordedAt: new Date().toISOString(),
|
|
208
|
+
lastUsage: lastUsage ?? null,
|
|
209
|
+
state,
|
|
210
|
+
projectionBasis: {
|
|
211
|
+
maxTokens: null,
|
|
212
|
+
contextMargin: null,
|
|
213
|
+
inputTokens: lastUsage?.input_tokens ?? null,
|
|
214
|
+
},
|
|
227
215
|
});
|
|
228
|
-
|
|
216
|
+
writeSessionEnvelope(filePath, envelope);
|
|
229
217
|
}
|
|
230
|
-
function
|
|
231
|
-
|
|
232
|
-
|
|
218
|
+
function appendSyntheticAssistantMessage(filePath, content) {
|
|
219
|
+
try {
|
|
220
|
+
if (!fs.existsSync(filePath))
|
|
221
|
+
return false;
|
|
222
|
+
const envelope = (0, session_events_1.loadSessionEnvelopeFile)(filePath);
|
|
223
|
+
if (!envelope)
|
|
224
|
+
return false;
|
|
225
|
+
const updated = (0, session_events_1.appendSyntheticAssistantEvent)(envelope, content, new Date().toISOString());
|
|
226
|
+
writeSessionEnvelope(filePath, updated);
|
|
233
227
|
(0, runtime_1.emitNervesEvent)({
|
|
234
|
-
level: "warn",
|
|
235
|
-
event: "mind.session_invariant_violation",
|
|
236
228
|
component: "mind",
|
|
237
|
-
|
|
238
|
-
|
|
229
|
+
event: "mind.session_synthetic_message_appended",
|
|
230
|
+
message: "appended synthetic assistant message to session",
|
|
231
|
+
meta: { path: filePath, contentLength: content.length },
|
|
239
232
|
});
|
|
240
|
-
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return false;
|
|
241
237
|
}
|
|
242
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
243
|
-
const envelope = { version: 1, messages };
|
|
244
|
-
if (lastUsage)
|
|
245
|
-
envelope.lastUsage = lastUsage;
|
|
246
|
-
fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
|
|
247
238
|
}
|
|
248
239
|
function loadSession(filePath) {
|
|
249
240
|
try {
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
if (data.version !== 1)
|
|
241
|
+
const envelope = (0, session_events_1.loadSessionEnvelopeFile)(filePath);
|
|
242
|
+
if (!envelope)
|
|
253
243
|
return null;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
component: "mind",
|
|
261
|
-
message: "session invariant violated on load",
|
|
262
|
-
meta: { path: filePath, violations },
|
|
263
|
-
});
|
|
264
|
-
messages = repairSessionMessages(messages);
|
|
265
|
-
}
|
|
266
|
-
return { messages, lastUsage: data.lastUsage };
|
|
244
|
+
return {
|
|
245
|
+
messages: (0, session_events_1.sanitizeProviderMessages)((0, session_events_1.projectProviderMessages)(envelope)),
|
|
246
|
+
events: envelope.events,
|
|
247
|
+
lastUsage: envelope.lastUsage ?? undefined,
|
|
248
|
+
state: denormalizeContinuityState(envelope.state),
|
|
249
|
+
};
|
|
267
250
|
}
|
|
268
251
|
catch {
|
|
269
252
|
return null;
|
|
270
253
|
}
|
|
271
254
|
}
|
|
272
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Synchronous post-turn: sanitize, trim (mutates messages in place), and persist to disk.
|
|
257
|
+
* For non-blocking persist, use postTurnTrim() + deferPostTurnPersist() instead.
|
|
258
|
+
*/
|
|
259
|
+
function postTurn(messages, sessPath, usage, hooks, state) {
|
|
260
|
+
const prepared = postTurnTrim(messages, usage, hooks);
|
|
261
|
+
postTurnPersist(sessPath, prepared, usage, state);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Synchronous phase: run hooks, sanitize, trim, and mutate the messages array in place.
|
|
265
|
+
* Returns the data needed by postTurnPersist / deferPostTurnPersist.
|
|
266
|
+
*/
|
|
267
|
+
function postTurnTrim(messages, usage, hooks) {
|
|
268
|
+
const preTrimMessages = [...messages];
|
|
273
269
|
if (hooks?.beforeTrim) {
|
|
274
270
|
try {
|
|
275
|
-
hooks.beforeTrim(
|
|
271
|
+
hooks.beforeTrim(preTrimMessages);
|
|
276
272
|
}
|
|
277
273
|
catch (error) {
|
|
278
274
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -287,9 +283,92 @@ function postTurn(messages, sessPath, usage, hooks) {
|
|
|
287
283
|
}
|
|
288
284
|
}
|
|
289
285
|
const { maxTokens, contextMargin } = (0, config_1.getContextConfig)();
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
286
|
+
const currentIngressTimes = messages.map(session_events_1.getIngressTime);
|
|
287
|
+
const currentMessages = (0, session_events_1.sanitizeProviderMessages)(messages);
|
|
288
|
+
const trimmedMessages = trimMessages(currentMessages, maxTokens, contextMargin, usage?.input_tokens);
|
|
289
|
+
messages.splice(0, messages.length, ...trimmedMessages);
|
|
290
|
+
return { currentMessages, trimmedMessages, currentIngressTimes, maxTokens, contextMargin };
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Synchronous persist: load existing envelope, build canonical envelope, write to disk.
|
|
294
|
+
*/
|
|
295
|
+
function postTurnPersist(sessPath, prepared, usage, state) {
|
|
296
|
+
const existing = (0, session_events_1.loadSessionEnvelopeFile)(sessPath);
|
|
297
|
+
const previousMessages = existing ? (0, session_events_1.projectProviderMessages)(existing) : [];
|
|
298
|
+
const { envelope, evictedEvents } = (0, session_events_1.buildCanonicalSessionEnvelope)({
|
|
299
|
+
existing,
|
|
300
|
+
previousMessages,
|
|
301
|
+
currentMessages: prepared.currentMessages,
|
|
302
|
+
trimmedMessages: prepared.trimmedMessages,
|
|
303
|
+
currentIngressTimes: prepared.currentIngressTimes,
|
|
304
|
+
recordedAt: new Date().toISOString(),
|
|
305
|
+
lastUsage: usage ?? null,
|
|
306
|
+
state,
|
|
307
|
+
projectionBasis: {
|
|
308
|
+
maxTokens: prepared.maxTokens,
|
|
309
|
+
contextMargin: prepared.contextMargin,
|
|
310
|
+
inputTokens: usage?.input_tokens ?? null,
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
(0, session_events_1.appendEvictedToArchive)(sessPath, evictedEvents);
|
|
314
|
+
writeSessionEnvelope(sessPath, envelope);
|
|
315
|
+
return envelope.events;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Per-sessPath serialization queue. Without this, two concurrent
|
|
319
|
+
* `deferPostTurnPersist` calls (e.g. two BlueBubbles webhooks for the same
|
|
320
|
+
* chat firing back-to-back, or a CLI postTurn racing the inner-dialog turn
|
|
321
|
+
* for the same MCP session) would each load the envelope, both compute the
|
|
322
|
+
* same "next sequence", and write events with colliding ids. The session
|
|
323
|
+
* file would silently accumulate duplicates and replay would diverge from
|
|
324
|
+
* what was actually sent on the wire.
|
|
325
|
+
*
|
|
326
|
+
* The queue is in-process only — it does not protect against multiple Node
|
|
327
|
+
* processes writing to the same file. The dedup-on-load behaviour in
|
|
328
|
+
* parseSessionEnvelope keeps cross-process races from leaving permanent
|
|
329
|
+
* corruption; this serializer just keeps the common (single-process) case
|
|
330
|
+
* race-free in the first place.
|
|
331
|
+
*/
|
|
332
|
+
const sessionPersistQueues = new Map();
|
|
333
|
+
function enqueueSessionPersist(sessPath, fn) {
|
|
334
|
+
const previous = sessionPersistQueues.get(sessPath) ?? Promise.resolve();
|
|
335
|
+
// Chain on the previous tail. fn runs whether previous resolved or rejected,
|
|
336
|
+
// so one failed turn cannot block subsequent turns on the same session.
|
|
337
|
+
const next = previous.then(fn, fn);
|
|
338
|
+
// Save a swallowed-rejection sentinel as the new tail so the next caller's
|
|
339
|
+
// `previous.then(fn, fn)` sees a clean resolution; the original `next` still
|
|
340
|
+
// propagates rejection to its own caller as expected.
|
|
341
|
+
/* v8 ignore start -- the swallow only matters when fn rejects, which is the failure path covered separately */
|
|
342
|
+
const sentinel = next.then(() => undefined, () => undefined);
|
|
343
|
+
/* v8 ignore stop */
|
|
344
|
+
sessionPersistQueues.set(sessPath, sentinel);
|
|
345
|
+
return next;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Deferred persist: same as postTurnPersist but runs on the next event loop
|
|
349
|
+
* tick AND serializes against any other deferred persist for the same
|
|
350
|
+
* sessPath, so concurrent turns cannot race and produce duplicate event ids
|
|
351
|
+
* in the saved session.
|
|
352
|
+
*/
|
|
353
|
+
function deferPostTurnPersist(sessPath, prepared, usage, state) {
|
|
354
|
+
return enqueueSessionPersist(sessPath, () => new Promise((resolve) => {
|
|
355
|
+
setImmediate(() => {
|
|
356
|
+
try {
|
|
357
|
+
const events = postTurnPersist(sessPath, prepared, usage, state);
|
|
358
|
+
resolve(events);
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
(0, runtime_1.emitNervesEvent)({
|
|
362
|
+
level: "warn",
|
|
363
|
+
component: "mind",
|
|
364
|
+
event: "mind.deferred_persist_error",
|
|
365
|
+
message: "deferred session persist failed",
|
|
366
|
+
meta: { error: err instanceof Error ? err.message : String(err) },
|
|
367
|
+
});
|
|
368
|
+
resolve([]);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}));
|
|
293
372
|
}
|
|
294
373
|
function deleteSession(filePath) {
|
|
295
374
|
try {
|