@ouro.bot/cli 0.1.0-alpha.69 → 0.1.0-alpha.691
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 +133 -23
- package/RepairGuide.ouro/agent.json +5 -0
- package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
- package/RepairGuide.ouro/psyche/SOUL.md +55 -0
- package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
- package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
- package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +4398 -14
- package/dist/a2a/card.js +57 -0
- package/dist/a2a/client.js +180 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/delegation-stores.js +56 -0
- package/dist/a2a/did-resolution.js +93 -0
- package/dist/a2a/identity.js +170 -0
- package/dist/a2a/inbound-share.js +107 -0
- package/dist/a2a/mission-result-wire.js +196 -0
- package/dist/a2a/onboarding.js +115 -0
- package/dist/a2a/pin-store.js +132 -0
- package/dist/a2a/seen-ledger.js +120 -0
- package/dist/a2a/server.js +604 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/transport.js +45 -0
- package/dist/a2a/types.js +3 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +144 -0
- package/dist/arc/episodes.js +118 -0
- package/dist/arc/evolution.js +490 -0
- package/dist/arc/flight-recorder.js +810 -0
- package/dist/arc/intentions.js +134 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +326 -0
- package/dist/arc/packets.js +288 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +57 -0
- package/dist/commerce/store.js +755 -0
- package/dist/commerce/types.js +3 -0
- package/dist/heart/active-work.js +867 -43
- package/dist/heart/agent-entry.js +160 -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/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 +479 -0
- package/dist/heart/awaiting/await-alert.js +146 -0
- package/dist/heart/awaiting/await-expiry.js +108 -0
- package/dist/heart/awaiting/await-loader.js +91 -0
- package/dist/heart/awaiting/await-parser.js +141 -0
- package/dist/heart/awaiting/await-runtime-state.js +100 -0
- package/dist/heart/awaiting/await-scheduler.js +378 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bridges/manager.js +137 -17
- package/dist/heart/bridges/store.js +14 -2
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +135 -0
- package/dist/heart/config-registry.js +331 -0
- package/dist/heart/config.js +122 -119
- package/dist/heart/context-loss-gauntlet.js +354 -0
- package/dist/heart/context-loss-sentinel.js +993 -0
- package/dist/heart/core.js +1297 -269
- package/dist/heart/cross-chat-delivery.js +8 -22
- package/dist/heart/daemon/agent-config-check.js +451 -0
- package/dist/heart/daemon/agent-discovery.js +185 -3
- package/dist/heart/daemon/agent-service.js +523 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +780 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +8100 -0
- package/dist/heart/daemon/cli-help.js +601 -0
- package/dist/heart/daemon/cli-parse.js +1995 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +770 -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 +29 -1814
- package/dist/heart/daemon/daemon-entry.js +668 -6
- package/dist/heart/daemon/daemon-health.js +176 -0
- package/dist/heart/daemon/daemon-rollup.js +57 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +1222 -84
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +873 -0
- package/dist/heart/daemon/habit-private-wake.js +56 -0
- package/dist/heart/daemon/health-monitor.js +131 -3
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
- 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 +78 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +37 -8
- package/dist/heart/daemon/log-tailer.js +79 -10
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/mcp-canary.js +297 -0
- package/dist/heart/daemon/message-router.js +45 -5
- package/dist/heart/daemon/migrate-to-desk.js +848 -0
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +14 -12
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/plugin-cli.js +432 -0
- package/dist/heart/daemon/process-manager.js +519 -40
- 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 +511 -0
- package/dist/heart/daemon/readiness-repair.js +365 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +41 -14
- package/dist/heart/daemon/runtime-metadata.js +2 -30
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +583 -43
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +1 -1
- package/dist/heart/daemon/socket-client.js +171 -12
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +338 -0
- package/dist/heart/daemon/task-scheduler.js +117 -39
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +255 -43
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -4
- package/dist/heart/external-events/router.js +121 -0
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +211 -0
- package/dist/heart/habits/habit-runtime-state.js +114 -0
- package/dist/heart/habits/habit-scheduler.js +411 -0
- package/dist/heart/habits/habit-session-summary.js +318 -0
- package/dist/heart/habits/habit-session.js +618 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +51 -57
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
- package/dist/heart/identity.js +174 -58
- package/dist/heart/kept-notes.js +289 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mail-import-discovery.js +353 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +96 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +375 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +37 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +197 -0
- package/dist/heart/mailbox/readers/agent-machine.js +428 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
- package/dist/heart/mailbox/readers/mail.js +375 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +815 -0
- package/dist/heart/mailbox/readers/sessions.js +232 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +696 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/orientation-frame.js +217 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +156 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +218 -0
- package/dist/heart/private-runtime/policy.js +214 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +273 -0
- package/dist/heart/provider-credentials.js +425 -0
- package/dist/heart/provider-failover.js +311 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-readiness-cache.js +119 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +139 -52
- package/dist/heart/providers/azure.js +23 -11
- package/dist/heart/providers/error-classification.js +127 -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 +26 -8
- package/dist/heart/providers/openai-codex-token.js +349 -0
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +389 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +17 -4
- package/dist/heart/session-activity.js +90 -24
- package/dist/heart/session-events.js +1133 -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-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +182 -0
- package/dist/heart/session-transcript.js +133 -0
- package/dist/heart/start-of-turn-packet.js +365 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/structured-output.js +196 -0
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +11 -7
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-description.js +155 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +450 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +426 -0
- package/dist/heart/versioning/ouro-version-manager.js +409 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/work-card.js +390 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +16 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +715 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +788 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +568 -0
- package/dist/mailroom/mbox-import.js +393 -0
- package/dist/mailroom/migration.js +164 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +233 -0
- package/dist/mailroom/search-cache.js +334 -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 +24 -2
- package/dist/mind/context.js +250 -101
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +68 -77
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/{associative-recall.js → note-search.js} +47 -58
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +13 -8
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1009 -169
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/record-paths.js +312 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/nerves/cli-logging.js +7 -1
- 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 +139 -5
- 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/review/cli-main.js +5 -0
- package/dist/nerves/review/cli.js +156 -0
- package/dist/nerves/review/core.js +152 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1042 -0
- package/dist/repertoire/bundle-templates.js +71 -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 +331 -0
- package/dist/repertoire/coding/feedback.js +218 -30
- package/dist/repertoire/coding/index.js +15 -3
- package/dist/repertoire/coding/manager.js +166 -10
- package/dist/repertoire/coding/spawner.js +58 -11
- package/dist/repertoire/coding/tools.js +242 -14
- package/dist/repertoire/coding/workbench-client.js +272 -0
- package/dist/repertoire/coding/workbench-manager.js +346 -0
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +178 -0
- package/dist/repertoire/desk/classifier.js +362 -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 +159 -27
- package/dist/repertoire/mcp-client.js +44 -3
- package/dist/repertoire/mcp-manager.js +259 -20
- package/dist/repertoire/mcp-tools.js +83 -0
- package/dist/repertoire/plugin-mcp.js +175 -0
- package/dist/repertoire/plugins.js +253 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +48 -4
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-a2a.js +699 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-awaiting.js +372 -0
- package/dist/repertoire/tools-base.js +63 -1095
- package/dist/repertoire/tools-bluebubbles.js +2 -0
- package/dist/repertoire/tools-bridge.js +144 -0
- package/dist/repertoire/tools-bundle.js +993 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-config.js +186 -0
- package/dist/repertoire/tools-continuity.js +252 -0
- package/dist/repertoire/tools-credential.js +383 -0
- package/dist/repertoire/tools-evolution.js +527 -0
- package/dist/repertoire/tools-files.js +344 -0
- package/dist/repertoire/tools-flight.js +290 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +3 -8
- package/dist/repertoire/tools-mail.js +1975 -0
- package/dist/repertoire/tools-notes.js +459 -0
- package/dist/repertoire/tools-obligations.js +143 -0
- package/dist/repertoire/tools-orientation.js +31 -0
- package/dist/repertoire/tools-record.js +482 -0
- package/dist/repertoire/tools-runtime.js +150 -0
- package/dist/repertoire/tools-session.js +881 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +224 -0
- package/dist/repertoire/tools-surface.js +355 -0
- package/dist/repertoire/tools-teams.js +12 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +982 -0
- package/dist/repertoire/tools-user-profile.js +146 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice.js +145 -0
- package/dist/repertoire/tools.js +250 -80
- 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 +594 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/a2a-entry.js +115 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +58 -0
- package/dist/senses/bluebubbles/active-turns.js +216 -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} +219 -18
- package/dist/senses/bluebubbles/entry.js +77 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +2732 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/replay.js +137 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/bluebubbles-meta-guard.js +39 -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 +607 -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 +85 -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 +100 -0
- package/dist/senses/cli.js +521 -214
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +160 -0
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +395 -0
- package/dist/senses/pipeline.js +873 -179
- package/dist/senses/private-runtime-worker.js +584 -0
- package/dist/senses/private-runtime.js +1145 -0
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +416 -0
- package/dist/senses/surface-tool.js +109 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +397 -112
- package/dist/senses/trust-gate.js +104 -9
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/elevenlabs.js +202 -0
- package/dist/senses/voice/floor-control.js +431 -0
- package/dist/senses/voice/floor-controller.js +115 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +29 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/outbound.js +190 -0
- package/dist/senses/voice/phone.js +33 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-trace.js +531 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +191 -0
- package/dist/senses/voice/twilio-phone-runtime.js +807 -0
- package/dist/senses/voice/twilio-phone.js +5077 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +81 -0
- package/dist/senses/voice-realtime-eval-command.js +99 -0
- package/dist/senses/voice-realtime-eval-entry.js +21 -0
- package/dist/senses/voice-twilio-entry.js +87 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +265 -0
- package/dist/util/frontmatter.js +69 -0
- package/package.json +57 -12
- package/skills/agent-commerce.md +113 -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 +99 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -351
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/update-hooks.js +0 -138
- package/dist/heart/safe-workspace.js +0 -228
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/friends/channel.js +0 -84
- package/dist/mind/friends/group-context.js +0 -144
- package/dist/mind/friends/resolver.js +0 -84
- package/dist/mind/friends/store-file.js +0 -190
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/trust-explanation.js +0 -74
- package/dist/mind/friends/types.js +0 -29
- package/dist/repertoire/tasks/board.js +0 -134
- package/dist/repertoire/tasks/index.js +0 -224
- package/dist/repertoire/tasks/lifecycle.js +0 -80
- package/dist/repertoire/tasks/middleware.js +0 -65
- package/dist/repertoire/tasks/parser.js +0 -173
- package/dist/repertoire/tasks/scanner.js +0 -132
- package/dist/repertoire/tasks/transitions.js +0 -144
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1180
- package/dist/senses/debug-activity.js +0 -148
- package/dist/senses/inner-dialog-worker.js +0 -90
- package/dist/senses/inner-dialog.js +0 -480
- package/subagents/README.md +0 -7
- /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-snake.md +0 -0
- /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +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
package/dist/senses/cli.js
CHANGED
|
@@ -33,9 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.MarkdownStreamer = exports.InputController = exports.Spinner = exports.wrapCliText = exports.formatEchoedInputSummary = void 0;
|
|
36
|
+
exports.InputQueue = exports.MarkdownStreamer = exports.InputController = exports.Spinner = exports.StreamingWordWrapper = exports.wrapCliText = exports.formatEchoedInputSummary = void 0;
|
|
37
37
|
exports.formatPendingPrefix = formatPendingPrefix;
|
|
38
38
|
exports.getCliContinuityIngressTexts = getCliContinuityIngressTexts;
|
|
39
|
+
exports.formatTimeAgo = formatTimeAgo;
|
|
40
|
+
exports.writeCliAsyncAssistantMessage = writeCliAsyncAssistantMessage;
|
|
41
|
+
exports.pauseActiveSpinner = pauseActiveSpinner;
|
|
42
|
+
exports.resumeActiveSpinner = resumeActiveSpinner;
|
|
43
|
+
exports.setActiveSpinner = setActiveSpinner;
|
|
39
44
|
exports.handleSigint = handleSigint;
|
|
40
45
|
exports.addHistory = addHistory;
|
|
41
46
|
exports.renderMarkdown = renderMarkdown;
|
|
@@ -51,38 +56,41 @@ const prompt_1 = require("../mind/prompt");
|
|
|
51
56
|
const phrases_1 = require("../mind/phrases");
|
|
52
57
|
const format_1 = require("../mind/format");
|
|
53
58
|
const config_1 = require("../heart/config");
|
|
59
|
+
const session_events_1 = require("../heart/session-events");
|
|
54
60
|
const context_1 = require("../mind/context");
|
|
55
61
|
const pending_1 = require("../mind/pending");
|
|
56
62
|
const commands_1 = require("./commands");
|
|
57
63
|
const identity_1 = require("../heart/identity");
|
|
58
64
|
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
59
65
|
const nerves_1 = require("../nerves");
|
|
60
|
-
const
|
|
61
|
-
const resolver_1 = require("../mind/friends/resolver");
|
|
62
|
-
const tokens_1 = require("../mind/friends/tokens");
|
|
66
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
63
67
|
const cli_logging_1 = require("../nerves/cli-logging");
|
|
64
68
|
const runtime_1 = require("../nerves/runtime");
|
|
65
69
|
const trust_gate_1 = require("./trust-gate");
|
|
66
70
|
const pipeline_1 = require("./pipeline");
|
|
67
|
-
const channel_1 = require("../mind/friends/channel");
|
|
68
71
|
const session_lock_1 = require("./session-lock");
|
|
69
|
-
const update_hooks_1 = require("../heart/
|
|
72
|
+
const update_hooks_1 = require("../heart/versioning/update-hooks");
|
|
70
73
|
const bundle_meta_1 = require("../heart/daemon/hooks/bundle-meta");
|
|
74
|
+
const agent_config_v2_1 = require("../heart/daemon/hooks/agent-config-v2");
|
|
71
75
|
const bundle_manifest_1 = require("../mind/bundle-manifest");
|
|
72
76
|
const cli_layout_1 = require("./cli-layout");
|
|
77
|
+
const image_paste_1 = require("./cli/image-paste");
|
|
78
|
+
const spinner_imperative_1 = require("./cli/spinner-imperative");
|
|
79
|
+
const tool_display_1 = require("./cli/tool-display");
|
|
73
80
|
var cli_layout_2 = require("./cli-layout");
|
|
74
81
|
Object.defineProperty(exports, "formatEchoedInputSummary", { enumerable: true, get: function () { return cli_layout_2.formatEchoedInputSummary; } });
|
|
75
82
|
Object.defineProperty(exports, "wrapCliText", { enumerable: true, get: function () { return cli_layout_2.wrapCliText; } });
|
|
83
|
+
Object.defineProperty(exports, "StreamingWordWrapper", { enumerable: true, get: function () { return cli_layout_2.StreamingWordWrapper; } });
|
|
76
84
|
/**
|
|
77
85
|
* Format pending messages as content-prefix strings for injection into
|
|
78
86
|
* the next user message. Self-messages (from === agentName) become
|
|
79
|
-
* `[
|
|
87
|
+
* `[private-runtime note: {content}]`, inter-agent messages become
|
|
80
88
|
* `[message from {name}: {content}]`.
|
|
81
89
|
*/
|
|
82
90
|
function formatPendingPrefix(messages, agentName) {
|
|
83
91
|
return messages
|
|
84
92
|
.map((msg) => msg.from === agentName
|
|
85
|
-
? `[
|
|
93
|
+
? `[private-runtime note: ${msg.content}]`
|
|
86
94
|
: `[message from ${msg.from}: ${msg.content}]`)
|
|
87
95
|
.join("\n");
|
|
88
96
|
}
|
|
@@ -90,68 +98,46 @@ function getCliContinuityIngressTexts(input) {
|
|
|
90
98
|
const trimmed = input.trim();
|
|
91
99
|
return trimmed ? [trimmed] : [];
|
|
92
100
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
/* v8 ignore next -- race guard: timer callback fires after stop() @preserve */
|
|
121
|
-
if (this.stopped)
|
|
122
|
-
return;
|
|
123
|
-
process.stderr.write(`\r\x1b[K${this.frames[this.i]} ${this.msg}... `);
|
|
124
|
-
this.i = (this.i + 1) % this.frames.length;
|
|
125
|
-
}
|
|
126
|
-
rotatePhrase() {
|
|
127
|
-
/* v8 ignore next -- race guard: timer callback fires after stop() @preserve */
|
|
128
|
-
if (this.stopped)
|
|
129
|
-
return;
|
|
130
|
-
const next = (0, phrases_1.pickPhrase)(this.phrases, this.lastPhrase);
|
|
131
|
-
this.lastPhrase = next;
|
|
132
|
-
this.msg = next;
|
|
133
|
-
}
|
|
134
|
-
stop(ok) {
|
|
135
|
-
this.stopped = true;
|
|
136
|
-
if (this.iv) {
|
|
137
|
-
clearInterval(this.iv);
|
|
138
|
-
this.iv = null;
|
|
139
|
-
}
|
|
140
|
-
if (this.piv) {
|
|
141
|
-
clearInterval(this.piv);
|
|
142
|
-
this.piv = null;
|
|
143
|
-
}
|
|
144
|
-
process.stderr.write("\r\x1b[K");
|
|
145
|
-
/* v8 ignore next -- ok parameter currently unused by callers @preserve */
|
|
146
|
-
if (ok)
|
|
147
|
-
process.stderr.write(`\x1b[32m\u2713\x1b[0m ${ok}\n`);
|
|
148
|
-
}
|
|
149
|
-
fail(msg) {
|
|
150
|
-
this.stop();
|
|
151
|
-
process.stderr.write(`\x1b[31m\u2717\x1b[0m ${msg}\n`);
|
|
101
|
+
/* v8 ignore start -- cosmetic time formatting @preserve */
|
|
102
|
+
function formatTimeAgo(date) {
|
|
103
|
+
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
104
|
+
if (seconds < 60)
|
|
105
|
+
return "just now";
|
|
106
|
+
const minutes = Math.floor(seconds / 60);
|
|
107
|
+
if (minutes < 60)
|
|
108
|
+
return `${minutes}m ago`;
|
|
109
|
+
const hours = Math.floor(minutes / 60);
|
|
110
|
+
if (hours < 24)
|
|
111
|
+
return `${hours}h ago`;
|
|
112
|
+
const days = Math.floor(hours / 24);
|
|
113
|
+
return `${days}d ago`;
|
|
114
|
+
}
|
|
115
|
+
const CLI_PROMPT = "\x1b[36m) \x1b[0m";
|
|
116
|
+
function writeCliAsyncAssistantMessage(rl, message, stdout = process.stdout) {
|
|
117
|
+
const rlInt = rl;
|
|
118
|
+
const currentLine = rlInt.line ?? "";
|
|
119
|
+
const currentCursor = rlInt.cursor ?? currentLine.length;
|
|
120
|
+
stdout.write("\r\x1b[K");
|
|
121
|
+
stdout.write(`${renderMarkdown(message)}\n`);
|
|
122
|
+
stdout.write(CLI_PROMPT);
|
|
123
|
+
if (!currentLine)
|
|
124
|
+
return;
|
|
125
|
+
stdout.write(currentLine);
|
|
126
|
+
if (currentCursor < currentLine.length) {
|
|
127
|
+
readline.cursorTo(process.stdout, 2 + currentCursor);
|
|
152
128
|
}
|
|
153
129
|
}
|
|
154
|
-
|
|
130
|
+
// Module-level active spinner for log coordination.
|
|
131
|
+
// The terminal log sink calls these to avoid interleaving with spinner output.
|
|
132
|
+
let _activeSpinner = null;
|
|
133
|
+
/* v8 ignore start -- spinner coordination: exercised at runtime, not unit-testable without real terminal @preserve */
|
|
134
|
+
function pauseActiveSpinner() { _activeSpinner?.pause(); }
|
|
135
|
+
function resumeActiveSpinner() { _activeSpinner?.resume(); }
|
|
136
|
+
/* v8 ignore stop */
|
|
137
|
+
function setActiveSpinner(s) { _activeSpinner = s; }
|
|
138
|
+
// Re-export ImperativeSpinner as Spinner for backward compatibility
|
|
139
|
+
var spinner_imperative_2 = require("./cli/spinner-imperative");
|
|
140
|
+
Object.defineProperty(exports, "Spinner", { enumerable: true, get: function () { return spinner_imperative_2.ImperativeSpinner; } });
|
|
155
141
|
// Input controller: pauses readline during model/tool execution.
|
|
156
142
|
// Does NOT touch raw mode — readline with terminal:true manages raw mode
|
|
157
143
|
// internally. Touching it causes ^C to be echoed by the terminal driver.
|
|
@@ -319,60 +305,65 @@ function createCliCallbacks() {
|
|
|
319
305
|
meta: {},
|
|
320
306
|
});
|
|
321
307
|
let currentSpinner = null;
|
|
322
|
-
|
|
308
|
+
function setSpinner(s) { currentSpinner = s; setActiveSpinner(s); }
|
|
323
309
|
let hadToolRun = false;
|
|
324
310
|
let textDirty = false; // true when text/reasoning was written without a trailing newline
|
|
325
311
|
const streamer = new MarkdownStreamer();
|
|
312
|
+
const wrapper = new cli_layout_1.StreamingWordWrapper();
|
|
326
313
|
return {
|
|
327
314
|
onModelStart: () => {
|
|
328
315
|
currentSpinner?.stop();
|
|
329
|
-
|
|
330
|
-
hadReasoning = false;
|
|
316
|
+
setSpinner(null);
|
|
331
317
|
textDirty = false;
|
|
332
318
|
streamer.reset();
|
|
319
|
+
wrapper.reset();
|
|
333
320
|
const phrases = (0, phrases_1.getPhrases)();
|
|
334
321
|
const pool = hadToolRun ? phrases.followup : phrases.thinking;
|
|
335
322
|
const first = (0, phrases_1.pickPhrase)(pool);
|
|
336
|
-
|
|
323
|
+
setSpinner(new spinner_imperative_1.ImperativeSpinner(first, pool));
|
|
337
324
|
currentSpinner.start();
|
|
338
325
|
},
|
|
339
326
|
onModelStreamStart: () => {
|
|
340
327
|
// No-op: content callbacks (onTextChunk, onReasoningChunk) handle
|
|
341
328
|
// stopping the spinner. onModelStreamStart fires too early and
|
|
342
|
-
// doesn't fire at all for
|
|
329
|
+
// doesn't fire at all for settle tool streaming.
|
|
343
330
|
},
|
|
344
331
|
onClearText: () => {
|
|
345
332
|
streamer.reset();
|
|
333
|
+
wrapper.reset();
|
|
346
334
|
},
|
|
347
335
|
onTextChunk: (text) => {
|
|
348
|
-
// Stop spinner if still running —
|
|
336
|
+
// Stop spinner if still running — settle streaming and Anthropic
|
|
349
337
|
// tool-only responses bypass onModelStreamStart, so the spinner would
|
|
350
338
|
// otherwise keep running (and its \r writes overwrite response text).
|
|
351
339
|
if (currentSpinner) {
|
|
352
340
|
currentSpinner.stop();
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
if (hadReasoning) {
|
|
356
|
-
// Single newline to separate reasoning from reply — reasoning
|
|
357
|
-
// output often ends with its own trailing newline(s)
|
|
358
|
-
process.stdout.write("\n");
|
|
359
|
-
hadReasoning = false;
|
|
341
|
+
setSpinner(null);
|
|
360
342
|
}
|
|
361
343
|
const rendered = streamer.push(text);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (currentSpinner) {
|
|
368
|
-
currentSpinner.stop();
|
|
369
|
-
currentSpinner = null;
|
|
344
|
+
/* v8 ignore start -- wrapper integration: tested via cli.test.ts onTextChunk tests @preserve */
|
|
345
|
+
if (rendered) {
|
|
346
|
+
const wrapped = wrapper.push(rendered);
|
|
347
|
+
if (wrapped)
|
|
348
|
+
process.stdout.write(wrapped);
|
|
370
349
|
}
|
|
371
|
-
|
|
372
|
-
process.stdout.write(`\x1b[2m${text}\x1b[0m`);
|
|
350
|
+
/* v8 ignore stop */
|
|
373
351
|
textDirty = text.length > 0 && !text.endsWith("\n");
|
|
374
352
|
},
|
|
353
|
+
flushNow: () => {
|
|
354
|
+
// CLI flushes immediately on each onTextChunk; nothing buffered to push.
|
|
355
|
+
},
|
|
356
|
+
onReasoningChunk: (_text) => {
|
|
357
|
+
// Keep reasoning private in the CLI surface. The spinner continues to
|
|
358
|
+
// represent active thinking until actual tool or answer output arrives.
|
|
359
|
+
},
|
|
375
360
|
onToolStart: (_name, _args) => {
|
|
361
|
+
// speak is flow-control: its visible output is the message itself (delivered
|
|
362
|
+
// via onTextChunk/flushNow). Do NOT start a tool spinner — that would write
|
|
363
|
+
// a "running speak..." phrase to stderr right before the actual message
|
|
364
|
+
// arrives, which is the visual churn the user explicitly does not want.
|
|
365
|
+
if (_name === "speak")
|
|
366
|
+
return;
|
|
376
367
|
// Stop the model-start spinner: when the model returns only tool calls
|
|
377
368
|
// (no content/reasoning), onModelStreamStart never fires, so the old
|
|
378
369
|
// spinner's intervals would leak.
|
|
@@ -385,31 +376,29 @@ function createCliCallbacks() {
|
|
|
385
376
|
}
|
|
386
377
|
const toolPhrases = (0, phrases_1.getPhrases)().tool;
|
|
387
378
|
const first = (0, phrases_1.pickPhrase)(toolPhrases);
|
|
388
|
-
|
|
379
|
+
setSpinner(new spinner_imperative_1.ImperativeSpinner(first, toolPhrases));
|
|
389
380
|
currentSpinner.start();
|
|
390
381
|
hadToolRun = true;
|
|
391
382
|
},
|
|
392
383
|
onToolEnd: (name, argSummary, success) => {
|
|
393
384
|
currentSpinner?.stop();
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const color = success ? "\x1b[32m" : "\x1b[31m";
|
|
397
|
-
process.stderr.write(`${color}${msg}\x1b[0m\n`);
|
|
385
|
+
setSpinner(null);
|
|
386
|
+
(0, tool_display_1.writeToolEnd)(name, argSummary, success);
|
|
398
387
|
},
|
|
399
388
|
onError: (error, severity) => {
|
|
400
389
|
if (severity === "transient") {
|
|
401
390
|
currentSpinner?.fail(error.message);
|
|
402
|
-
|
|
391
|
+
setSpinner(null);
|
|
403
392
|
}
|
|
404
393
|
else {
|
|
405
394
|
currentSpinner?.stop();
|
|
406
|
-
|
|
395
|
+
setSpinner(null);
|
|
407
396
|
process.stderr.write(`\x1b[31m${(0, format_1.formatError)(error)}\x1b[0m\n`);
|
|
408
397
|
}
|
|
409
398
|
},
|
|
410
399
|
onKick: () => {
|
|
411
400
|
currentSpinner?.stop();
|
|
412
|
-
|
|
401
|
+
setSpinner(null);
|
|
413
402
|
if (textDirty) {
|
|
414
403
|
process.stdout.write("\n");
|
|
415
404
|
textDirty = false;
|
|
@@ -418,10 +407,18 @@ function createCliCallbacks() {
|
|
|
418
407
|
},
|
|
419
408
|
flushMarkdown: () => {
|
|
420
409
|
currentSpinner?.stop();
|
|
421
|
-
|
|
410
|
+
setSpinner(null);
|
|
411
|
+
/* v8 ignore start -- wrapper flush: tested via cli.test.ts flushMarkdown tests @preserve */
|
|
422
412
|
const remaining = streamer.flush();
|
|
423
|
-
if (remaining)
|
|
424
|
-
|
|
413
|
+
if (remaining) {
|
|
414
|
+
const wrapped = wrapper.push(remaining);
|
|
415
|
+
if (wrapped)
|
|
416
|
+
process.stdout.write(wrapped);
|
|
417
|
+
}
|
|
418
|
+
const tail = wrapper.flush();
|
|
419
|
+
if (tail)
|
|
420
|
+
process.stdout.write(tail);
|
|
421
|
+
/* v8 ignore stop */
|
|
425
422
|
},
|
|
426
423
|
};
|
|
427
424
|
}
|
|
@@ -462,29 +459,243 @@ async function* createDebouncedLines(source, debounceMs) {
|
|
|
462
459
|
yield lines.join("\n");
|
|
463
460
|
}
|
|
464
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Async queue that bridges push-based Ink input to pull-based async iteration.
|
|
464
|
+
* Input from Ink's onSubmit callback is pushed; the business logic loop awaits via for-await.
|
|
465
|
+
*/
|
|
466
|
+
class InputQueue {
|
|
467
|
+
queue = [];
|
|
468
|
+
resolve = null;
|
|
469
|
+
done = false;
|
|
470
|
+
push(input) {
|
|
471
|
+
if (this.done)
|
|
472
|
+
return;
|
|
473
|
+
if (this.resolve) {
|
|
474
|
+
const r = this.resolve;
|
|
475
|
+
this.resolve = null;
|
|
476
|
+
r({ value: input, done: false });
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
this.queue.push(input);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
close() {
|
|
483
|
+
this.done = true;
|
|
484
|
+
if (this.resolve) {
|
|
485
|
+
const r = this.resolve;
|
|
486
|
+
this.resolve = null;
|
|
487
|
+
r({ value: undefined, done: true });
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/** Drain all buffered items, leaving any pending async awaiter untouched. */
|
|
491
|
+
drainAll() {
|
|
492
|
+
const items = [...this.queue];
|
|
493
|
+
this.queue = [];
|
|
494
|
+
return items;
|
|
495
|
+
}
|
|
496
|
+
[Symbol.asyncIterator]() {
|
|
497
|
+
return {
|
|
498
|
+
next: () => {
|
|
499
|
+
if (this.queue.length > 0) {
|
|
500
|
+
return Promise.resolve({ value: this.queue.shift(), done: false });
|
|
501
|
+
}
|
|
502
|
+
if (this.done) {
|
|
503
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
504
|
+
}
|
|
505
|
+
return new Promise((resolve) => {
|
|
506
|
+
this.resolve = resolve;
|
|
507
|
+
});
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
exports.InputQueue = InputQueue;
|
|
465
513
|
async function runCliSession(options) {
|
|
466
514
|
/* v8 ignore start -- integration: runCliSession is interactive, tested via E2E @preserve */
|
|
467
|
-
const pasteDebounceMs = options.pasteDebounceMs ?? 50;
|
|
468
515
|
const registry = (0, commands_1.createCommandRegistry)();
|
|
469
516
|
if (!options.disableCommands) {
|
|
470
517
|
(0, commands_1.registerDefaultCommands)(registry);
|
|
471
518
|
}
|
|
472
519
|
const messages = options.messages
|
|
473
|
-
?? [{ role: "system", content: await (0, prompt_1.buildSystem)("cli") }];
|
|
474
|
-
|
|
475
|
-
const
|
|
520
|
+
?? [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("cli")) }];
|
|
521
|
+
// ─── Rendering: TUI (Ink + Static) for TTY, imperative for tests/pipes ───
|
|
522
|
+
const useTui = !options._testInputSource && process.stdin.isTTY === true;
|
|
476
523
|
let currentAbort = null;
|
|
477
|
-
const history = [];
|
|
478
524
|
let closed = false;
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
525
|
+
// eslint-disable-next-line prefer-const -- set by onImageMap callback during input
|
|
526
|
+
let pendingImages = null;
|
|
527
|
+
let cliCallbacks;
|
|
528
|
+
let tuiStore = null;
|
|
529
|
+
let inkRef = null;
|
|
530
|
+
const inputQueue = useTui ? new InputQueue() : null;
|
|
531
|
+
let rl = null;
|
|
532
|
+
if (useTui) {
|
|
533
|
+
try {
|
|
534
|
+
const [ink, React, tuiMod, storeMod] = await Promise.all([
|
|
535
|
+
Promise.resolve().then(() => __importStar(require("ink"))),
|
|
536
|
+
Promise.resolve().then(() => __importStar(require("react"))),
|
|
537
|
+
Promise.resolve().then(() => __importStar(require("./cli/ouro-tui"))),
|
|
538
|
+
Promise.resolve().then(() => __importStar(require("./cli/tui-store"))),
|
|
539
|
+
]);
|
|
540
|
+
const { OuroTui } = tuiMod;
|
|
541
|
+
const { TuiStore, createTuiCallbacks } = storeMod;
|
|
542
|
+
tuiStore = new TuiStore();
|
|
543
|
+
cliCallbacks = createTuiCallbacks(tuiStore);
|
|
544
|
+
// Seed input history from previous session (for up/down arrows) — NOT display
|
|
545
|
+
const prevUserMsgs = messages
|
|
546
|
+
.filter((msg) => msg.role === "user" && typeof msg.content === "string")
|
|
547
|
+
.map(msg => msg.content);
|
|
548
|
+
tuiStore.seedHistory(prevUserMsgs);
|
|
549
|
+
// Show session resume context: last 2 exchanges as normal messages
|
|
550
|
+
if (messages.length > 1) {
|
|
551
|
+
const userAssistantMsgs = messages.filter((m) => (m.role === "user" || m.role === "assistant") && typeof m.content === "string" && m.content.trim().length > 0);
|
|
552
|
+
// Extract last 2 exchanges (up to 4 messages)
|
|
553
|
+
const lastExchanges = [];
|
|
554
|
+
for (let i = userAssistantMsgs.length - 1; i >= 0 && lastExchanges.length < 4; i--) {
|
|
555
|
+
lastExchanges.unshift({ role: userAssistantMsgs[i].role, content: userAssistantMsgs[i].content });
|
|
556
|
+
}
|
|
557
|
+
tuiStore.addResumeMessages(lastExchanges);
|
|
558
|
+
}
|
|
559
|
+
// Compute resumeInfo for header banner
|
|
560
|
+
const resumeInfo = messages.length > 1
|
|
561
|
+
? {
|
|
562
|
+
messageCount: messages.filter(m => m.role === "user" || m.role === "assistant").length,
|
|
563
|
+
timeAgo: options.lastActivityAt ? formatTimeAgo(new Date(options.lastActivityAt)) : "unknown",
|
|
564
|
+
}
|
|
565
|
+
: undefined;
|
|
566
|
+
// Ctrl-C state machine (Claude Code behavior):
|
|
567
|
+
// During generation: abort current request
|
|
568
|
+
// Idle with text: clear input
|
|
569
|
+
// Idle empty (first): warn
|
|
570
|
+
// Idle empty (second): exit
|
|
571
|
+
let ctrlCWarned = false;
|
|
572
|
+
let ctrlCTimer = null;
|
|
573
|
+
const handleCtrlC = (hasInput) => {
|
|
574
|
+
if (currentAbort) {
|
|
575
|
+
currentAbort.abort();
|
|
576
|
+
ctrlCWarned = false;
|
|
577
|
+
return "abort";
|
|
578
|
+
}
|
|
579
|
+
if (hasInput) {
|
|
580
|
+
ctrlCWarned = false;
|
|
581
|
+
return "clear";
|
|
582
|
+
}
|
|
583
|
+
if (ctrlCWarned) {
|
|
584
|
+
ctrlCWarned = false;
|
|
585
|
+
if (ctrlCTimer) {
|
|
586
|
+
clearTimeout(ctrlCTimer);
|
|
587
|
+
ctrlCTimer = null;
|
|
588
|
+
}
|
|
589
|
+
closed = true;
|
|
590
|
+
inputQueue.close();
|
|
591
|
+
return "exit";
|
|
592
|
+
}
|
|
593
|
+
ctrlCWarned = true;
|
|
594
|
+
// Reset after 2 seconds — must press twice within window
|
|
595
|
+
ctrlCTimer = setTimeout(() => { ctrlCWarned = false; }, 2000);
|
|
596
|
+
return "warn";
|
|
597
|
+
};
|
|
598
|
+
// TUI root: subscribes to store, passes props to OuroTui
|
|
599
|
+
// Elapsed timer is local React state (no store.notify overhead)
|
|
600
|
+
const storeRef = tuiStore;
|
|
601
|
+
function TuiRoot() {
|
|
602
|
+
const [, forceUpdate] = React.useState(0);
|
|
603
|
+
const [elapsed, setElapsed] = React.useState(0);
|
|
604
|
+
React.useEffect(() => storeRef.subscribe(() => {
|
|
605
|
+
forceUpdate((n) => n + 1);
|
|
606
|
+
// Reset ctrlC warning on any state change (new turn, etc.)
|
|
607
|
+
}), []);
|
|
608
|
+
React.useEffect(() => {
|
|
609
|
+
const iv = setInterval(() => setElapsed(storeRef.getElapsed()), 1000);
|
|
610
|
+
return () => clearInterval(iv);
|
|
611
|
+
}, []);
|
|
612
|
+
return React.createElement(OuroTui, {
|
|
613
|
+
agentName: options.agentName,
|
|
614
|
+
model: (0, identity_1.loadAgentConfig)().humanFacing?.model ?? "",
|
|
615
|
+
completedMessages: storeRef.completedMessages,
|
|
616
|
+
inputHistory: storeRef.inputHistory,
|
|
617
|
+
queuedInputs: storeRef.queuedInputs,
|
|
618
|
+
live: storeRef.live,
|
|
619
|
+
elapsedSeconds: elapsed,
|
|
620
|
+
contextPercent: 0,
|
|
621
|
+
onSubmit: (text) => { ctrlCWarned = false; inputQueue.push(text); storeRef.enqueueInput(text); },
|
|
622
|
+
onCtrlC: handleCtrlC,
|
|
623
|
+
onPopQueue: () => { const items = storeRef.popAllQueuedForEditing(); inputQueue.drainAll(); return items; },
|
|
624
|
+
headerShown: storeRef.headerShown,
|
|
625
|
+
cwd: process.cwd().replace(process.env.HOME ?? "", "~"),
|
|
626
|
+
resumeInfo,
|
|
627
|
+
onImageMap: (images) => { pendingImages = images; },
|
|
628
|
+
onHistoryAdd: (text) => { storeRef.addToHistoryOnly(text); },
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
inkRef = ink.render(React.createElement(TuiRoot), { exitOnCtrlC: false, patchConsole: false });
|
|
632
|
+
}
|
|
633
|
+
catch (err) {
|
|
634
|
+
// Ink failed to load (CJS compat, missing deps, etc.) — fall through to imperative
|
|
635
|
+
(0, runtime_1.emitNervesEvent)({
|
|
636
|
+
component: "senses",
|
|
637
|
+
event: "senses.tui_fallback",
|
|
638
|
+
message: `TUI failed to load, falling back to imperative: ${err instanceof Error ? err.message : String(err)}`,
|
|
639
|
+
meta: {},
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
// Fallback to imperative callbacks if TUI didn't initialize
|
|
644
|
+
if (!tuiStore) {
|
|
645
|
+
cliCallbacks = createCliCallbacks();
|
|
646
|
+
if (options.banner !== false) {
|
|
647
|
+
const bannerText = typeof options.banner === "string"
|
|
648
|
+
? options.banner
|
|
649
|
+
: `${options.agentName} (type /commands for help)`;
|
|
650
|
+
// eslint-disable-next-line no-console -- terminal UX: startup banner
|
|
651
|
+
console.log(`\n${bannerText}\n`);
|
|
652
|
+
}
|
|
486
653
|
}
|
|
487
|
-
|
|
654
|
+
// Display helpers: route to TUI store or imperative stderr/stdout
|
|
655
|
+
const display = {
|
|
656
|
+
error: (msg) => {
|
|
657
|
+
if (tuiStore)
|
|
658
|
+
tuiStore.setError(msg);
|
|
659
|
+
else
|
|
660
|
+
process.stderr.write(`\x1b[31m${msg}\x1b[0m\n`);
|
|
661
|
+
},
|
|
662
|
+
warn: (msg) => {
|
|
663
|
+
if (tuiStore)
|
|
664
|
+
tuiStore.setError(msg); // TUI shows warnings as errors (amber color handled in component)
|
|
665
|
+
else
|
|
666
|
+
process.stderr.write(`\x1b[33m${msg}\x1b[0m\n`);
|
|
667
|
+
},
|
|
668
|
+
text: (msg) => {
|
|
669
|
+
if (tuiStore)
|
|
670
|
+
tuiStore.appendText(msg);
|
|
671
|
+
else
|
|
672
|
+
process.stdout.write(`${msg}\n`);
|
|
673
|
+
},
|
|
674
|
+
suppressInput: () => {
|
|
675
|
+
if (tuiStore)
|
|
676
|
+
tuiStore.suppressInput();
|
|
677
|
+
},
|
|
678
|
+
restoreInput: () => {
|
|
679
|
+
if (tuiStore)
|
|
680
|
+
tuiStore.restoreInput();
|
|
681
|
+
},
|
|
682
|
+
};
|
|
683
|
+
const effectiveToolContext = {
|
|
684
|
+
signin: options.toolContext?.signin ?? (async () => undefined),
|
|
685
|
+
...options.toolContext,
|
|
686
|
+
codingFeedback: {
|
|
687
|
+
send: async (message) => {
|
|
688
|
+
const assistantMessage = {
|
|
689
|
+
role: "assistant",
|
|
690
|
+
content: message,
|
|
691
|
+
};
|
|
692
|
+
messages.push(assistantMessage);
|
|
693
|
+
await options.onAsyncAssistantMessage?.(messages, assistantMessage);
|
|
694
|
+
display.text(message);
|
|
695
|
+
await options.toolContext?.codingFeedback?.send(message);
|
|
696
|
+
},
|
|
697
|
+
},
|
|
698
|
+
};
|
|
488
699
|
// exitOnToolCall machinery: wrap execTool to detect target tool
|
|
489
700
|
let exitToolResult;
|
|
490
701
|
let exitToolFired = false;
|
|
@@ -495,8 +706,6 @@ async function runCliSession(options) {
|
|
|
495
706
|
if (name === options.exitOnToolCall) {
|
|
496
707
|
exitToolResult = result;
|
|
497
708
|
exitToolFired = true;
|
|
498
|
-
// Abort immediately so the model doesn't generate more output
|
|
499
|
-
// (e.g. reasoning about calling final_answer after complete_adoption)
|
|
500
709
|
currentAbort?.abort();
|
|
501
710
|
}
|
|
502
711
|
return result;
|
|
@@ -504,28 +713,6 @@ async function runCliSession(options) {
|
|
|
504
713
|
: resolvedExecTool;
|
|
505
714
|
// Resolve toolChoiceRequired: use explicit option if set, else fall back to toggle
|
|
506
715
|
const getEffectiveToolChoiceRequired = () => options.toolChoiceRequired !== undefined ? options.toolChoiceRequired : (0, commands_1.getToolChoiceRequired)();
|
|
507
|
-
// Ctrl-C at the input prompt: clear line or warn/exit
|
|
508
|
-
rl.on("SIGINT", () => {
|
|
509
|
-
const rlInt = rl;
|
|
510
|
-
const currentLine = rlInt.line || "";
|
|
511
|
-
const result = handleSigint(rl, currentLine);
|
|
512
|
-
if (result === "clear") {
|
|
513
|
-
rlInt.line = "";
|
|
514
|
-
rlInt.cursor = 0;
|
|
515
|
-
process.stdout.write("\r\x1b[K\x1b[36m> \x1b[0m");
|
|
516
|
-
}
|
|
517
|
-
else if (result === "warn") {
|
|
518
|
-
rlInt.line = "";
|
|
519
|
-
rlInt.cursor = 0;
|
|
520
|
-
process.stdout.write("\r\x1b[K");
|
|
521
|
-
process.stderr.write("press Ctrl-C again to exit\n");
|
|
522
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
523
|
-
}
|
|
524
|
-
else {
|
|
525
|
-
rl.close();
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
const debouncedLines = (source) => createDebouncedLines(source, pasteDebounceMs);
|
|
529
716
|
(0, runtime_1.emitNervesEvent)({
|
|
530
717
|
component: "senses",
|
|
531
718
|
event: "senses.cli_session_start",
|
|
@@ -538,7 +725,7 @@ async function runCliSession(options) {
|
|
|
538
725
|
if (options.autoFirstTurn && messages.length > 0 && messages[messages.length - 1]?.role === "user") {
|
|
539
726
|
currentAbort = new AbortController();
|
|
540
727
|
const traceId = (0, nerves_1.createTraceId)();
|
|
541
|
-
|
|
728
|
+
display.suppressInput();
|
|
542
729
|
let result;
|
|
543
730
|
try {
|
|
544
731
|
result = await (0, core_1.runAgent)(messages, cliCallbacks, options.skipSystemPromptRefresh ? undefined : "cli", currentAbort.signal, {
|
|
@@ -546,119 +733,145 @@ async function runCliSession(options) {
|
|
|
546
733
|
traceId,
|
|
547
734
|
tools: options.tools,
|
|
548
735
|
execTool: wrappedExecTool,
|
|
549
|
-
toolContext:
|
|
736
|
+
toolContext: effectiveToolContext,
|
|
550
737
|
});
|
|
551
738
|
}
|
|
552
739
|
catch (err) {
|
|
553
|
-
// AbortError (Ctrl-C) -- silently continue to prompt
|
|
554
|
-
// All other errors: show the user what happened
|
|
555
740
|
if (!(err instanceof DOMException && err.name === "AbortError")) {
|
|
556
|
-
|
|
741
|
+
display.error(err instanceof Error ? err.message : String(err));
|
|
557
742
|
}
|
|
558
743
|
}
|
|
559
744
|
cliCallbacks.flushMarkdown();
|
|
560
|
-
|
|
745
|
+
display.restoreInput();
|
|
561
746
|
currentAbort = null;
|
|
562
747
|
if (exitToolFired) {
|
|
563
748
|
exitReason = "tool_exit";
|
|
564
|
-
|
|
749
|
+
closed = true;
|
|
565
750
|
}
|
|
566
751
|
else {
|
|
567
752
|
const lastMsg = messages[messages.length - 1];
|
|
568
753
|
if (lastMsg?.role === "assistant" && !(typeof lastMsg.content === "string" ? lastMsg.content : "").trim()) {
|
|
569
|
-
|
|
754
|
+
display.warn("(empty response)");
|
|
570
755
|
}
|
|
571
|
-
process.stdout.write("\n\n");
|
|
572
756
|
if (options.onTurnEnd) {
|
|
573
757
|
await options.onTurnEnd(messages, result ?? { usage: undefined });
|
|
574
758
|
}
|
|
575
759
|
}
|
|
576
760
|
}
|
|
577
|
-
if (!exitToolFired) {
|
|
578
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
579
|
-
}
|
|
580
761
|
try {
|
|
581
|
-
for
|
|
762
|
+
// Input source: TUI queue for Ink, test source for tests, readline fallback
|
|
763
|
+
let inputSource;
|
|
764
|
+
let inputCtrl = null;
|
|
765
|
+
if (tuiStore && inputQueue) {
|
|
766
|
+
// TUI path: input comes from Ink's onSubmit → InputQueue
|
|
767
|
+
inputSource = inputQueue;
|
|
768
|
+
}
|
|
769
|
+
else if (options._testInputSource) {
|
|
770
|
+
inputSource = options._testInputSource;
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
// Imperative fallback: readline
|
|
774
|
+
const isTTY = process.stdin.isTTY === true;
|
|
775
|
+
rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: isTTY });
|
|
776
|
+
inputCtrl = new InputController(rl);
|
|
777
|
+
inputSource = rl;
|
|
778
|
+
process.stdout.write(CLI_PROMPT);
|
|
779
|
+
}
|
|
780
|
+
for await (const input of inputSource) {
|
|
582
781
|
if (closed)
|
|
583
782
|
break;
|
|
584
|
-
if (!input.trim())
|
|
585
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
783
|
+
if (!input.trim())
|
|
586
784
|
continue;
|
|
587
|
-
|
|
785
|
+
// Remove from TUI queue display as the agent picks up this message
|
|
786
|
+
tuiStore?.dequeueInput(input);
|
|
588
787
|
// Optional input gate (e.g. trust gate in main)
|
|
589
788
|
if (options.onInput) {
|
|
590
789
|
const gate = options.onInput(input);
|
|
591
790
|
if (!gate.allowed) {
|
|
592
791
|
if (gate.reply) {
|
|
593
|
-
|
|
792
|
+
display.text(gate.reply);
|
|
594
793
|
}
|
|
595
794
|
if (closed)
|
|
596
795
|
break;
|
|
597
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
598
796
|
continue;
|
|
599
797
|
}
|
|
600
798
|
}
|
|
601
|
-
// Check for slash commands
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
if (dispatchResult.
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
console.log("session cleared");
|
|
615
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
616
|
-
continue;
|
|
617
|
-
}
|
|
618
|
-
else if (dispatchResult.result.action === "response") {
|
|
619
|
-
// eslint-disable-next-line no-console -- terminal UX: command dispatch result
|
|
620
|
-
console.log(dispatchResult.result.message || "");
|
|
621
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
622
|
-
continue;
|
|
799
|
+
// Check for slash commands (legacy path only — pipeline handles commands for runTurn path)
|
|
800
|
+
if (!options.runTurn) {
|
|
801
|
+
const parsed = (0, commands_1.parseSlashCommand)(input);
|
|
802
|
+
if (parsed) {
|
|
803
|
+
const dispatchResult = registry.dispatch(parsed.command, { channel: "cli" });
|
|
804
|
+
if (dispatchResult.handled && dispatchResult.result) {
|
|
805
|
+
if (dispatchResult.result.action === "exit") {
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
else if (dispatchResult.result.action === "response") {
|
|
809
|
+
display.text(dispatchResult.result.message || "");
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
623
812
|
}
|
|
624
813
|
}
|
|
625
814
|
}
|
|
626
|
-
//
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
addHistory(history, input);
|
|
815
|
+
// Track user message in TUI for display
|
|
816
|
+
if (tuiStore)
|
|
817
|
+
tuiStore.addUserMessage(input);
|
|
630
818
|
currentAbort = new AbortController();
|
|
631
|
-
|
|
819
|
+
if (tuiStore)
|
|
820
|
+
tuiStore.suppressInput();
|
|
821
|
+
inputCtrl?.suppress(() => { currentAbort?.abort(); });
|
|
822
|
+
// Resolve pending image content before the turn executes
|
|
823
|
+
let contentParts = null;
|
|
824
|
+
const currentImages = pendingImages;
|
|
825
|
+
if (currentImages && currentImages.size > 0) {
|
|
826
|
+
contentParts = await (0, image_paste_1.resolveImageContent)(input, currentImages);
|
|
827
|
+
pendingImages = null;
|
|
828
|
+
}
|
|
632
829
|
let result;
|
|
633
830
|
try {
|
|
634
831
|
if (options.runTurn) {
|
|
635
832
|
// Pipeline-based turn: the runTurn callback handles user message assembly,
|
|
636
833
|
// pending drain, trust gate, runAgent, postTurn, and token accumulation.
|
|
637
|
-
result = await options.runTurn(messages, input, cliCallbacks, currentAbort.signal);
|
|
834
|
+
result = await options.runTurn(messages, input, cliCallbacks, currentAbort.signal, effectiveToolContext, contentParts ?? undefined);
|
|
835
|
+
// Handle pipeline-intercepted commands with loop-control side effects
|
|
836
|
+
if (result?.turnOutcome === "command") {
|
|
837
|
+
if (result.commandAction === "exit") {
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
840
|
+
// For "response" commands: the pipeline already emitted the response via onTextChunk
|
|
841
|
+
cliCallbacks.flushMarkdown();
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
638
844
|
}
|
|
639
845
|
else {
|
|
640
|
-
// Legacy path: inline runAgent (used by
|
|
846
|
+
// Legacy path: inline runAgent (used by serpent guide and tests)
|
|
641
847
|
const prefix = options.getContentPrefix?.();
|
|
642
|
-
|
|
848
|
+
const userContent = contentParts
|
|
849
|
+
? contentParts
|
|
850
|
+
: (prefix ? `${prefix}\n\n${input}` : input);
|
|
851
|
+
const userMsg = { role: "user", content: userContent };
|
|
852
|
+
(0, session_events_1.stampIngressTime)(userMsg);
|
|
853
|
+
messages.push(userMsg);
|
|
643
854
|
const traceId = (0, nerves_1.createTraceId)();
|
|
644
855
|
result = await (0, core_1.runAgent)(messages, cliCallbacks, options.skipSystemPromptRefresh ? undefined : "cli", currentAbort.signal, {
|
|
645
856
|
toolChoiceRequired: getEffectiveToolChoiceRequired(),
|
|
646
857
|
traceId,
|
|
647
858
|
tools: options.tools,
|
|
648
859
|
execTool: wrappedExecTool,
|
|
649
|
-
toolContext:
|
|
860
|
+
toolContext: effectiveToolContext,
|
|
650
861
|
});
|
|
651
862
|
}
|
|
652
863
|
}
|
|
653
864
|
catch (err) {
|
|
654
|
-
// AbortError (Ctrl-C) -- silently return to prompt
|
|
655
|
-
// All other errors: show the user what happened
|
|
656
865
|
if (!(err instanceof DOMException && err.name === "AbortError")) {
|
|
657
|
-
|
|
866
|
+
display.error(err instanceof Error ? err.message : String(err));
|
|
658
867
|
}
|
|
659
868
|
}
|
|
660
869
|
cliCallbacks.flushMarkdown();
|
|
661
|
-
|
|
870
|
+
if (!tuiStore)
|
|
871
|
+
process.stdout.write("\n"); // ensure response ends with newline before prompt (imperative only)
|
|
872
|
+
if (tuiStore)
|
|
873
|
+
tuiStore.restoreInput();
|
|
874
|
+
inputCtrl?.restore();
|
|
662
875
|
currentAbort = null;
|
|
663
876
|
// Check if exit tool was fired during this turn
|
|
664
877
|
if (exitToolFired) {
|
|
@@ -668,20 +881,35 @@ async function runCliSession(options) {
|
|
|
668
881
|
// Safety net: never silently swallow an empty response
|
|
669
882
|
const lastMsg = messages[messages.length - 1];
|
|
670
883
|
if (lastMsg?.role === "assistant" && !(typeof lastMsg.content === "string" ? lastMsg.content : "").trim()) {
|
|
671
|
-
|
|
884
|
+
display.warn("(empty response)");
|
|
672
885
|
}
|
|
673
|
-
process.stdout.write("\n\n");
|
|
674
886
|
// Post-turn hook (session persistence, pending drain, prompt refresh, etc.)
|
|
675
887
|
if (options.onTurnEnd) {
|
|
676
888
|
await options.onTurnEnd(messages, result ?? { usage: undefined });
|
|
677
889
|
}
|
|
678
890
|
if (closed)
|
|
679
891
|
break;
|
|
680
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
681
892
|
}
|
|
682
893
|
}
|
|
683
894
|
finally {
|
|
684
|
-
rl
|
|
895
|
+
rl?.close();
|
|
896
|
+
if (inkRef) {
|
|
897
|
+
// Suppress React "state update on unmounted component" warnings during cleanup.
|
|
898
|
+
// Ink's useInput hook fires after unmount — this is harmless but noisy.
|
|
899
|
+
// This also covers the SerpentGuide exitOnToolCall path, which aborts the
|
|
900
|
+
// current request and breaks out of the loop into this finally block.
|
|
901
|
+
// eslint-disable-next-line no-console -- intentional console.warn/error override for cleanup
|
|
902
|
+
const origWarn = console.warn;
|
|
903
|
+
const origError = console.error; // eslint-disable-line no-console
|
|
904
|
+
// eslint-disable-next-line no-console -- suppress React unmount warnings
|
|
905
|
+
console.warn = (...args) => { if (typeof args[0] === "string" && args[0].includes("Can't perform a React state update"))
|
|
906
|
+
return; origWarn.apply(console, args); };
|
|
907
|
+
// eslint-disable-next-line no-console -- suppress React unmount warnings
|
|
908
|
+
console.error = (...args) => { if (typeof args[0] === "string" && args[0].includes("Can't perform a React state update"))
|
|
909
|
+
return; origError.apply(console, args); };
|
|
910
|
+
inkRef.unmount();
|
|
911
|
+
setTimeout(() => { console.warn = origWarn; console.error = origError; }, 100); // eslint-disable-line no-console
|
|
912
|
+
}
|
|
685
913
|
if (options.banner !== false) {
|
|
686
914
|
// eslint-disable-next-line no-console -- terminal UX: goodbye
|
|
687
915
|
console.log("bye");
|
|
@@ -694,18 +922,33 @@ async function main(agentName, options) {
|
|
|
694
922
|
if (agentName)
|
|
695
923
|
(0, identity_1.setAgentName)(agentName);
|
|
696
924
|
const pasteDebounceMs = options?.pasteDebounceMs ?? 50;
|
|
925
|
+
// Safety net: process-level SIGINT handler ensures Ctrl+C always exits,
|
|
926
|
+
// even when Ink's event loop is blocked by expensive renders.
|
|
927
|
+
/* v8 ignore start -- process signal handler @preserve */
|
|
928
|
+
let sigintCount = 0;
|
|
929
|
+
const sigintHandler = () => {
|
|
930
|
+
sigintCount++;
|
|
931
|
+
if (sigintCount >= 2)
|
|
932
|
+
process.exit(1);
|
|
933
|
+
};
|
|
934
|
+
if (!options?._testInputSource) {
|
|
935
|
+
process.on("SIGINT", sigintHandler);
|
|
936
|
+
}
|
|
937
|
+
/* v8 ignore stop */
|
|
938
|
+
// Register spinner hooks so log output clears the spinner before printing
|
|
939
|
+
(0, nerves_1.registerSpinnerHooks)(pauseActiveSpinner, resumeActiveSpinner);
|
|
697
940
|
// Fallback: apply pending updates for daemon-less direct CLI usage
|
|
698
941
|
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
942
|
+
(0, update_hooks_1.registerUpdateHook)(agent_config_v2_1.agentConfigV2Hook);
|
|
699
943
|
await (0, update_hooks_1.applyPendingUpdates)((0, identity_1.getAgentBundlesRoot)(), (0, bundle_manifest_1.getPackageVersion)());
|
|
700
944
|
// Fail fast if provider is misconfigured (triggers human-readable error + exit)
|
|
701
|
-
(0, core_1.getProvider)();
|
|
945
|
+
(0, core_1.getProvider)("human");
|
|
702
946
|
// Resolve context kernel (identity + channel) for CLI
|
|
703
947
|
const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
|
|
704
|
-
const friendStore = new
|
|
948
|
+
const friendStore = new friends_1.FileFriendStore(friendsPath);
|
|
705
949
|
const username = os.userInfo().username;
|
|
706
|
-
const
|
|
707
|
-
const
|
|
708
|
-
const resolver = new resolver_1.FriendResolver(friendStore, {
|
|
950
|
+
const localExternalId = username;
|
|
951
|
+
const resolver = new friends_1.FriendResolver(friendStore, {
|
|
709
952
|
provider: "local",
|
|
710
953
|
externalId: localExternalId,
|
|
711
954
|
displayName: username,
|
|
@@ -735,32 +978,74 @@ async function main(agentName, options) {
|
|
|
735
978
|
// Load existing session or start fresh
|
|
736
979
|
const existing = (0, context_1.loadSession)(sessPath);
|
|
737
980
|
let sessionState = existing?.state;
|
|
981
|
+
let sessionEvents = existing?.events ?? [];
|
|
738
982
|
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
739
983
|
const sessionMessages = existing?.messages && existing.messages.length > 0
|
|
740
984
|
? existing.messages
|
|
741
|
-
: [{ role: "system", content: await (0, prompt_1.buildSystem)("cli", {
|
|
985
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("cli", {}, resolvedContext)) }];
|
|
986
|
+
// Repair any orphaned tool calls from a crash mid-turn
|
|
987
|
+
(0, core_1.repairOrphanedToolCalls)(sessionMessages);
|
|
742
988
|
// Per-turn pipeline input: CLI capabilities and pending dir
|
|
743
|
-
const cliCapabilities = (0,
|
|
989
|
+
const cliCapabilities = (0, friends_1.getChannelCapabilities)("cli");
|
|
744
990
|
const currentAgentName = (0, identity_1.getAgentName)();
|
|
745
991
|
const pendingDir = (0, pending_1.getPendingDir)(currentAgentName, friendId, "cli", "session");
|
|
746
|
-
const summarize = (0, core_1.createSummarize)();
|
|
992
|
+
const summarize = (0, core_1.createSummarize)("human");
|
|
993
|
+
const cliFailoverState = { pending: null };
|
|
747
994
|
try {
|
|
748
995
|
await runCliSession({
|
|
749
996
|
agentName: currentAgentName,
|
|
750
997
|
pasteDebounceMs,
|
|
751
998
|
messages: sessionMessages,
|
|
752
|
-
|
|
999
|
+
lastActivityAt: sessionState?.lastFriendActivityAt,
|
|
1000
|
+
_testInputSource: options?._testInputSource,
|
|
1001
|
+
onAsyncAssistantMessage: async (messages, _assistantMessage) => {
|
|
1002
|
+
const prepared = (0, context_1.postTurnTrim)(messages);
|
|
1003
|
+
const events = (0, context_1.postTurnPersist)(sessPath, prepared, undefined, sessionState);
|
|
1004
|
+
/* v8 ignore next -- defensive: postTurnPersist always returns events in practice @preserve */
|
|
1005
|
+
sessionEvents = events.length > 0 ? events : sessionEvents;
|
|
1006
|
+
},
|
|
1007
|
+
runTurn: async (messages, userInput, callbacks, signal, toolContext, userContent) => {
|
|
753
1008
|
// Run the full per-turn pipeline: resolve -> gate -> session -> drain -> runAgent -> postTurn -> tokens
|
|
754
1009
|
// User message passed via input.messages so the pipeline can prepend pending messages to it.
|
|
1010
|
+
const failoverState = cliFailoverState;
|
|
1011
|
+
// Capture terminal errors instead of displaying immediately — the failover
|
|
1012
|
+
// message replaces the raw error if failover triggers successfully.
|
|
1013
|
+
let capturedTerminalError = null;
|
|
1014
|
+
/* v8 ignore start -- failover-aware callback wrapper: tested via pipeline integration @preserve */
|
|
1015
|
+
const failoverAwareCallbacks = {
|
|
1016
|
+
...callbacks,
|
|
1017
|
+
// Save session after each tool result for crash recovery (deferred to avoid blocking)
|
|
1018
|
+
onToolResult: (turnMessages) => {
|
|
1019
|
+
const prepared = (0, context_1.postTurnTrim)(turnMessages);
|
|
1020
|
+
(0, context_1.deferPostTurnPersist)(sessPath, prepared, undefined, sessionState);
|
|
1021
|
+
},
|
|
1022
|
+
onError: (error, severity) => {
|
|
1023
|
+
if (severity === "terminal" && failoverState) {
|
|
1024
|
+
capturedTerminalError = error;
|
|
1025
|
+
callbacks.onError(new Error(""), "transient");
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
callbacks.onError(error, severity);
|
|
1029
|
+
},
|
|
1030
|
+
};
|
|
1031
|
+
/* v8 ignore stop */
|
|
755
1032
|
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
756
1033
|
channel: "cli",
|
|
757
1034
|
sessionKey: "session",
|
|
758
1035
|
capabilities: cliCapabilities,
|
|
759
|
-
messages: [{ role: "user", content: userInput }],
|
|
1036
|
+
messages: [{ role: "user", content: userContent ?? userInput }],
|
|
760
1037
|
continuityIngressTexts: getCliContinuityIngressTexts(userInput),
|
|
761
|
-
callbacks,
|
|
1038
|
+
callbacks: failoverAwareCallbacks,
|
|
762
1039
|
friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
|
|
763
|
-
sessionLoader: {
|
|
1040
|
+
sessionLoader: {
|
|
1041
|
+
loadOrCreate: () => Promise.resolve({
|
|
1042
|
+
messages,
|
|
1043
|
+
sessionPath: sessPath,
|
|
1044
|
+
state: sessionState,
|
|
1045
|
+
events: sessionEvents,
|
|
1046
|
+
structuredOutputs: existing?.structuredOutputs,
|
|
1047
|
+
}),
|
|
1048
|
+
},
|
|
764
1049
|
pendingDir,
|
|
765
1050
|
friendStore,
|
|
766
1051
|
provider: "local",
|
|
@@ -778,31 +1063,53 @@ async function main(agentName, options) {
|
|
|
778
1063
|
},
|
|
779
1064
|
}),
|
|
780
1065
|
postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
|
|
781
|
-
(
|
|
1066
|
+
// Trim synchronously (mutates turnMessages for next turn),
|
|
1067
|
+
// then defer envelope build + disk I/O to avoid blocking the TUI.
|
|
1068
|
+
const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
|
|
782
1069
|
sessionState = state;
|
|
1070
|
+
(0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state).then((events) => {
|
|
1071
|
+
/* v8 ignore next -- defensive: deferPostTurnPersist always resolves events in practice @preserve */
|
|
1072
|
+
sessionEvents = events.length > 0 ? events : sessionEvents;
|
|
1073
|
+
});
|
|
783
1074
|
},
|
|
784
|
-
accumulateFriendTokens:
|
|
1075
|
+
accumulateFriendTokens: friends_1.accumulateFriendTokens,
|
|
785
1076
|
signal,
|
|
786
1077
|
runAgentOptions: {
|
|
787
1078
|
toolChoiceRequired: (0, commands_1.getToolChoiceRequired)(),
|
|
788
1079
|
traceId: (0, nerves_1.createTraceId)(),
|
|
789
1080
|
mcpManager,
|
|
1081
|
+
toolContext,
|
|
790
1082
|
},
|
|
1083
|
+
failoverState,
|
|
791
1084
|
});
|
|
1085
|
+
/* v8 ignore start -- failover display: tested via pipeline integration tests @preserve */
|
|
1086
|
+
if (result.failoverMessage) {
|
|
1087
|
+
// Failover handled it — show the actionable message instead of the raw error
|
|
1088
|
+
process.stdout.write(`\x1b[33m${result.failoverMessage}\x1b[0m\n`);
|
|
1089
|
+
}
|
|
1090
|
+
else if (capturedTerminalError) {
|
|
1091
|
+
// Failover didn't trigger (no failoverState, or sequence failed) — show the raw error
|
|
1092
|
+
process.stderr.write(`\x1b[31m${(0, format_1.formatError)(capturedTerminalError)}\x1b[0m\n`);
|
|
1093
|
+
}
|
|
1094
|
+
/* v8 ignore stop */
|
|
792
1095
|
// Handle gate rejection: display auto-reply if present
|
|
793
1096
|
if (!result.gateResult.allowed) {
|
|
794
1097
|
if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
|
|
795
1098
|
process.stdout.write(`${result.gateResult.autoReply}\n`);
|
|
796
1099
|
}
|
|
797
1100
|
}
|
|
798
|
-
return { usage: result.usage };
|
|
799
|
-
},
|
|
800
|
-
onNewSession: () => {
|
|
801
|
-
(0, context_1.deleteSession)(sessPath);
|
|
1101
|
+
return { usage: result.usage, turnOutcome: result.turnOutcome, commandAction: result.commandAction };
|
|
802
1102
|
},
|
|
803
1103
|
});
|
|
804
1104
|
}
|
|
805
1105
|
finally {
|
|
806
1106
|
sessionLock?.release();
|
|
807
1107
|
}
|
|
1108
|
+
// Force exit: lingering handles (Ink cleanup timers, MCP connections) keep the
|
|
1109
|
+
// event loop alive after the interactive session ends. This is safe because all
|
|
1110
|
+
// session persistence has already completed in the finally block above.
|
|
1111
|
+
/* v8 ignore next -- process.exit not callable in vitest @preserve */
|
|
1112
|
+
if (!options?._testInputSource)
|
|
1113
|
+
process.exit(0);
|
|
808
1114
|
}
|
|
1115
|
+
// CI trigger
|