@ouro.bot/cli 0.1.0-alpha.43 → 0.1.0-alpha.430
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 +123 -15
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -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 +2715 -9
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +58 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +426 -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 +110 -128
- package/dist/heart/core.js +745 -227
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +424 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +214 -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 +604 -0
- package/dist/heart/daemon/cli-exec.js +5094 -0
- package/dist/heart/daemon/cli-help.js +428 -0
- package/dist/heart/daemon/cli-parse.js +1156 -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 +442 -0
- package/dist/heart/daemon/daemon-cli.js +28 -1582
- package/dist/heart/daemon/daemon-entry.js +356 -3
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +175 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +684 -58
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +427 -0
- package/dist/heart/daemon/health-monitor.js +79 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/human-command-screens.js +140 -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 +307 -0
- package/dist/heart/daemon/launchd.js +46 -9
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +105 -0
- package/dist/heart/daemon/message-router.js +2 -2
- 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 +214 -0
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +275 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +73 -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 +145 -32
- 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 +259 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/terminal-ui.js +169 -0
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/daemon/up-progress.js +259 -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-flow.js +52 -117
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
- package/dist/heart/identity.js +161 -65
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http-hooks.js +64 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +232 -0
- package/dist/heart/outlook/outlook-http-static.js +99 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +28 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +195 -0
- package/dist/heart/outlook/readers/agent-machine.js +359 -0
- package/dist/heart/outlook/readers/continuity-readers.js +332 -0
- package/dist/heart/outlook/readers/runtime-readers.js +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 +133 -0
- package/dist/heart/provider-binding-resolver.js +239 -0
- package/dist/heart/provider-credentials.js +391 -0
- package/dist/heart/provider-failover.js +266 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +237 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +186 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +193 -55
- package/dist/heart/providers/azure.js +103 -12
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +29 -7
- package/dist/heart/providers/openai-codex.js +62 -38
- package/dist/heart/runtime-credentials.js +260 -0
- package/dist/heart/sense-truth.js +3 -0
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +855 -0
- package/dist/heart/session-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +351 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +301 -0
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +3 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +132 -93
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +74 -93
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +21 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +38 -1
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +1 -1
- 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 +66 -7
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +947 -165
- package/dist/mind/provenance-trust.js +26 -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 +83 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-BAcU08c-.css +1 -0
- package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +702 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +79 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +111 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +371 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +37 -4
- 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 -78
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +42 -690
- package/dist/repertoire/tools-bluebubbles.js +1 -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 +361 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +739 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +9 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +144 -113
- 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 +421 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +260 -9
- package/dist/senses/bluebubbles/entry.js +73 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1620 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +45 -3
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- 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 +516 -211
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +102 -19
- package/dist/senses/inner-dialog.js +597 -95
- package/dist/senses/pipeline.js +533 -72
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +205 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +413 -163
- package/dist/senses/trust-gate.js +5 -5
- package/package.json +29 -7
- 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/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/mind/associative-recall.js +0 -209
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -854
- package/dist/senses/debug-activity.js +0 -127
- package/subagents/README.md +0 -60
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueuedMessages = QueuedMessages;
|
|
4
|
+
exports.OuroTui = OuroTui;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* Ouroboros TUI — full terminal interface using Ink v3 + Static.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* <Static> renders completed messages ONCE — they scroll up naturally.
|
|
11
|
+
* Only the "live" area (current streaming + spinner + input) re-renders.
|
|
12
|
+
* This avoids the screen-clearing problem that broke the previous Ink attempt.
|
|
13
|
+
*
|
|
14
|
+
* Design language: ouroboros brand palette from ouroboros.bot / Outlook UI.
|
|
15
|
+
* ZERO business logic here — pure rendering from CliStore state.
|
|
16
|
+
*/
|
|
17
|
+
const react_1 = require("react");
|
|
18
|
+
const ink_1 = require("ink");
|
|
19
|
+
const streaming_markdown_1 = require("./streaming-markdown");
|
|
20
|
+
const image_paste_1 = require("./image-paste");
|
|
21
|
+
const kill_ring_1 = require("./kill-ring");
|
|
22
|
+
const input_keys_1 = require("./input-keys");
|
|
23
|
+
const image_ref_navigation_1 = require("./image-ref-navigation");
|
|
24
|
+
// ─── Ouroboros Brand Palette (ANSI RGB) ─────────────────────────────
|
|
25
|
+
// From packages/outlook-ui/src/style.css and ouroboros.bot
|
|
26
|
+
const OURO = {
|
|
27
|
+
scale: "#2f8f4e", // primary green
|
|
28
|
+
teal: "#4ec9b0", // tool/accent teal
|
|
29
|
+
glow: "#74e08f", // bright green (highlights)
|
|
30
|
+
bone: "#eef2ea", // light text
|
|
31
|
+
mist: "#a5b8a8", // dim text
|
|
32
|
+
shadow: "#708373", // very dim
|
|
33
|
+
fang: "#d35f47", // error red
|
|
34
|
+
gold: "#d6b56f", // warning amber
|
|
35
|
+
moss: "#183325", // subtle bg accent
|
|
36
|
+
separator: "#3a5a40", // dim line separator
|
|
37
|
+
};
|
|
38
|
+
// ─── Ring Spinner (growing/shrinking) ───────────────────────────────
|
|
39
|
+
const RING_FRAMES = ["∙", "○", "◎", "●", "◎", "○"];
|
|
40
|
+
function ringColor(elapsedSec) {
|
|
41
|
+
if (elapsedSec >= 45)
|
|
42
|
+
return OURO.fang;
|
|
43
|
+
if (elapsedSec >= 15)
|
|
44
|
+
return OURO.gold;
|
|
45
|
+
return OURO.scale;
|
|
46
|
+
}
|
|
47
|
+
// ─── Header ─────────────────────────────────────────────────────────
|
|
48
|
+
/** Terminal width for content (capped at 200, with 2-char margin) */
|
|
49
|
+
function safeWidth() {
|
|
50
|
+
return Math.min(process.stdout.columns || 80, 200) - 2;
|
|
51
|
+
}
|
|
52
|
+
/** Full terminal width for edge-to-edge elements (separators) */
|
|
53
|
+
function termWidth() {
|
|
54
|
+
return process.stdout.columns || 80;
|
|
55
|
+
}
|
|
56
|
+
function Header({ agentName, model, contextPercent, cwd, resumeInfo }) {
|
|
57
|
+
const showCtx = contextPercent > 0;
|
|
58
|
+
const info = [agentName, model, cwd, showCtx ? `ctx ${contextPercent}%` : ""].filter(Boolean).join(" · ");
|
|
59
|
+
// 3-segment snake: TAIL (fixed) + MIDDLE (stretches) + HEAD (fixed)
|
|
60
|
+
const HEAD1 = " ____";
|
|
61
|
+
const HEAD2 = "____/ O \\___/";
|
|
62
|
+
const HEAD3 = "_________/ \\";
|
|
63
|
+
const TAIL1 = " ";
|
|
64
|
+
const TAIL2 = " __";
|
|
65
|
+
const TAIL3 = "<__";
|
|
66
|
+
const fixedWidth = TAIL1.length + HEAD1.length;
|
|
67
|
+
const maxMiddle = safeWidth() - fixedWidth;
|
|
68
|
+
const middleLen = Math.max(Math.min(Math.max(info.length, 20), maxMiddle), 10);
|
|
69
|
+
const textPad = Math.max(middleLen - info.length, 0);
|
|
70
|
+
const line1 = TAIL1 + " ".repeat(middleLen) + HEAD1;
|
|
71
|
+
const line2 = TAIL2 + "_".repeat(middleLen) + HEAD2;
|
|
72
|
+
const line3text = (info.length > middleLen ? info.slice(0, middleLen) : info) + "_".repeat(textPad);
|
|
73
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: line1 }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: line2 }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.scale, children: [TAIL3, (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.glow, children: line3text }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: HEAD3 })] }), resumeInfo ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.teal, children: [" resuming \u00b7 ", resumeInfo.messageCount, " messages \u00b7 last active ", resumeInfo.timeAgo] })) : null] }));
|
|
74
|
+
}
|
|
75
|
+
// ─── Message Rendering ──────────────────────────────────────────────
|
|
76
|
+
// Flow control tools are invisible to the user — they are internal agent mechanics
|
|
77
|
+
const FLOW_CONTROL_TOOLS = new Set(["settle", "ponder", "observe", "rest"]);
|
|
78
|
+
function ToolResultLine({ tc }) {
|
|
79
|
+
const icon = tc.success !== false ? "✓" : "✗";
|
|
80
|
+
const iconColor = tc.success !== false ? OURO.scale : OURO.fang;
|
|
81
|
+
const argColor = tc.success === false ? OURO.fang : OURO.shadow;
|
|
82
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: iconColor, children: icon }), " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, children: tc.name }), " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: argColor, children: tc.argSummary })] }));
|
|
83
|
+
}
|
|
84
|
+
function MessageBlock({ msg }) {
|
|
85
|
+
if (msg.role === "tool") {
|
|
86
|
+
const visibleCalls = msg.toolCalls?.filter(tc => !FLOW_CONTROL_TOOLS.has(tc.name));
|
|
87
|
+
// Flow control tools produce no visible output at all
|
|
88
|
+
if (!visibleCalls || visibleCalls.length === 0)
|
|
89
|
+
return (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" });
|
|
90
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: visibleCalls.map((tc, i) => (0, jsx_runtime_1.jsx)(ToolResultLine, { tc: tc }, i)) }));
|
|
91
|
+
}
|
|
92
|
+
if (msg.role === "user") {
|
|
93
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: [msg.content ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, bold: true, children: msg.content }) : null, (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) })] }));
|
|
94
|
+
}
|
|
95
|
+
if (msg.role === "assistant") {
|
|
96
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginBottom: 1, children: msg.content ? (0, jsx_runtime_1.jsx)(streaming_markdown_1.StreamingMarkdown, { text: msg.content, maxWidth: safeWidth() }) : null }));
|
|
97
|
+
}
|
|
98
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: msg.content }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) })] }));
|
|
99
|
+
}
|
|
100
|
+
// ─── Live Area (re-renders) ─────────────────────────────────────────
|
|
101
|
+
function Spinner({ phrasePool, elapsed }) {
|
|
102
|
+
const [frame, setFrame] = (0, react_1.useState)(0);
|
|
103
|
+
const [currentPhrase, setCurrentPhrase] = (0, react_1.useState)(() => phrasePool.length > 0 ? phrasePool[Math.floor(Math.random() * phrasePool.length)] : "");
|
|
104
|
+
// Animate ring frames
|
|
105
|
+
(0, react_1.useEffect)(() => {
|
|
106
|
+
const iv = setInterval(() => setFrame(f => (f + 1) % RING_FRAMES.length), 300);
|
|
107
|
+
return () => clearInterval(iv);
|
|
108
|
+
}, []);
|
|
109
|
+
// Rotate phrases every 3 seconds
|
|
110
|
+
(0, react_1.useEffect)(() => {
|
|
111
|
+
if (phrasePool.length <= 1)
|
|
112
|
+
return;
|
|
113
|
+
const iv = setInterval(() => {
|
|
114
|
+
setCurrentPhrase(phrasePool[Math.floor(Math.random() * phrasePool.length)]);
|
|
115
|
+
}, 3000);
|
|
116
|
+
return () => clearInterval(iv);
|
|
117
|
+
}, [phrasePool]);
|
|
118
|
+
const color = ringColor(elapsed);
|
|
119
|
+
const timeStr = elapsed > 0 ? `${elapsed}s` : "";
|
|
120
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: color, children: [RING_FRAMES[frame], " ", timeStr ? (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.shadow, children: [timeStr, " \u00B7 "] }) : "", currentPhrase] }));
|
|
121
|
+
}
|
|
122
|
+
function formatActiveToolArgs(name, args) {
|
|
123
|
+
switch (name) {
|
|
124
|
+
case "shell":
|
|
125
|
+
return `$ ${args.command ?? "?"}`;
|
|
126
|
+
case "read_file":
|
|
127
|
+
return `path=${args.path ?? "?"}`;
|
|
128
|
+
case "write_file":
|
|
129
|
+
return `path=${args.path ?? "?"}`;
|
|
130
|
+
case "edit_file":
|
|
131
|
+
return args.path ?? "?";
|
|
132
|
+
case "glob":
|
|
133
|
+
return `pattern=${args.pattern ?? "?"}`;
|
|
134
|
+
case "grep":
|
|
135
|
+
return `pattern=${args.pattern ?? "?"} ${args.path ? `path=${args.path}` : ""}`.trim();
|
|
136
|
+
default: {
|
|
137
|
+
// Show first 2 key=value pairs
|
|
138
|
+
const entries = Object.entries(args).slice(0, 2);
|
|
139
|
+
return entries.map(([k, v]) => `${k}=${String(v).slice(0, 50)}`).join(" ");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function ActiveToolLine({ tool }) {
|
|
144
|
+
// Hide flow control tools from in-progress display
|
|
145
|
+
if (FLOW_CONTROL_TOOLS.has(tool.name))
|
|
146
|
+
return (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" });
|
|
147
|
+
const argStr = formatActiveToolArgs(tool.name, tool.args);
|
|
148
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: "∙" }), " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, children: tool.name }), " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: argStr })] }));
|
|
149
|
+
}
|
|
150
|
+
function LiveArea({ live, elapsed }) {
|
|
151
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [live.streamingText ? ((0, jsx_runtime_1.jsx)(streaming_markdown_1.StreamingMarkdown, { text: live.streamingText, maxWidth: safeWidth() })) : null, live.activeTool ? ((0, jsx_runtime_1.jsx)(ActiveToolLine, { tool: live.activeTool })) : null, live.loading ? ((0, jsx_runtime_1.jsx)(Spinner, { phrasePool: live.spinnerPhrasePool, elapsed: elapsed })) : null, live.errorMessage ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.fang, children: ["✗ ", live.errorMessage] })) : null, live.kickMessage ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.gold, children: ["↻ ", live.kickMessage] })) : null] }));
|
|
152
|
+
}
|
|
153
|
+
// ─── Queued Messages ───────────────────────────────────────────────
|
|
154
|
+
function QueuedMessages({ items }) {
|
|
155
|
+
if (items.length === 0)
|
|
156
|
+
return (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" });
|
|
157
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: items.map((text, i) => ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: "\u231B queued: " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.mist, children: ["\"", text, "\""] })] }, i))) }));
|
|
158
|
+
}
|
|
159
|
+
// ─── Kill Ring (session-scoped) ─────────────────────────────────────
|
|
160
|
+
const killRing = new kill_ring_1.KillRing();
|
|
161
|
+
// ─── Input ──────────────────────────────────────────────────────────
|
|
162
|
+
function InputArea({ onSubmit, onCtrlC, history, queuedInputs, onPopQueue, agentName, model, onImageMap, onHistoryAdd }) {
|
|
163
|
+
const [input, setInput] = (0, react_1.useState)("");
|
|
164
|
+
const [cursorPos, setCursorPos] = (0, react_1.useState)(0); // cursor position within input
|
|
165
|
+
const [tooltip, setTooltip] = (0, react_1.useState)("");
|
|
166
|
+
const [cursorVisible, setCursorVisible] = (0, react_1.useState)(true);
|
|
167
|
+
const inputRef = (0, react_1.useRef)("");
|
|
168
|
+
const cursorRef = (0, react_1.useRef)(0);
|
|
169
|
+
const historyIdx = (0, react_1.useRef)(-1);
|
|
170
|
+
const savedInput = (0, react_1.useRef)("");
|
|
171
|
+
// Deferred ESC handling: Ink 3.2 fires escape events for ESC prefix of arrow
|
|
172
|
+
// keys (\x1b[D) and Alt+Enter (\x1b\r). We defer ESC actions by 80ms — if
|
|
173
|
+
// another key arrives in that window, it was an escape sequence, not standalone ESC.
|
|
174
|
+
const escTimerRef = (0, react_1.useRef)(null);
|
|
175
|
+
const lastEscTime = (0, react_1.useRef)(0);
|
|
176
|
+
const tooltipTimerRef = (0, react_1.useRef)(null);
|
|
177
|
+
(0, react_1.useEffect)(() => () => {
|
|
178
|
+
if (tooltipTimerRef.current)
|
|
179
|
+
clearTimeout(tooltipTimerRef.current);
|
|
180
|
+
}, []);
|
|
181
|
+
// Helper: update input and cursor together
|
|
182
|
+
const updateInput = (text, pos) => {
|
|
183
|
+
inputRef.current = text;
|
|
184
|
+
cursorRef.current = pos ?? text.length;
|
|
185
|
+
setInput(text);
|
|
186
|
+
setCursorPos(cursorRef.current);
|
|
187
|
+
};
|
|
188
|
+
// Blinking cursor
|
|
189
|
+
(0, react_1.useEffect)(() => {
|
|
190
|
+
const iv = setInterval(() => setCursorVisible(v => !v), 530);
|
|
191
|
+
return () => clearInterval(iv);
|
|
192
|
+
}, []);
|
|
193
|
+
const handleCtrlC = (0, react_1.useCallback)(() => {
|
|
194
|
+
const action = onCtrlC(inputRef.current.length > 0);
|
|
195
|
+
if (action === "clear") {
|
|
196
|
+
inputRef.current = "";
|
|
197
|
+
setInput("");
|
|
198
|
+
setTooltip("");
|
|
199
|
+
setTooltip("");
|
|
200
|
+
}
|
|
201
|
+
else if (action === "warn") {
|
|
202
|
+
// tooltip handled below
|
|
203
|
+
setTooltip("Ctrl-C again to exit");
|
|
204
|
+
if (tooltipTimerRef.current)
|
|
205
|
+
clearTimeout(tooltipTimerRef.current);
|
|
206
|
+
tooltipTimerRef.current = setTimeout(() => setTooltip(""), 3000);
|
|
207
|
+
}
|
|
208
|
+
// "abort" and "exit" are handled by the parent (cli.ts)
|
|
209
|
+
}, [onCtrlC]);
|
|
210
|
+
(0, ink_1.useInput)((inputChar, key) => {
|
|
211
|
+
if (key.ctrl && inputChar === "c") {
|
|
212
|
+
handleCtrlC();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// Input is NEVER blocked — user can type while agent responds (TTFA)
|
|
216
|
+
// Any non-Ctrl-C input clears tooltip
|
|
217
|
+
setTooltip("");
|
|
218
|
+
if (key.escape) {
|
|
219
|
+
lastEscTime.current = Date.now();
|
|
220
|
+
// Defer ESC action — if another key arrives within 80ms, this was an escape
|
|
221
|
+
// sequence prefix (arrow key, Alt+Enter), not a standalone Escape press.
|
|
222
|
+
if (escTimerRef.current)
|
|
223
|
+
clearTimeout(escTimerRef.current);
|
|
224
|
+
escTimerRef.current = setTimeout(() => {
|
|
225
|
+
escTimerRef.current = null;
|
|
226
|
+
if (inputRef.current) {
|
|
227
|
+
// Save to history before clearing (so Up arrow can retrieve it)
|
|
228
|
+
if (onHistoryAdd)
|
|
229
|
+
onHistoryAdd(inputRef.current);
|
|
230
|
+
updateInput("");
|
|
231
|
+
historyIdx.current = -1;
|
|
232
|
+
setTooltip("Esc again to clear");
|
|
233
|
+
if (tooltipTimerRef.current)
|
|
234
|
+
clearTimeout(tooltipTimerRef.current);
|
|
235
|
+
tooltipTimerRef.current = setTimeout(() => setTooltip(""), 2000);
|
|
236
|
+
}
|
|
237
|
+
else if (queuedInputs.length > 0) {
|
|
238
|
+
const items = onPopQueue();
|
|
239
|
+
updateInput(items.join("\n"));
|
|
240
|
+
historyIdx.current = -1;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
setTooltip("");
|
|
244
|
+
}
|
|
245
|
+
}, 80);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Cancel pending ESC action — this key is part of an escape sequence
|
|
249
|
+
if (escTimerRef.current) {
|
|
250
|
+
clearTimeout(escTimerRef.current);
|
|
251
|
+
escTimerRef.current = null;
|
|
252
|
+
}
|
|
253
|
+
// PageUp/PageDown: suppress (no text insertion, no action)
|
|
254
|
+
if (key.pageUp || key.pageDown)
|
|
255
|
+
return;
|
|
256
|
+
// Alt+Enter (single data event): Ink checks `return: input === '\r'` before
|
|
257
|
+
// stripping the \x1b prefix, so key.return is false when the terminal sends
|
|
258
|
+
// \x1b\r as one chunk. Detect via the stripped inputChar instead.
|
|
259
|
+
if (inputChar === "\r" && key.meta) {
|
|
260
|
+
lastEscTime.current = 0;
|
|
261
|
+
const before = inputRef.current.slice(0, cursorRef.current);
|
|
262
|
+
const after = inputRef.current.slice(cursorRef.current);
|
|
263
|
+
updateInput(before + "\n" + after, cursorRef.current + 1);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (key.return) {
|
|
267
|
+
// Alt+Enter: detect via key.meta OR recent ESC (within 50ms — Ink splits \x1b\r)
|
|
268
|
+
const recentEsc = (Date.now() - lastEscTime.current) < 50;
|
|
269
|
+
if (key.meta || key.shift || recentEsc) {
|
|
270
|
+
lastEscTime.current = 0;
|
|
271
|
+
const before = inputRef.current.slice(0, cursorRef.current);
|
|
272
|
+
const after = inputRef.current.slice(cursorRef.current);
|
|
273
|
+
updateInput(before + "\n" + after, cursorRef.current + 1);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
// Backslash+Enter: insert newline (like Claude Code)
|
|
277
|
+
if (cursorRef.current > 0 && inputRef.current[cursorRef.current - 1] === "\\") {
|
|
278
|
+
const before = inputRef.current.slice(0, cursorRef.current - 1);
|
|
279
|
+
const after = inputRef.current.slice(cursorRef.current);
|
|
280
|
+
updateInput(before + "\n" + after, cursorRef.current);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const text = inputRef.current;
|
|
284
|
+
if (text.trim()) {
|
|
285
|
+
const { text: processedText, images } = (0, image_paste_1.processSubmitInput)(text);
|
|
286
|
+
if (images.size > 0 && onImageMap) {
|
|
287
|
+
onImageMap(images);
|
|
288
|
+
}
|
|
289
|
+
onSubmit(images.size > 0 ? processedText : text);
|
|
290
|
+
}
|
|
291
|
+
updateInput("");
|
|
292
|
+
historyIdx.current = -1;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
// Backspace / Forward Delete:
|
|
296
|
+
// Ink 3.2 maps \x7f (macOS backspace) to key.delete (meta=false).
|
|
297
|
+
// Ink maps \x1b[3~ (fn+Delete / forward delete) to key.delete (meta=true,
|
|
298
|
+
// because \x1b prefix triggers meta detection).
|
|
299
|
+
// Ink maps \x08 (Ctrl+H style) to key.backspace.
|
|
300
|
+
// Distinguishing: key.meta differentiates fn+Delete from plain backspace.
|
|
301
|
+
if ((key.backspace || key.delete) && key.meta) {
|
|
302
|
+
// fn+Delete: forward delete
|
|
303
|
+
const result = (0, input_keys_1.handleForwardDelete)(inputRef.current, cursorRef.current);
|
|
304
|
+
updateInput(result.text, result.cursorPos);
|
|
305
|
+
historyIdx.current = -1;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (key.backspace || key.delete) {
|
|
309
|
+
// Plain backspace: delete backward
|
|
310
|
+
if (cursorRef.current > 0) {
|
|
311
|
+
const chip = (0, image_ref_navigation_1.deleteTokenBefore)(inputRef.current, cursorRef.current);
|
|
312
|
+
if (chip) {
|
|
313
|
+
updateInput(chip.text, chip.pos);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
const result = (0, input_keys_1.handleBackspace)(inputRef.current, cursorRef.current);
|
|
317
|
+
updateInput(result.text, result.cursorPos);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
historyIdx.current = -1;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
// Left/right arrow: move cursor (char by char, token-aware)
|
|
324
|
+
// NOTE: Ink 3.2 bug — key.meta is ALWAYS true for arrow keys because all
|
|
325
|
+
// arrows start with \x1b and Ink sets meta=true for any ESC-prefixed input.
|
|
326
|
+
// Word-jump is handled via Meta+B/F (emacs bindings) instead.
|
|
327
|
+
if (key.leftArrow) {
|
|
328
|
+
const chipStart = (0, image_ref_navigation_1.imageRefEndingAt)(inputRef.current, cursorRef.current);
|
|
329
|
+
cursorRef.current = chipStart !== undefined ? chipStart : Math.max(0, cursorRef.current - 1);
|
|
330
|
+
setCursorPos(cursorRef.current);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (key.rightArrow) {
|
|
334
|
+
const chipEnd = (0, image_ref_navigation_1.imageRefStartingAt)(inputRef.current, cursorRef.current);
|
|
335
|
+
cursorRef.current = chipEnd !== undefined ? chipEnd : Math.min(inputRef.current.length, cursorRef.current + 1);
|
|
336
|
+
setCursorPos(cursorRef.current);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
// Up/Down: queue pop takes priority over history
|
|
340
|
+
if (key.upArrow) {
|
|
341
|
+
// If not already browsing history and queue has items, pop queue into input
|
|
342
|
+
if (historyIdx.current === -1 && queuedInputs.length > 0) {
|
|
343
|
+
const items = onPopQueue();
|
|
344
|
+
updateInput(items.join("\n"));
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
// Otherwise, browse history
|
|
348
|
+
if (history.length > 0) {
|
|
349
|
+
if (historyIdx.current === -1) {
|
|
350
|
+
savedInput.current = inputRef.current;
|
|
351
|
+
historyIdx.current = history.length - 1;
|
|
352
|
+
}
|
|
353
|
+
else if (historyIdx.current > 0) {
|
|
354
|
+
historyIdx.current--;
|
|
355
|
+
}
|
|
356
|
+
updateInput(history[historyIdx.current]);
|
|
357
|
+
}
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (key.downArrow) {
|
|
361
|
+
if (historyIdx.current >= 0) {
|
|
362
|
+
if (historyIdx.current < history.length - 1) {
|
|
363
|
+
historyIdx.current++;
|
|
364
|
+
updateInput(history[historyIdx.current]);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
historyIdx.current = -1;
|
|
368
|
+
updateInput(savedInput.current);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
// Meta+B / Meta+F: word movement (emacs — Option+Arrow on macOS sends these)
|
|
374
|
+
if (key.meta && inputChar === "b") {
|
|
375
|
+
const before = inputRef.current.slice(0, cursorRef.current);
|
|
376
|
+
const match = before.match(/(?:^|\s)\S+\s*$/);
|
|
377
|
+
cursorRef.current = match ? Math.max(0, cursorRef.current - match[0].length + (match[0][0] === " " ? 1 : 0)) : 0;
|
|
378
|
+
setCursorPos(cursorRef.current);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (key.meta && inputChar === "f") {
|
|
382
|
+
const after = inputRef.current.slice(cursorRef.current);
|
|
383
|
+
const match = after.match(/^\s*\S+/);
|
|
384
|
+
cursorRef.current = match ? Math.min(inputRef.current.length, cursorRef.current + match[0].length) : inputRef.current.length;
|
|
385
|
+
setCursorPos(cursorRef.current);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// Meta+D: delete word forward
|
|
389
|
+
if (key.meta && inputChar === "d") {
|
|
390
|
+
const after = inputRef.current.slice(cursorRef.current);
|
|
391
|
+
const match = after.match(/^\s*\S+/);
|
|
392
|
+
if (match) {
|
|
393
|
+
const before = inputRef.current.slice(0, cursorRef.current);
|
|
394
|
+
const rest = after.slice(match[0].length);
|
|
395
|
+
updateInput(before + rest, cursorRef.current);
|
|
396
|
+
}
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
// Ctrl+A / Ctrl+E: home / end
|
|
400
|
+
if (key.ctrl && inputChar === "a") {
|
|
401
|
+
cursorRef.current = 0;
|
|
402
|
+
setCursorPos(0);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (key.ctrl && inputChar === "e") {
|
|
406
|
+
cursorRef.current = inputRef.current.length;
|
|
407
|
+
setCursorPos(inputRef.current.length);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
// ─── Emacs Navigation ─────────────────────────────────────────
|
|
411
|
+
// Ctrl+B: cursor left
|
|
412
|
+
if (key.ctrl && inputChar === "b") {
|
|
413
|
+
cursorRef.current = (0, input_keys_1.handleCursorLeft)(inputRef.current, cursorRef.current);
|
|
414
|
+
setCursorPos(cursorRef.current);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// Ctrl+F: cursor right
|
|
418
|
+
if (key.ctrl && inputChar === "f") {
|
|
419
|
+
cursorRef.current = (0, input_keys_1.handleCursorRight)(inputRef.current, cursorRef.current);
|
|
420
|
+
setCursorPos(cursorRef.current);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
// Ctrl+P: history up (same as up arrow)
|
|
424
|
+
if (key.ctrl && inputChar === "p") {
|
|
425
|
+
if (historyIdx.current === -1 && queuedInputs.length > 0) {
|
|
426
|
+
const items = onPopQueue();
|
|
427
|
+
updateInput(items.join("\n"));
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (history.length > 0) {
|
|
431
|
+
if (historyIdx.current === -1) {
|
|
432
|
+
savedInput.current = inputRef.current;
|
|
433
|
+
historyIdx.current = history.length - 1;
|
|
434
|
+
}
|
|
435
|
+
else if (historyIdx.current > 0) {
|
|
436
|
+
historyIdx.current--;
|
|
437
|
+
}
|
|
438
|
+
updateInput(history[historyIdx.current]);
|
|
439
|
+
}
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
// Ctrl+N: history down (same as down arrow)
|
|
443
|
+
if (key.ctrl && inputChar === "n") {
|
|
444
|
+
if (historyIdx.current >= 0) {
|
|
445
|
+
if (historyIdx.current < history.length - 1) {
|
|
446
|
+
historyIdx.current++;
|
|
447
|
+
updateInput(history[historyIdx.current]);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
historyIdx.current = -1;
|
|
451
|
+
updateInput(savedInput.current);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
// Ctrl+H: token-aware backspace
|
|
457
|
+
if (key.ctrl && inputChar === "h") {
|
|
458
|
+
const chip = (0, image_ref_navigation_1.deleteTokenBefore)(inputRef.current, cursorRef.current);
|
|
459
|
+
if (chip) {
|
|
460
|
+
updateInput(chip.text, chip.pos);
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
const result = (0, input_keys_1.handleBackspace)(inputRef.current, cursorRef.current);
|
|
464
|
+
updateInput(result.text, result.cursorPos);
|
|
465
|
+
}
|
|
466
|
+
historyIdx.current = -1;
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// Ctrl+D: forward-delete when input present, exit when empty
|
|
470
|
+
if (key.ctrl && inputChar === "d") {
|
|
471
|
+
if (inputRef.current.length === 0) {
|
|
472
|
+
handleCtrlC();
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
const result = (0, input_keys_1.handleForwardDelete)(inputRef.current, cursorRef.current);
|
|
476
|
+
updateInput(result.text, result.cursorPos);
|
|
477
|
+
}
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
// ─── Kill Ring Keybindings ─────────────────────────────────────
|
|
481
|
+
// Ctrl+K: kill from cursor to end of line
|
|
482
|
+
if (key.ctrl && inputChar === "k") {
|
|
483
|
+
const result = (0, input_keys_1.handleKillToEnd)(inputRef.current, cursorRef.current, killRing);
|
|
484
|
+
updateInput(result.text, result.cursorPos);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
// Ctrl+U: kill from start to cursor
|
|
488
|
+
if (key.ctrl && inputChar === "u") {
|
|
489
|
+
const result = (0, input_keys_1.handleKillToStart)(inputRef.current, cursorRef.current, killRing);
|
|
490
|
+
updateInput(result.text, result.cursorPos);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
// Ctrl+W: kill word before cursor (token-aware)
|
|
494
|
+
if (key.ctrl && inputChar === "w") {
|
|
495
|
+
const chip = (0, image_ref_navigation_1.deleteTokenBefore)(inputRef.current, cursorRef.current);
|
|
496
|
+
if (chip) {
|
|
497
|
+
updateInput(chip.text, chip.pos);
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
const result = (0, input_keys_1.handleKillWordBack)(inputRef.current, cursorRef.current, killRing);
|
|
501
|
+
updateInput(result.text, result.cursorPos);
|
|
502
|
+
}
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
// Ctrl+Y: yank from kill ring
|
|
506
|
+
if (key.ctrl && inputChar === "y") {
|
|
507
|
+
const result = (0, input_keys_1.handleYank)(inputRef.current, cursorRef.current, killRing);
|
|
508
|
+
if (result)
|
|
509
|
+
updateInput(result.text, result.cursorPos);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// Alt+Y: yank-pop (cycle kill ring)
|
|
513
|
+
if (key.meta && inputChar === "y") {
|
|
514
|
+
const result = (0, input_keys_1.handleYankPop)(inputRef.current, cursorRef.current, killRing);
|
|
515
|
+
if (result)
|
|
516
|
+
updateInput(result.text, result.cursorPos);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
// Option+Backspace: Ink parses \x1b\x7f as meta=true, inputChar="" (empty because
|
|
520
|
+
// \x7f matches key.delete but the whole input was \x1b\x7f not \x7f).
|
|
521
|
+
// Signature: key.meta && !inputChar && !key.escape && !key.return
|
|
522
|
+
if (key.meta && !inputChar && !key.escape && !key.return && !key.tab) {
|
|
523
|
+
if (cursorRef.current > 0) {
|
|
524
|
+
const chip = (0, image_ref_navigation_1.deleteTokenBefore)(inputRef.current, cursorRef.current);
|
|
525
|
+
if (chip) {
|
|
526
|
+
updateInput(chip.text, chip.pos);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
const result = (0, input_keys_1.handleKillWordBack)(inputRef.current, cursorRef.current, killRing);
|
|
530
|
+
updateInput(result.text, result.cursorPos);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
historyIdx.current = -1;
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
// ─── Non-kill/non-yank keystroke resets ────────────────────────
|
|
537
|
+
killRing.resetAccumulation();
|
|
538
|
+
killRing.resetYankState();
|
|
539
|
+
// Detect raw escape sequences — Ink 3.2 strips \x1b prefix and sets key.meta,
|
|
540
|
+
// so we re-prepend it for classification when meta is set and inputChar starts with [
|
|
541
|
+
const escInput = key.meta && inputChar.startsWith("[") ? "\x1b" + inputChar : inputChar;
|
|
542
|
+
const escClass = (0, input_keys_1.classifyEscapeSequence)(escInput);
|
|
543
|
+
if (escClass === "home") {
|
|
544
|
+
cursorRef.current = (0, input_keys_1.handleHome)(inputRef.current, cursorRef.current);
|
|
545
|
+
setCursorPos(cursorRef.current);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
if (escClass === "end") {
|
|
549
|
+
cursorRef.current = (0, input_keys_1.handleEnd)(inputRef.current, cursorRef.current);
|
|
550
|
+
setCursorPos(cursorRef.current);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (escClass === "word-left") {
|
|
554
|
+
const before = inputRef.current.slice(0, cursorRef.current);
|
|
555
|
+
const match = before.match(/(?:^|\s)\S+\s*$/);
|
|
556
|
+
cursorRef.current = match ? Math.max(0, cursorRef.current - match[0].length + (match[0].startsWith(" ") ? 1 : 0)) : 0;
|
|
557
|
+
setCursorPos(cursorRef.current);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (escClass === "word-right") {
|
|
561
|
+
const after = inputRef.current.slice(cursorRef.current);
|
|
562
|
+
const match = after.match(/^\s*\S+/);
|
|
563
|
+
cursorRef.current = match ? Math.min(inputRef.current.length, cursorRef.current + match[0].length) : inputRef.current.length;
|
|
564
|
+
setCursorPos(cursorRef.current);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (escClass === "ignore")
|
|
568
|
+
return;
|
|
569
|
+
// Regular character: insert at cursor position
|
|
570
|
+
if (!key.ctrl && !key.meta && inputChar) {
|
|
571
|
+
// Strip ANSI escape codes and normalize \r to \n (SSH/paste compat)
|
|
572
|
+
const cleaned = inputChar.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\r\n?/g, "\n");
|
|
573
|
+
if (!cleaned)
|
|
574
|
+
return;
|
|
575
|
+
const before = inputRef.current.slice(0, cursorRef.current);
|
|
576
|
+
const after = inputRef.current.slice(cursorRef.current);
|
|
577
|
+
updateInput(before + cleaned + after, cursorRef.current + cleaned.length);
|
|
578
|
+
historyIdx.current = -1;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
const isMultiline = input.includes("\n");
|
|
582
|
+
const inputLines = input.split("\n");
|
|
583
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(termWidth()) }), isMultiline ? ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: inputLines.map((line, i) => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, bold: true, children: i === 0 ? ") " : "· " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: line }), i === inputLines.length - 1 ? (cursorVisible ? (0, jsx_runtime_1.jsx)(ink_1.Text, { backgroundColor: OURO.scale, color: "#000000", children: " " }) : (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " })) : null] }, i))) })) : ((0, jsx_runtime_1.jsx)(ink_1.Box, { children: (0, jsx_runtime_1.jsx)(ink_1.Text, { wrap: "wrap", children: (() => {
|
|
584
|
+
const prompt = `\x1b[1m\x1b[38;2;78;201;176m) \x1b[0m`;
|
|
585
|
+
if (!input && queuedInputs.length > 0) {
|
|
586
|
+
return `${prompt}\x1b[38;2;112;131;115mPress up to edit queued messages\x1b[0m`;
|
|
587
|
+
}
|
|
588
|
+
const beforeCursor = `\x1b[38;2;238;242;234m${input.slice(0, cursorPos)}\x1b[0m`;
|
|
589
|
+
const cursorChar = input[cursorPos] ?? " ";
|
|
590
|
+
const cursor = cursorVisible
|
|
591
|
+
? `\x1b[48;2;47;143;78m\x1b[38;2;0;0;0m${cursorChar}\x1b[0m`
|
|
592
|
+
: `\x1b[38;2;238;242;234m${cursorChar}\x1b[0m`;
|
|
593
|
+
const afterCursor = `\x1b[38;2;238;242;234m${input.slice(cursorPos + 1)}\x1b[0m`;
|
|
594
|
+
return `${prompt}${beforeCursor}${cursor}${afterCursor}`;
|
|
595
|
+
})() }) })), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(termWidth()) }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [" ", agentName, model ? ` · ${model}` : "", " \u00B7 /help"] }), (0, jsx_runtime_1.jsx)(ink_1.Box, { flexGrow: 1 }), tooltip ? (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: tooltip }) : (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "opt+enter for newline" })] })] }));
|
|
596
|
+
}
|
|
597
|
+
// ─── Main TUI Component ─────────────────────────────────────────────
|
|
598
|
+
function OuroTui({ agentName, model, completedMessages, inputHistory, queuedInputs, live, elapsedSeconds, contextPercent, onSubmit, onCtrlC, onPopQueue, cwd, resumeInfo, onImageMap, onHistoryAdd, }) {
|
|
599
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Static, { items: [{ id: "__header__" }, ...completedMessages], children: (item, index) => {
|
|
600
|
+
if (index === 0) {
|
|
601
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginBottom: 2, children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }), (0, jsx_runtime_1.jsx)(Header, { agentName: agentName, model: model, contextPercent: contextPercent, cwd: cwd, resumeInfo: resumeInfo }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, dimColor: true, children: " Ctrl-C twice to exit \u00b7 \u2191\u2193 history \u00b7 Esc clear \u00b7 opt+Enter newline" })] }, "header"));
|
|
602
|
+
}
|
|
603
|
+
return (0, jsx_runtime_1.jsx)(MessageBlock, { msg: item }, item.id);
|
|
604
|
+
} }), (live.loading || live.streamingText || live.activeTool) ? (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }) : null, (0, jsx_runtime_1.jsx)(LiveArea, { live: live, elapsed: elapsedSeconds }), (live.loading || live.streamingText || live.activeTool) ? (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }) : null, queuedInputs.length > 0 ? (0, jsx_runtime_1.jsx)(QueuedMessages, { items: queuedInputs }) : null, (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }), (0, jsx_runtime_1.jsx)(InputArea, { onSubmit: onSubmit, onCtrlC: onCtrlC, history: inputHistory, queuedInputs: queuedInputs, onPopQueue: onPopQueue, agentName: agentName, model: model, onImageMap: onImageMap, onHistoryAdd: onHistoryAdd })] }));
|
|
605
|
+
}
|