elanous 0.2.31 → 0.2.32
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{1834-50625904996a8fa8.js → 1834-98b64172bc1efe77.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/69-0a2c8209c6d4a158.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3535a4a69f1fafaa.js → page-a9717a67c2eecbaf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/{page-5a503541353e60a8.js → page-c0f1ca371f710b06.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-6025101384e361a5.js → page-46430d45a9e04eac.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a645f00a06483e9a.js → page-a27b1e5a0d1b308f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-e9bab7413b028b28.js → page-8ea49a4c6dd2c6e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-e547e6ce55f1fb33.js → page-0e5a41fac1c75396.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-d27e1b9850fd54bf.js → page-31aaf1ed37c6df9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-0fec29c7ba01894b.js → page-c69247e7134d329d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-339a07410cd6f36b.js → layout-66dc5cd3a0999abe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-2fd93ed8c9f16580.js → page-d256f5d5d9552823.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-9b564dbff0fefc35.js → page-ce8f1907b8a6101c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-814ba51385225776.js → page-32866485913d6a04.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-397d479626e5a459.js → not-found-7607cca9b84950c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-af1bba4e3251b728.js → page-eeab86176b03dc3b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-3ad3be5e7d3fdce9.js → page-5b93dbae3432b996.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-4da93ad35d4a8d28.js → page-f47e768b5b242b5a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-3a658d5fa6d16a25.js → page-95306f892aa2d0ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-0157de6e85f8bb6b.js → page-940fb6c217f6b470.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-0a07363b130182a1.js → page-8c66a3b24a745630.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-841d73d4ac44ed49.js → page-7b675dfd35c36fc9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-6f43a3d1146e6377.js → page-8bd0d3cf7665bcfc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-521ee23ac8c0abfa.js → page-c219f2ed171131be.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-a9992ee285efaa83.js → page-40f8fa21bdae955b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-3d48e85fa89ec04f.js → page-468f8313e4f1fbd9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-04d00ae9b0890349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-233c71cd5f68f0d7.js → page-4214b855d9233ea8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-70151d78de2f375e.js → page-936752c7aa58762e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-dcac33a3e2cb72c0.js → page-ec052ad359df1797.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0448145ccd5c45c1.js → page-fad650e4793adb65.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +1 -0
- package/apps/pwa/out/_next/static/css/b5d88eba78a3d7bf.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +17 -14
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/graphs/implement-loop.yaml +5 -0
- package/package.json +1 -1
- package/scripts/ci-typecheck-changed.ts +26 -1
- package/scripts/measure-si-task-cpu.ts +143 -0
- package/scripts/ratchets/test-interference-patterns.allowlist.txt +0 -2
- package/scripts/release-loop/prepare-node.ts +102 -4
- package/src/acp/core-turn-bridge.ts +16 -4
- package/src/acp/elanous-extensions.ts +3 -0
- package/src/acp/server.ts +3 -0
- package/src/acp/tool-trace.ts +186 -11
- package/src/agent/self-ambient-cache.ts +156 -0
- package/src/agent/self-ambient.ts +8 -1
- package/src/agent/task-notification.ts +5 -0
- package/src/agent-substrate/pr-reviewer.ts +3 -1
- package/src/ask-user-question/tool.ts +22 -0
- package/src/autopilot/build/llm-conflict-merge.ts +56 -10
- package/src/autopilot/pr-manager.ts +23 -5
- package/src/capture/frame-state-detect.ts +39 -0
- package/src/capture/tui-self-report.ts +134 -6
- package/src/chat/index.ts +61 -15
- package/src/chat/pickers/modal-runtime.ts +6 -0
- package/src/chat/pickers/modals.ts +8 -2
- package/src/cli/ask-bare.ts +1 -0
- package/src/cli/import-cli.ts +12 -0
- package/src/cli/log-event-names.ts +10 -0
- package/src/cli/msg-cli.ts +1 -0
- package/src/cli/pod-cli.ts +14 -3
- package/src/cli/pr-cli.ts +67 -32
- package/src/cli/pr-land-base-read.ts +34 -0
- package/src/cli/pr-land-heavy-remote.ts +81 -25
- package/src/cli/pr-land-overlap-evidence.ts +46 -2
- package/src/cli/pr-land-pod-shadow-job.ts +26 -3
- package/src/cli/pr-land-refusal.ts +20 -5
- package/src/cli/pty-takeover-cli.ts +88 -14
- package/src/cli/sim-cli.ts +18 -0
- package/src/context-bus/ops-events.ts +6 -2
- package/src/core-turn/run-core-turn.ts +1 -0
- package/src/core-turn/run-goal-loop.ts +21 -0
- package/src/core-turn/types.ts +3 -0
- package/src/dashboard/acp-active-model.ts +42 -1
- package/src/dashboard/background-jobs-status.ts +66 -0
- package/src/dashboard/index.ts +117 -44
- package/src/dashboard/input/agent-wake.ts +242 -0
- package/src/dashboard/input/chat-main-entry-runtime.ts +5 -0
- package/src/dashboard/input/external-input-host.ts +23 -9
- package/src/dashboard/input/mouse-wiring.ts +108 -11
- package/src/dashboard/log-click-runtime.ts +118 -2
- package/src/dashboard/turn-preamble.ts +116 -40
- package/src/dashboard/turn-session-layers.ts +4 -2
- package/src/dashboard/turn-start-segments.ts +222 -0
- package/src/display/prompt-row-owner.ts +146 -0
- package/src/display/resize-listener.ts +33 -2
- package/src/fold-stack.ts +4 -2
- package/src/git-fs/index.ts +263 -36
- package/src/harness/gate-reserve.ts +87 -0
- package/src/harness/harness-incidents.ts +13 -0
- package/src/harness/harness-queue.ts +15 -0
- package/src/index.ts +8 -6
- package/src/input/external-input-queue.ts +34 -9
- package/src/llm/route-decision.ts +36 -0
- package/src/llm/tool-loop-steered-rejection.ts +8 -0
- package/src/llm-request-shape.ts +209 -0
- package/src/llm.ts +166 -267
- package/src/loops/orchestrator/tick.ts +24 -2
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/mss/logging/render-categories.ts +5 -0
- package/src/ops/overview.ts +59 -2
- package/src/pr-tool/census.ts +19 -4
- package/src/pr-tool/inflight-registry.ts +143 -0
- package/src/pty-shell/pty-manifest.ts +12 -3
- package/src/pty-shell/tui-run-state.ts +34 -3
- package/src/seat-loop/traffic-signal.ts +2 -1
- package/src/self-dev/ask-launch-flow.ts +13 -0
- package/src/self-dev/draft-residue.ts +243 -24
- package/src/self-dev/shard-plan.ts +11 -3
- package/src/self-dev/shard-run.ts +103 -10
- package/src/self-implement/elanous-tui-spawn.ts +14 -0
- package/src/self-implement/follow-up-placement.ts +79 -0
- package/src/self-implement/gate-artifacts.ts +15 -4
- package/src/self-implement/gate-baseline.ts +96 -5
- package/src/self-implement/gate-cli.ts +7 -2
- package/src/self-implement/gate-remote.ts +26 -1
- package/src/self-implement/host-regate.ts +36 -4
- package/src/self-implement/orchestrator.ts +101 -11
- package/src/self-implement/run-ledger.ts +16 -14
- package/src/session-runtime/index.ts +22 -267
- package/src/shell-runner/background-surface.ts +15 -5
- package/src/skills/tools/agent.ts +39 -6
- package/src/skills/tools/list-dir.ts +11 -0
- package/src/skills/tools/schedule-wakeup.ts +408 -0
- package/src/skills/tools/shell.ts +245 -3
- package/src/status/bar.ts +26 -0
- package/src/steward/digest-delivery.ts +118 -0
- package/src/steward/hold-hygiene.ts +120 -0
- package/src/steward/triage.ts +10 -0
- package/src/surface/shell-runner-boot.ts +5 -2
- package/src/task-agent/task-hand.ts +24 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +65 -11
- package/src/task-orchestrator/surfaces/pod-lease.ts +98 -0
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-pool.ts +3 -1
- package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +10 -6
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +525 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runner.ts +90 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +156 -2
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +7 -5
- package/src/tui/chat/execution-badge.ts +5 -1
- package/src/tui/input-lag-probe.ts +230 -0
- package/src/tui.ts +4 -0
- package/src/ux-sim/scenario.ts +255 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/8097-92f87bd108322f66.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-bf3634a117322df7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b9f51447f757fbf2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-2ebb8fd59b9461f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a2fc4e49a4fa4ba8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f4b630cc8b5273cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-61ac22cbab1ee4ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c992eafe028b642e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-805ac2e137b7a121.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-349f2752d8cd37ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-d5c6dcf31c25cecc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-62f4475d3c38fb9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-f219ccdbc0bf27df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-456666766e5ce6fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-7404e377e8adb8c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-14456240ed2a36ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d2ced71132fac9b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-f08358bdf251adb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-149dc1caba411bb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9cb0c444903da65d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-17e504c396e0e389.js +0 -1
- package/apps/pwa/out/_next/static/css/3f5fc6f96e2cf7b2.css +0 -1
- /package/apps/pwa/out/_next/static/{LSXfY0u9QtfDbejY0Ukf_ → IVKr3AlRUVLKsw2fh7eXF}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{LSXfY0u9QtfDbejY0Ukf_ → IVKr3AlRUVLKsw2fh7eXF}/_ssgManifest.js +0 -0
|
@@ -39,10 +39,11 @@ import { createTuiScreenMirror } from './tui-screen-mirror.js';
|
|
|
39
39
|
import { getChannelBus } from '../terminal-matrix/index.js';
|
|
40
40
|
import { publishSelfReportFrame } from './self-report-frame.js';
|
|
41
41
|
import {
|
|
42
|
-
upsertPtyManifest, updatePtyManifestFrame, touchLivePtyManifest, markPtyManifestClosed, removePtyManifest,
|
|
42
|
+
upsertPtyManifest, updatePtyManifestFrame, touchLivePtyManifest, markPtyManifestClosed, removePtyManifest, getPtyManifest,
|
|
43
|
+
updatePtyManifestBuild,
|
|
43
44
|
} from '../pty-shell/pty-manifest.js';
|
|
44
45
|
import { writeHarnessScreen } from '../harness/harness-screen.js';
|
|
45
|
-
import { startTuiRunStateRecorder } from '../pty-shell/tui-run-state.js';
|
|
46
|
+
import { runningBuild, startTuiRunStateRecorder } from '../pty-shell/tui-run-state.js';
|
|
46
47
|
import { debug } from '../debug/log.js';
|
|
47
48
|
import { routeExternalInput, type ExternalInputRoute } from '../input/external-input-queue.js';
|
|
48
49
|
|
|
@@ -187,6 +188,46 @@ export interface TuiSelfReportDeps {
|
|
|
187
188
|
startRunState?: (surfaceId: string) => (() => void);
|
|
188
189
|
/** 미룬 우편 깨움 폴러(TUI-H4)를 연다 — 반환 = 정리. 기본은 실 행 등록과 짝일 때만 늦게 불러온 `startPendingWakePoller`. */
|
|
189
190
|
startWakePoller?: (surfaceId: string) => (() => void);
|
|
191
|
+
/** 박동마다 자기 행을 본다 — 없으면 null · ⛔ 조회 실패는 «던진다»(없다로 읽지 않는다). 기본 = `getPtyManifest(id, { strict })`. */
|
|
192
|
+
lookupRow?: (surfaceId: string) => TuiOwnRowView | null;
|
|
193
|
+
/** 재등록 뒤 실행 판(build) 칸을 다시 적는다 — 기본 = 기동 때 판(`runningBuild`). */
|
|
194
|
+
restoreBuild?: (surfaceId: string) => void;
|
|
195
|
+
/** 박동 스케줄러 — 기본 setInterval(unref). 시험이 박동을 손으로 돌린다. */
|
|
196
|
+
driveHeartbeat?: (tick: () => void, ms: number) => (() => void);
|
|
197
|
+
/** SIGCONT 수신 훅(행 상실 단서) — 반환 = 해제. 기본 = process.on('SIGCONT'). */
|
|
198
|
+
onSigcont?: (fn: () => void) => (() => void);
|
|
199
|
+
/** 행 상실 관측 — 기본 = debug.log('tui.self-report', 'row-missing', data). */
|
|
200
|
+
logRowMissing?: (data: TuiRowMissingEvent) => void;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** 박동이 보는 자기 행 — `PtyManifestRow` 의 부분(재등록 뒤 frame·build 복원 확인에도 쓴다). */
|
|
204
|
+
export interface TuiOwnRowView {
|
|
205
|
+
readonly alive: boolean;
|
|
206
|
+
readonly frameAt?: number;
|
|
207
|
+
readonly build?: unknown;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** `tui.self-report` · `row-missing` 한 줄 — 살아 있는 TUI 의 manifest 행이 사라졌을 때(재등록 시도 결과 ⊕ 원인 단서). */
|
|
211
|
+
export interface TuiRowMissingEvent {
|
|
212
|
+
readonly surfaceId: string;
|
|
213
|
+
readonly pid: number;
|
|
214
|
+
/** 'absent' = 행이 없다 · 'closed' = 행은 있는데 alive=0 · 'lookup-failed' = 조회 자체가 실패(재등록 안 함). */
|
|
215
|
+
readonly state: 'absent' | 'closed' | 'lookup-failed';
|
|
216
|
+
/** 마지막으로 자기 행을 «본» 시각(등록·재등록 포함). */
|
|
217
|
+
readonly lastSeenAt: number;
|
|
218
|
+
readonly sinceMs: number;
|
|
219
|
+
/** 직전 박동과의 간격 — 박동 주기보다 훨씬 크면 그 사이 프로세스가 멈췄다(SIGSTOP·SIGTTIN·이벤트 루프 정체). */
|
|
220
|
+
readonly gapMs: number;
|
|
221
|
+
readonly heartbeatMs: number;
|
|
222
|
+
/** 마지막 SIGCONT 시각(없으면 null) — `lastSeenAt` 뒤면 «정지 → 재개» 사이에 걷혔다는 단서. */
|
|
223
|
+
readonly sigcontAt: number | null;
|
|
224
|
+
readonly sigcontSinceLastSeen: boolean;
|
|
225
|
+
readonly reRegistered: boolean;
|
|
226
|
+
/** 재등록이 비운 frame 칸에 마지막 화면을 다시 적었나(`pty snapshot` 이 바로 닿는다). */
|
|
227
|
+
readonly frameRestored: boolean;
|
|
228
|
+
/** 재등록이 비운 실행 판(build) 칸을 다시 적었나(다시 읽어 확인). */
|
|
229
|
+
readonly buildRestored: boolean;
|
|
230
|
+
readonly error?: string;
|
|
190
231
|
}
|
|
191
232
|
|
|
192
233
|
export type TuiControlInitStage = 'create-target' | 'register-target' | 'start-poller' | 'publish-manifest';
|
|
@@ -292,8 +333,11 @@ export function startTuiSelfReport(deps: TuiSelfReportDeps = {}): () => void {
|
|
|
292
333
|
// The manifest row is published only after both input capabilities are
|
|
293
334
|
// live. A listed TUI therefore always has a registered target and poller.
|
|
294
335
|
|
|
336
|
+
// 마지막으로 내보낸 화면 — 재등록이 frame 칸을 비우면 다시 적는다(거울은 바뀐 화면만 내보내 idle 이면 영영 빈 칸).
|
|
337
|
+
let lastEmittedText: string | null = null;
|
|
295
338
|
const emitText = (text: string, cols: number, rows: number): void => {
|
|
296
339
|
const at = now();
|
|
340
|
+
lastEmittedText = text;
|
|
297
341
|
const frame: SelfReportFrame = { surfaceId, instance, kind: 'tui', mode: 'self-report', text, cols, rows, at, ...(runId ? { runId } : {}) };
|
|
298
342
|
try { publishFrame(frame); } catch { /* fail-soft */ }
|
|
299
343
|
// `at` is shared with the manifest write → its internal throttle keys
|
|
@@ -340,16 +384,100 @@ export function startTuiSelfReport(deps: TuiSelfReportDeps = {}): () => void {
|
|
|
340
384
|
}
|
|
341
385
|
|
|
342
386
|
// Heartbeat so a live-but-idle TUI isn't culled by reapStale.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
387
|
+
// ⭐ 박동마다 자기 행을 본다 — 남이 걷어 간 행(정지 중 stale 수거 등)을 같은 박동에서 다시 세운다.
|
|
388
|
+
// 행이 없으면 pty text/key/snapshot·우편 깨움이 이 TUI 에 영영 안 닿는다(#75 · #84).
|
|
389
|
+
// 행 등록을 갈아 끼운 호출자(시험)는 실 manifest 를 안 읽는다 — 기본 조회는 기본 행 등록과 짝일 때만.
|
|
390
|
+
const lookupRow: (id: string) => TuiOwnRowView | null = deps.lookupRow
|
|
391
|
+
?? (deps.registerRow ? () => ({ alive: true }) : (id: string) => getPtyManifest(id, { strict: true }));
|
|
392
|
+
const restoreBuild = deps.restoreBuild ?? (deps.registerRow ? () => {} : (id: string) => { updatePtyManifestBuild(id, runningBuild()); });
|
|
393
|
+
const logRowMissing = deps.logRowMissing ?? ((data: TuiRowMissingEvent) => debug.log('tui.self-report', 'row-missing', data));
|
|
394
|
+
const rowNickname = deps.nickname?.trim() || undefined;
|
|
395
|
+
let lastSeenAt = now();
|
|
396
|
+
let lastBeatAt = lastSeenAt;
|
|
397
|
+
let sigcontAt: number | null = null;
|
|
398
|
+
let stopSigcont: () => void = () => {};
|
|
399
|
+
try {
|
|
400
|
+
stopSigcont = (deps.onSigcont ?? ((fn) => {
|
|
401
|
+
process.on('SIGCONT', fn);
|
|
402
|
+
return () => { process.off('SIGCONT', fn); };
|
|
403
|
+
}))(() => { sigcontAt = now(); });
|
|
404
|
+
} catch { /* fail-soft — 단서일 뿐 */ }
|
|
346
405
|
let stopped = false;
|
|
406
|
+
const beat = (): void => {
|
|
407
|
+
if (stopped) return;
|
|
408
|
+
const at = now();
|
|
409
|
+
const prevSeenAt = lastSeenAt;
|
|
410
|
+
const gapMs = at - lastBeatAt;
|
|
411
|
+
lastBeatAt = at;
|
|
412
|
+
let state: TuiRowMissingEvent['state'] | null = null;
|
|
413
|
+
let error: string | undefined;
|
|
414
|
+
try {
|
|
415
|
+
const row = lookupRow(surfaceId);
|
|
416
|
+
if (!row) state = 'absent';
|
|
417
|
+
else if (!row.alive) state = 'closed';
|
|
418
|
+
} catch (e) {
|
|
419
|
+
state = 'lookup-failed';
|
|
420
|
+
error = e instanceof Error ? e.message : String(e);
|
|
421
|
+
}
|
|
422
|
+
if (state === null) {
|
|
423
|
+
lastSeenAt = at;
|
|
424
|
+
try { heartbeat(surfaceId, at); } catch { /* fail-soft */ }
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
let reRegistered = false;
|
|
428
|
+
let frameRestored = false;
|
|
429
|
+
let buildRestored = false;
|
|
430
|
+
if (state === 'lookup-failed') {
|
|
431
|
+
// ⛔ 실패를 «없다»로 읽지 않는다 — 재등록 없이 평소 박동만.
|
|
432
|
+
try { heartbeat(surfaceId, at); } catch { /* fail-soft */ }
|
|
433
|
+
} else {
|
|
434
|
+
try {
|
|
435
|
+
registerRow(surfaceId, at, rowNickname);
|
|
436
|
+
// 기본 행 등록은 fail-soft(조용히 삼킨다) — «다시 세웠다» 는 다시 읽어서만 말한다.
|
|
437
|
+
reRegistered = lookupRow(surfaceId)?.alive === true;
|
|
438
|
+
if (!reRegistered) throw new Error('row-still-missing-after-register');
|
|
439
|
+
lastSeenAt = at;
|
|
440
|
+
// 재등록은 실행 판·턴 칸을 비운다 — 판은 기동 때 값으로 다시 적는다. 턴은 비운 채(unknown) 둔다:
|
|
441
|
+
// 녹음기를 다시 열면 «idle» 을 적는데, 턴 도중이면 거짓 idle 이 깨움을 끼어들게 한다. 다음 턴 변화가 채운다.
|
|
442
|
+
const restoreErrors: string[] = [];
|
|
443
|
+
try { restoreBuild(surfaceId); } catch (e) { restoreErrors.push(`build: ${e instanceof Error ? e.message : String(e)}`); }
|
|
444
|
+
if (lastEmittedText !== null) {
|
|
445
|
+
try { writeFrame(surfaceId, lastEmittedText, at); } catch (e) { restoreErrors.push(`frame: ${e instanceof Error ? e.message : String(e)}`); }
|
|
446
|
+
}
|
|
447
|
+
// 기본 쓰기도 fail-soft — «복원했다» 는 다시 읽어서만 말한다.
|
|
448
|
+
try {
|
|
449
|
+
const after = lookupRow(surfaceId);
|
|
450
|
+
frameRestored = lastEmittedText !== null && (after?.frameAt ?? 0) >= at;
|
|
451
|
+
buildRestored = after?.build != null;
|
|
452
|
+
} catch (e) { restoreErrors.push(`verify: ${e instanceof Error ? e.message : String(e)}`); }
|
|
453
|
+
if (restoreErrors.length) error = restoreErrors.join('; ');
|
|
454
|
+
} catch (e) {
|
|
455
|
+
error = e instanceof Error ? e.message : String(e);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
logRowMissing({
|
|
460
|
+
surfaceId, pid: process.pid, state, lastSeenAt: prevSeenAt, sinceMs: at - prevSeenAt, gapMs, heartbeatMs,
|
|
461
|
+
sigcontAt, sigcontSinceLastSeen: sigcontAt !== null && sigcontAt >= prevSeenAt,
|
|
462
|
+
reRegistered, frameRestored, buildRestored, ...(error ? { error } : {}),
|
|
463
|
+
});
|
|
464
|
+
} catch { /* fail-soft diagnostic */ }
|
|
465
|
+
};
|
|
466
|
+
const stopHeartbeat = (deps.driveHeartbeat ?? ((tick, ms) => {
|
|
467
|
+
// setImmediate 로 한 칸 미룬다 — 정지(SIGSTOP·SIGTTIN)에서 풀리면 밀린 타이머가 SIGCONT 처리기보다 «먼저» 돈다
|
|
468
|
+
// (WSL bun 실측). 미뤄야 같은 박동의 row-missing 이 sigcontAt 을 본다.
|
|
469
|
+
const t = setInterval(() => { setImmediate(tick); }, ms);
|
|
470
|
+
(t as { unref?: () => void }).unref?.();
|
|
471
|
+
return () => clearInterval(t);
|
|
472
|
+
}))(beat, heartbeatMs);
|
|
473
|
+
|
|
347
474
|
const stop = (): void => {
|
|
348
475
|
if (stopped) return;
|
|
349
476
|
stopped = true;
|
|
350
477
|
try { stopSource(); } catch { /* fail-soft — unregisters observer / clears tick timer */ }
|
|
351
478
|
try { mirror?.stop(); } catch { /* fail-soft — restores stdout.write */ }
|
|
352
|
-
|
|
479
|
+
try { stopHeartbeat(); } catch { /* fail-soft */ }
|
|
480
|
+
try { stopSigcont(); } catch { /* fail-soft */ }
|
|
353
481
|
try { stopRunState(); } catch { /* fail-soft */ }
|
|
354
482
|
wakePollerStopped = true;
|
|
355
483
|
try { stopWakePoller(); } catch { /* fail-soft */ }
|
package/src/chat/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { ChatPickerKind } from './pickers/kinds.js';
|
|
|
10
10
|
import { createPickerState, type BufferAction, type PickerBufferView } from './pickers/state.js';
|
|
11
11
|
import { createChatPickerModalRuntime } from './pickers/modal-runtime.js';
|
|
12
12
|
import type { ModalSurface } from '../display/modal-stack.js';
|
|
13
|
+
import type { PromptRowOwner } from '../display/prompt-row-owner.js';
|
|
13
14
|
// Type-only import: avoids the runtime circular dep that streamGrok
|
|
14
15
|
// below sidesteps with a dynamic `await import('../llm.js')`. Keeping
|
|
15
16
|
// ChatMessage structurally compatible with LLMMessage lets the
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
} from '../input-core/index.js';
|
|
36
37
|
import { applyInlineEditorKey } from '../input-core/inline-editor-key.js';
|
|
37
38
|
import type { InputSourceRef } from '../input/input-source-kind.js';
|
|
39
|
+
import { routeRawPtyStop } from '../input/external-input-queue.js';
|
|
38
40
|
import {
|
|
39
41
|
multilineEditorBackspace,
|
|
40
42
|
multilineEditorInsertLineBreak,
|
|
@@ -70,6 +72,7 @@ export interface ModalSink {
|
|
|
70
72
|
* DisplayCoordinator that satisfies both. */
|
|
71
73
|
export interface DisplaySink extends CursorSink, ModalSink {}
|
|
72
74
|
import type { Key } from '../tui.js';
|
|
75
|
+
import { noteInputLagKey, noteInputLagPaint } from '../tui/input-lag-probe.js';
|
|
73
76
|
import {
|
|
74
77
|
createSpinner,
|
|
75
78
|
createToolLoader,
|
|
@@ -173,6 +176,8 @@ function writeTerminal(chunk: string): void {
|
|
|
173
176
|
process.stdout.write(chunk);
|
|
174
177
|
// viaCoordinator=false — this is the bypass path /perf highlights.
|
|
175
178
|
perf.recordStdoutWrite(chunk.length, false);
|
|
179
|
+
// 입력칸 키 echo 는 render() 가 아니라 이 직접 쓰기로 칠해진다 — 키→칠 관측의 칠 지점.
|
|
180
|
+
noteInputLagPaint();
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
/** textInput reports the current prompt's top row so writeTerminal
|
|
@@ -324,11 +329,16 @@ export function createTextInputRenderActions(opts: {
|
|
|
324
329
|
* coordinator-owned cursor sinks re-show it at the final position. */
|
|
325
330
|
hideCursorBeforePaint?: boolean;
|
|
326
331
|
beforePaint?: () => void;
|
|
332
|
+
/** The frame renderer owns the prompt band (TUI-PROMPT-SINGLE-OWNER):
|
|
333
|
+
* `paint` only publishes, so there are no direct writes to wrap in
|
|
334
|
+
* synchronized output. */
|
|
335
|
+
frameOwned?: boolean;
|
|
327
336
|
modalSink?: ModalSink;
|
|
328
337
|
shouldRequestModalRender?: () => boolean;
|
|
329
338
|
}): TextInputRenderActions {
|
|
330
|
-
const paintAll = (
|
|
339
|
+
const paintAll = (requestSynchronizedOutput = false) => {
|
|
331
340
|
if (opts.shouldPaint?.() === false) return false;
|
|
341
|
+
const synchronizedOutput = requestSynchronizedOutput && !opts.frameOwned;
|
|
332
342
|
if (synchronizedOutput) writeTerminal(SYNCHRONIZED_OUTPUT_BEGIN);
|
|
333
343
|
try {
|
|
334
344
|
if (opts.beforePaint) {
|
|
@@ -837,6 +847,10 @@ export async function textInput(opts: {
|
|
|
837
847
|
* input is idle. */
|
|
838
848
|
insertAtCursor?: (text: string) => void;
|
|
839
849
|
};
|
|
850
|
+
/** TUI-PROMPT-SINGLE-OWNER — when supplied, the frame renderer owns
|
|
851
|
+
* the prompt band: drawInput publishes its rows/hint/caret here and
|
|
852
|
+
* writes nothing to the prompt rows itself (no direct `\x1b[2K`). */
|
|
853
|
+
promptRowOwner?: PromptRowOwner;
|
|
840
854
|
/** P2.2.c — sink for cursor placement. When supplied, drawInput
|
|
841
855
|
* routes its caret-position decision through this instead of
|
|
842
856
|
* emitting `\\x1b[r;cH` directly. The coordinator owns the
|
|
@@ -1194,6 +1208,8 @@ export async function textInput(opts: {
|
|
|
1194
1208
|
// from there as lines accumulate. `startOffset` is how many rows
|
|
1195
1209
|
// up from `row` we start touching — zero when both current and
|
|
1196
1210
|
// previous drew only one line.
|
|
1211
|
+
const promptRowOwner = opts.promptRowOwner;
|
|
1212
|
+
const ownedRows: string[] = [];
|
|
1197
1213
|
for (let i = 0; i < rowsToTouch; i++) {
|
|
1198
1214
|
const r = row - i; // i=0 → bottom row; i=1 → one row up; etc.
|
|
1199
1215
|
if (r < 1) continue;
|
|
@@ -1212,8 +1228,9 @@ export async function textInput(opts: {
|
|
|
1212
1228
|
).display;
|
|
1213
1229
|
|
|
1214
1230
|
const padded = display + ' '.repeat(Math.max(0, rowTextW - visibleWidth(display)));
|
|
1215
|
-
|
|
1216
|
-
|
|
1231
|
+
if (promptRowOwner) ownedRows.unshift(pfx + padded);
|
|
1232
|
+
else writeTerminal(ansi.moveTo(r, col) + '\x1b[2K' + pfx + padded);
|
|
1233
|
+
} else if (!promptRowOwner) {
|
|
1217
1234
|
// This row had content on the previous paint but doesn't anymore
|
|
1218
1235
|
// (user deleted a line / shrank the input). Clear it so stale
|
|
1219
1236
|
// text doesn't linger on what is now part of the log / divider
|
|
@@ -1223,7 +1240,9 @@ export async function textInput(opts: {
|
|
|
1223
1240
|
}
|
|
1224
1241
|
prevDrawnLines = visibleLines;
|
|
1225
1242
|
|
|
1226
|
-
if (
|
|
1243
|
+
if (promptRowOwner) {
|
|
1244
|
+
// The frame renders the hint row from the published paint.
|
|
1245
|
+
} else if (ctrlCHint) {
|
|
1227
1246
|
writeTerminal(ansi.moveTo(row + 2, col) + '\x1b[2K' + C.muted(truncate(ctrlCHint, getWidth())));
|
|
1228
1247
|
ctrlCHintPainted = true;
|
|
1229
1248
|
} else if (ctrlCHintPainted) {
|
|
@@ -1244,6 +1263,12 @@ export async function textInput(opts: {
|
|
|
1244
1263
|
const pfxW = lineIdx === 0 ? visibleWidth(prompt) : contPfxW;
|
|
1245
1264
|
const { cursorCol } = inputLineWindow(lines[lineIdx]!, colIdx, textWFor(lineIdx));
|
|
1246
1265
|
const curX = col + pfxW + cursorCol;
|
|
1266
|
+
promptRowOwner?.publish({
|
|
1267
|
+
rows: ownedRows,
|
|
1268
|
+
hint: ctrlCHint ? C.muted(truncate(ctrlCHint, getWidth())) : null,
|
|
1269
|
+
caret: curR >= 1 ? { row: curR, col: curX, visible: true } : null,
|
|
1270
|
+
source: 'text-input',
|
|
1271
|
+
});
|
|
1247
1272
|
// P2.2.c — coordinator owns the cursor emit when wired; falls
|
|
1248
1273
|
// back to direct stdout for non-coordinator callers (tests, non-dashboard
|
|
1249
1274
|
// entry points) so the change is non-breaking.
|
|
@@ -1342,19 +1367,28 @@ export async function textInput(opts: {
|
|
|
1342
1367
|
const display = prefix + result;
|
|
1343
1368
|
const padded = display + ' '.repeat(Math.max(0, textW - visibleWidth(display)));
|
|
1344
1369
|
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1370
|
+
const curR = searchRow;
|
|
1371
|
+
const curX = col + promptW + visibleWidth(prefix);
|
|
1372
|
+
if (opts.promptRowOwner) {
|
|
1373
|
+
opts.promptRowOwner.publish({
|
|
1374
|
+
rows: [promptStr + padded],
|
|
1375
|
+
hint: null,
|
|
1376
|
+
caret: { row: curR, col: curX, visible: true },
|
|
1377
|
+
source: 'reverse-search',
|
|
1378
|
+
});
|
|
1379
|
+
} else {
|
|
1380
|
+
for (let i = 0; i < rowsToTouch; i++) {
|
|
1381
|
+
const r = row - i;
|
|
1382
|
+
if (r < 1) continue;
|
|
1383
|
+
if (i === 0) {
|
|
1384
|
+
writeTerminal(ansi.moveTo(searchRow, col) + '\x1b[2K' + promptStr + padded);
|
|
1385
|
+
} else {
|
|
1386
|
+
writeTerminal(ansi.moveTo(r, col) + '\x1b[2K');
|
|
1387
|
+
}
|
|
1352
1388
|
}
|
|
1353
1389
|
}
|
|
1354
1390
|
prevDrawnLines = visibleLines;
|
|
1355
1391
|
|
|
1356
|
-
const curR = searchRow;
|
|
1357
|
-
const curX = col + promptW + visibleWidth(prefix);
|
|
1358
1392
|
if (opts.cursorSink && (opts.canClaimCursor?.() ?? true)) {
|
|
1359
1393
|
opts.cursorSink.setCursor({ row: curR, col: curX, visible: true });
|
|
1360
1394
|
} else if (!opts.cursorSink) {
|
|
@@ -1389,11 +1423,16 @@ export async function textInput(opts: {
|
|
|
1389
1423
|
syncActivePickerModal();
|
|
1390
1424
|
drawInput();
|
|
1391
1425
|
};
|
|
1426
|
+
// A frame-owned prompt band has no direct paint to hide the cursor
|
|
1427
|
+
// for, wrap in synchronized output, or re-assert divider chrome around:
|
|
1428
|
+
// the frame render does all three in its own single write.
|
|
1429
|
+
const frameOwnsPromptBand = opts.promptRowOwner !== undefined;
|
|
1392
1430
|
const renderActions = createTextInputRenderActions({
|
|
1393
1431
|
shouldPaint: opts.shouldPaint,
|
|
1394
|
-
paintChrome: opts.onPaintChrome,
|
|
1432
|
+
paintChrome: frameOwnsPromptBand ? undefined : opts.onPaintChrome,
|
|
1395
1433
|
paint: repaintPure,
|
|
1396
|
-
|
|
1434
|
+
frameOwned: frameOwnsPromptBand,
|
|
1435
|
+
beforePaint: frameOwnsPromptBand ? () => {} : () => {
|
|
1397
1436
|
const chunk = hideTextInputCursor(opts.cursorSink);
|
|
1398
1437
|
if (chunk) writeTerminal(chunk);
|
|
1399
1438
|
else if (opts.cursorSink) writeTerminal(paintCursorVisibility(false));
|
|
@@ -1407,6 +1446,7 @@ export async function textInput(opts: {
|
|
|
1407
1446
|
const finalizeInput = (result: InputResult): InputResult => {
|
|
1408
1447
|
// 외부 주입 라우터가 «composer 가 안 떠 있다»를 알 수 있게 내린다.
|
|
1409
1448
|
if (hostControl) hostControl.getBufferText = undefined;
|
|
1449
|
+
opts.promptRowOwner?.clear();
|
|
1410
1450
|
detachInternalTurnYield(result.yieldedToInternalTurn === true);
|
|
1411
1451
|
clearPicker();
|
|
1412
1452
|
writeTerminal(endTextInputCursor(opts.cursorSink));
|
|
@@ -1723,6 +1763,7 @@ export async function textInput(opts: {
|
|
|
1723
1763
|
// input loop exits cleanly and the dashboard's pane dispatcher
|
|
1724
1764
|
// picks up the keys.
|
|
1725
1765
|
if (hostControl) hostControl.getBufferText = undefined;
|
|
1766
|
+
opts.promptRowOwner?.clear();
|
|
1726
1767
|
detachInternalTurnYield();
|
|
1727
1768
|
return { text: '', submitted: false, gotoPane: true };
|
|
1728
1769
|
}
|
|
@@ -2066,7 +2107,12 @@ export function attachStreamingKeys(
|
|
|
2066
2107
|
const listener = (data: string | Buffer) => {
|
|
2067
2108
|
clearEscapeTimer();
|
|
2068
2109
|
deliverInjected();
|
|
2110
|
+
// ⏹ TUI-STOP-RAW-PTY — 원시 pty 길로 한 청크에 온 `/stop⏎`·`/esc⏎` 는 typeahead 대기열 대신 지금 턴의 escGate 로.
|
|
2111
|
+
// 파서가 이스케이프·붙여넣기·UTF-8 조각을 쥐고 있으면 그 청크는 앞 입력의 이어짐이라 건드리지 않는다.
|
|
2112
|
+
if (!parser.hasPendingEscape() && !parser.hasPendingPaste() && !parser.hasPendingDecoderBytes()
|
|
2113
|
+
&& routeRawPtyStop(typeof data === 'string' ? data : data.toString('utf8'))) return;
|
|
2069
2114
|
const keys = parser.push(data);
|
|
2115
|
+
if (keys.length > 0) noteInputLagKey(keys.length);
|
|
2070
2116
|
deliver(keys);
|
|
2071
2117
|
// ⚠️ `unref()` — 이 타이머가 프로세스를 붙잡아 두면 안 된다(입력 대기는 stdin 의 몫).
|
|
2072
2118
|
if (parser.hasPendingEscape()) {
|
|
@@ -146,6 +146,12 @@ export function createChatPickerModalRuntime(deps: ChatPickerModalRuntimeDeps):
|
|
|
146
146
|
getInputZoneHeight: deps.getInputZoneHeight,
|
|
147
147
|
width: deps.getBounds().width,
|
|
148
148
|
getWidth: () => deps.getBounds().width,
|
|
149
|
+
// TUI-PICKER-RESIZE-SHRINK — follow the live prompt row, not the
|
|
150
|
+
// row captured when the family was created (stale after resize).
|
|
151
|
+
getAnchor: () => {
|
|
152
|
+
const b = deps.getBounds();
|
|
153
|
+
return { row: b.row, col: b.col };
|
|
154
|
+
},
|
|
149
155
|
onKey: makePickerOnKey(kind),
|
|
150
156
|
onRowClick: makePickerOnRowClick(kind),
|
|
151
157
|
});
|
|
@@ -108,6 +108,10 @@ export interface ChatPickerModalBindings {
|
|
|
108
108
|
getInputZoneHeight: () => number;
|
|
109
109
|
width: number;
|
|
110
110
|
getWidth?: () => number;
|
|
111
|
+
/** Live prompt anchor (input bottom row + col). When present it wins
|
|
112
|
+
* over the mount-time `bounds` snapshot, so a terminal resize moves
|
|
113
|
+
* an already-open picker with the prompt (TUI-PICKER-RESIZE-SHRINK). */
|
|
114
|
+
getAnchor?: () => { row: number; col: number };
|
|
111
115
|
onKey?: PickerOnKey;
|
|
112
116
|
onRowClick?: PickerOnRowClick;
|
|
113
117
|
}
|
|
@@ -124,6 +128,7 @@ type FramedPickerSpec<T> = {
|
|
|
124
128
|
getInputZoneHeight: () => number;
|
|
125
129
|
width: number;
|
|
126
130
|
getWidth?: () => number;
|
|
131
|
+
getAnchor?: () => { row: number; col: number };
|
|
127
132
|
onKey?: PickerOnKey;
|
|
128
133
|
onRowClick?: PickerOnRowClick;
|
|
129
134
|
};
|
|
@@ -132,7 +137,8 @@ function createFramedPickerModal<T>(spec: FramedPickerSpec<T>): ModalSurface {
|
|
|
132
137
|
const computeBounds = (): (ModalBounds & { startIdx: number; visibleCount: number }) | null => {
|
|
133
138
|
const rows = spec.getRows();
|
|
134
139
|
const inputZoneHeight = spec.getInputZoneHeight();
|
|
135
|
-
const
|
|
140
|
+
const anchor = spec.getAnchor?.() ?? spec.bounds;
|
|
141
|
+
const bottomRow = anchor.row - inputZoneHeight;
|
|
136
142
|
if (rows.length === 0 || bottomRow < 4) return null;
|
|
137
143
|
const { visibleCount, startIdx } = pickerWindow(
|
|
138
144
|
rows.length,
|
|
@@ -142,7 +148,7 @@ function createFramedPickerModal<T>(spec: FramedPickerSpec<T>): ModalSurface {
|
|
|
142
148
|
if (visibleCount <= 0) return null;
|
|
143
149
|
const height = visibleCount + 3;
|
|
144
150
|
const row = bottomRow - height + 1;
|
|
145
|
-
return { row, col:
|
|
151
|
+
return { row, col: anchor.col, width: spec.getWidth?.() ?? spec.width, height, startIdx, visibleCount };
|
|
146
152
|
};
|
|
147
153
|
|
|
148
154
|
/** Phase 4.5a (substrate Occam · §4-pre.7 · 2026-05-03) — update
|
package/src/cli/ask-bare.ts
CHANGED
|
@@ -37,6 +37,7 @@ export async function askBare({ text, provider, model, deps }: {
|
|
|
37
37
|
...(provider === undefined ? { initialProvider: chosenProvider } : { provider: chosenProvider }),
|
|
38
38
|
...(selected.model === '(none)' ? {} : { model: selected.model }),
|
|
39
39
|
onResolvedProvider: (name, activeModel) => { resolvedProvider = name; resolvedModel = activeModel; },
|
|
40
|
+
usageSite: 'cli.ask-bare',
|
|
40
41
|
});
|
|
41
42
|
return { provider: resolvedProvider, model: resolvedModel, reply };
|
|
42
43
|
}
|
package/src/cli/import-cli.ts
CHANGED
|
@@ -21,8 +21,20 @@ export async function buildImportPlan(home = homedir()): Promise<ImportPlan> {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function registerImportCommand(program: Command): void {
|
|
24
|
+
const publicContributionHelp = `
|
|
25
|
+
Public contribution (EC1 · 10-28 update; moved from 0.2.8 in the 10-01 release split):
|
|
26
|
+
Show HN / community: share a working import example and feedback; posting and replies remain with the human author.
|
|
27
|
+
CB1 contribution entry: open an issue or pull request at https://github.com/ElanvitalAI/elanous
|
|
28
|
+
Marketplace plugins: propose a plugin in plugins/ via pull request; inclusion in the signed marketplace is reviewed, not automatic.
|
|
29
|
+
Remaining drafts (not published): Show HN · 긱뉴스 · 3-minute install-to-first-result video
|
|
30
|
+
docs/marketing/DRAFT-ec1-show-hn-geeknews-and-3min-video-2026-10-01.md
|
|
31
|
+
Measure adoption separately: GitHub stars · installs · npm downloads (downloads are not unique users).
|
|
32
|
+
Historical positioning (§4, 2026-10-01; not current counts): elanous ★0 · OpenClaw ~390k · Hermes ~250k · Codex ~127k.
|
|
33
|
+
10-03 baseline (not current results): stars 0 · npm 7-day downloads 790 (includes our own installs).
|
|
34
|
+
`;
|
|
24
35
|
program.command('import')
|
|
25
36
|
.description('Show what elanous can bring over from Claude Code, Codex, ~/.agents, OpenClaw and Hermes — a plan first; `--apply` registers skills in place and adds MCP switched off, `--undo` puts everything back')
|
|
37
|
+
.addHelpText('after', publicContributionHelp)
|
|
26
38
|
.option('--json', 'Print the plan (or the apply result) as JSON')
|
|
27
39
|
.option('--apply', 'Apply the plan: skills in place (ref) or as link/copy · MCP switched off · secrets and hooks only listed')
|
|
28
40
|
.option('--mode <mode>', `How skills come over: ${IMPORT_MODES.join('|')} (default ref — nothing copied)`)
|
|
@@ -316,6 +316,10 @@ export const KNOWN_LOG_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
|
316
316
|
"daily-reflection.error",
|
|
317
317
|
"dashboard-acp",
|
|
318
318
|
"dashboard-boot",
|
|
319
|
+
"dashboard-executed",
|
|
320
|
+
"dashboard-executed-fallback",
|
|
321
|
+
"dashboard-executed-remote-skipped",
|
|
322
|
+
"dashboard-executed-session-unknown",
|
|
319
323
|
"dashboard-optional-specs.return",
|
|
320
324
|
"dashboard-optional-specs.short-circuited",
|
|
321
325
|
"debug-logs.ingest",
|
|
@@ -430,6 +434,7 @@ export const KNOWN_LOG_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
|
430
434
|
"doom-loop-ask-timeout",
|
|
431
435
|
"doom-loop-auto-undo-applied",
|
|
432
436
|
"doom-loop-detected",
|
|
437
|
+
"doom-loop-self-heal",
|
|
433
438
|
"downgrade",
|
|
434
439
|
"downstream-dep-deescalate",
|
|
435
440
|
"downstream.emit",
|
|
@@ -547,6 +552,8 @@ export const KNOWN_LOG_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
|
547
552
|
"focused-widget-key",
|
|
548
553
|
"fold-applied",
|
|
549
554
|
"fold-broken",
|
|
555
|
+
"fold-click-anchor",
|
|
556
|
+
"fold-click-follow-tail",
|
|
550
557
|
"force-flush",
|
|
551
558
|
"forced",
|
|
552
559
|
"forked",
|
|
@@ -1519,7 +1526,10 @@ export const KNOWN_LOG_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
|
1519
1526
|
"tool-unavailable",
|
|
1520
1527
|
"tool.call",
|
|
1521
1528
|
"tools-tier",
|
|
1529
|
+
"trace-persist-deferred",
|
|
1530
|
+
"trace-persist-dropped",
|
|
1522
1531
|
"trace-persist-failed",
|
|
1532
|
+
"trace-persist-flushed",
|
|
1523
1533
|
"transcribe.begin",
|
|
1524
1534
|
"transcribe.error",
|
|
1525
1535
|
"transcribe.ok",
|
package/src/cli/msg-cli.ts
CHANGED
|
@@ -359,6 +359,7 @@ export function registerMsgCommands(program: Command, deps: MsgCliDeps = {}): vo
|
|
|
359
359
|
let store: MsgStore | undefined;
|
|
360
360
|
try {
|
|
361
361
|
const asked = withStore(ledger => ledger.post({ from, to, body: question }));
|
|
362
|
+
emit(asked, opts.json, () => out.log(`#${asked.id}`));
|
|
362
363
|
// msg ask uses the same saved-message handler before waiting for its linked reply.
|
|
363
364
|
await handleTaMessage(asked);
|
|
364
365
|
const deadline = Date.now() + waitSeconds * 1000;
|
package/src/cli/pod-cli.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { readPodMemoryAdvice } from './pod-memory-advice.js';
|
|
|
7
7
|
import { getUserConfig } from '../user-config.js';
|
|
8
8
|
import { listCodexAccountsInStore } from '../oauth/codex-account-store.js';
|
|
9
9
|
import { parsePodPool, resolvePodPoolSpec, podPoolHostLease, type PoolKubectl } from '../task-orchestrator/surfaces/pod-pool.js';
|
|
10
|
-
import { leaseKubectl, measurePoolLease, probePoolDns, recommendConcurrency, type PoolLeaseMeasure, type PoolDnsProbe } from '../task-orchestrator/surfaces/pod-lease.js';
|
|
10
|
+
import { leaseKubectl, measurePoolLease, probePoolDns, probePoolGitHub, recommendConcurrency, type PoolLeaseMeasure, type PoolDnsProbe, type ExternalGitHubProbe } from '../task-orchestrator/surfaces/pod-lease.js';
|
|
11
11
|
import { POD_COMMAND_DEADLINE_SECONDS, runPodCommand, type PodCommandResult, type RunPodCommandOptions } from '../task-orchestrator/surfaces/pod-command-job.js';
|
|
12
12
|
import { hostLeaseCounts, leaseHasPendingPod } from '../pod-lease/host-lease.js';
|
|
13
13
|
|
|
@@ -22,6 +22,7 @@ export interface PodCliDeps {
|
|
|
22
22
|
run?: (options: RunPodCommandOptions) => Promise<PodCommandResult>;
|
|
23
23
|
kubectl?: PoolKubectl;
|
|
24
24
|
dns?: (context: string, kubectl: PoolKubectl) => PoolDnsProbe;
|
|
25
|
+
externalGitHub?: (context: string, kubectl: PoolKubectl) => ExternalGitHubProbe;
|
|
25
26
|
accounts?: () => number;
|
|
26
27
|
perAccount?: () => number;
|
|
27
28
|
poolSpec?: (explicit?: string) => string | null;
|
|
@@ -81,7 +82,8 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
|
|
|
81
82
|
.command('status').description('지금 추가로 쏠 수 있는 Pod 골과 제한 근거')
|
|
82
83
|
.option('--pool <spec>', '풀 스펙 — 컨텍스트[@ssh호스트][:상한][#레지스트리:포트] 쉼표')
|
|
83
84
|
.option('--json', '표 대신 동일 측정값을 JSON 으로')
|
|
84
|
-
.
|
|
85
|
+
.option('--github-egress', '같은 namespace 임시 Pod에서 GitHub DNS/HTTPS를 별도 관측 (admission 불변)')
|
|
86
|
+
.action((opts: { pool?: string; json?: boolean; githubEgress?: boolean }) => {
|
|
85
87
|
try {
|
|
86
88
|
const kubectl = deps.kubectl ?? leaseKubectl;
|
|
87
89
|
const spec = (deps.poolSpec ?? ((explicit?: string) => resolvePodPoolSpec(explicit, process.env, () => {
|
|
@@ -105,7 +107,14 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
|
|
|
105
107
|
// Pending Pods and Job-only reservations are disjoint; a lease for an already Pending Job is the same Job.
|
|
106
108
|
const waitingJobs = decision.pending === null ? null : decision.pending +
|
|
107
109
|
records.filter((r) => r.stage === 'job' && !leaseHasPendingPod(r, decision.pendingJobs ?? [])).length;
|
|
108
|
-
|
|
110
|
+
// This explicit observation runs after the admission calculation. A slow or failed external
|
|
111
|
+
// probe can never change recommended/limitedBy or slow the ordinary lease status path.
|
|
112
|
+
const externalGitHub = opts.githubEgress ? Object.fromEntries(members.map((member) => {
|
|
113
|
+
try { return [member.context, (deps.externalGitHub ?? probePoolGitHub)(member.context, kubectl)]; }
|
|
114
|
+
catch { return [member.context, { externalDns: null, githubHttps: null, measuredAt: null, reason: 'probe failed' }]; }
|
|
115
|
+
})) : undefined;
|
|
116
|
+
const result = { pool: spec, ...measure, accounts, perAccount, ...decision, reserved: host.reserved, waitingJobs,
|
|
117
|
+
...(externalGitHub ? { externalGitHub } : {}) };
|
|
109
118
|
debug.log('pod.lease', 'status', { pool: spec ? members.map((m) => ({ capacity: m.capacity })) : null, running: decision.running, pending: decision.pending, reserved: host.reserved, waitingJobs, unleasedRunning: decision.unleasedRunning, recommended: decision.recommended, limitedBy: decision.limitedBy });
|
|
110
119
|
if (opts.json) io.log(JSON.stringify(result));
|
|
111
120
|
else {
|
|
@@ -123,6 +132,8 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
|
|
|
123
132
|
}
|
|
124
133
|
io.log(`예약(저작 중) ${host.reserved} · 대기 Job ${waitingJobs ?? '?'} · 실행 ${decision.running ?? '?'}`);
|
|
125
134
|
io.log(`임대 없는 실행 ${decision.unleasedRunning ?? '?'}`);
|
|
135
|
+
if (externalGitHub) for (const [context, probe] of Object.entries(externalGitHub))
|
|
136
|
+
io.log(`GitHub 외부망 ${context}: DNS=${probe.externalDns ?? '?'} HTTPS=${probe.githubHttps ?? '?'} 측정=${probe.measuredAt ?? '?'}${probe.reason ? ` · ${probe.reason}` : ''}`);
|
|
126
137
|
io.log(`capacity: ${decision.capacitySlots ?? '?'} 칸 (상한 ${members.reduce((sum, m) => sum + m.capacity, 0)} − Running ${decision.running ?? '?'} − Pending ${decision.pending ?? '?'}; 권장 수에서 건강한 멤버의 임대 없는 실행 차감)`);
|
|
127
138
|
io.log(`memory: ${decision.memorySlots ?? '?'} 칸 (할당 가능 − Running 하니스 상한 합; 골당 16Gi · 모든 네임스페이스의 노드별 기존 예약을 반영해 배치 가능 ${decision.placeableSlots ?? '?'} 칸)`);
|
|
128
139
|
io.log(`lite: ${decision.liteMemorySlots ?? '?'} 칸 (같은 여유를 lite 골 2Gi 로 센 값 · 관측만 — 권장 수는 위 standard 기준)`);
|