mixdog 0.9.147 → 0.9.149
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/package.json +23 -3
- package/scripts/build-voice-runtime-unix.sh +101 -0
- package/scripts/embedding-quality-probe.mjs +96 -0
- package/scripts/generate-voice-runtime-manifest.mjs +300 -0
- package/scripts/generate-voice-runtime-manifest.test.mjs +135 -0
- package/scripts/native-binary-arch.mjs +30 -13
- package/scripts/native-binary-arch.test.mjs +40 -4
- package/scripts/plan-voice-runtime-release.mjs +73 -0
- package/scripts/prepare-native-assets.mjs +50 -9
- package/scripts/prune-embedding-runtime.mjs +3 -3
- package/scripts/sync-voice-runtime-manifest.mjs +46 -0
- package/scripts/verify-release-assets.mjs +106 -6
- package/scripts/verify-voice-runtime-release.mjs +89 -0
- package/scripts/voice-runtime-config.json +52 -0
- package/src/app.mjs +42 -5
- package/src/headless-exec.mjs +58 -32
- package/src/headless-exec.test.mjs +74 -13
- package/src/rules/lead/01-general.md +2 -0
- package/src/rules/shared/10-tool-workflow.md +7 -4
- package/src/rules/shared/30-exploration.md +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +28 -9
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +6 -1
- package/src/runtime/agent/orchestrator/config.mjs +53 -18
- package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +139 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +34 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -10
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +98 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +67 -16
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +4 -82
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +0 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +62 -121
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +48 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +215 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +44 -48
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +54 -31
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +56 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +34 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +61 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +61 -15
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +120 -1
- package/src/runtime/agent/orchestrator/providers/lib/env-utils.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +0 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +187 -84
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +32 -9
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +78 -33
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +21 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +36 -36
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +16 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +50 -213
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +135 -126
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +20 -11
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +112 -36
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +220 -50
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +112 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +130 -60
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +28 -10
- package/src/runtime/agent/orchestrator/providers/registry.mjs +122 -95
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +69 -3
- package/src/runtime/agent/orchestrator/providers/tool-stream-state.mjs +4 -22
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +7 -14
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +27 -48
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +19 -14
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +9 -43
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +43 -5
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +325 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +599 -195
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +22 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +26 -16
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +23 -11
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +118 -3
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +332 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +38 -1
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +27 -6
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +291 -128
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -9
- package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +224 -0
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +53 -3
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +255 -25
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +87 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +132 -9
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-pattern-fanout.mjs +29 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +180 -31
- package/src/runtime/agent/orchestrator/tools/builtin/local-search-telemetry.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +32 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +38 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +31 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +11 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-windows.mjs +22 -4
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +6 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +25 -24
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +152 -133
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +114 -42
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +129 -15
- package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +72 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +7 -8
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +201 -19
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +26 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +55 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +115 -25
- package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +268 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +296 -46
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +104 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +136 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +127 -38
- package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +661 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +350 -9
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +418 -44
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +27 -3
- package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +552 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +65 -15
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +284 -109
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +51 -21
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +44 -27
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/attachments/store.mjs +11 -3
- package/src/runtime/channels/data/voice-runtime-manifest.json +46 -70
- package/src/runtime/channels/lib/event-queue.mjs +51 -41
- package/src/runtime/channels/lib/scheduler.mjs +23 -28
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -17
- package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +17 -0
- package/src/runtime/channels/lib/webhook.mjs +39 -20
- package/src/runtime/channels/lib/whisper-server.mjs +46 -1
- package/src/runtime/channels/lib/worker-main.mjs +3 -1
- package/src/runtime/media/adapters/gemini-video.mjs +20 -4
- package/src/runtime/media/jobs.mjs +13 -0
- package/src/runtime/media/renditions.mjs +7 -1
- package/src/runtime/media/store.mjs +42 -22
- package/src/runtime/memory/index.mjs +4 -9
- package/src/runtime/memory/lib/agent-ipc.mjs +1 -6
- package/src/runtime/memory/lib/archive-security.test.mjs +21 -1
- package/src/runtime/memory/lib/core-memory-store.mjs +28 -50
- package/src/runtime/memory/lib/embedding-model-config.mjs +37 -1
- package/src/runtime/memory/lib/embedding-model-config.test.mjs +33 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +34 -12
- package/src/runtime/memory/lib/embedding-worker.mjs +37 -5
- package/src/runtime/memory/lib/ko-morph.mjs +46 -15
- package/src/runtime/memory/lib/memory-action-handlers.mjs +18 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +98 -45
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +71 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +78 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +37 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +50 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +9 -24
- package/src/runtime/memory/lib/memory.mjs +50 -12
- package/src/runtime/memory/lib/pg/process.mjs +1 -10
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -9
- package/src/runtime/memory/lib/query-handlers.mjs +2 -4
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +2 -2
- package/src/runtime/shared/background-tasks.mjs +15 -0
- package/src/runtime/shared/background-tasks.test.mjs +78 -0
- package/src/runtime/shared/child-guardian.mjs +2 -10
- package/src/runtime/shared/pid-liveness.mjs +27 -0
- package/src/runtime/shared/pristine-execution-contract.json +0 -1
- package/src/runtime/shared/runtime-root.mjs +1 -11
- package/src/runtime/shared/service-discovery.mjs +1 -17
- package/src/runtime/shared/singleton-owner.mjs +1 -11
- package/src/runtime/shared/staged-update.mjs +2 -12
- package/src/runtime/shared/tool-status.mjs +5 -1
- package/src/runtime/shared/tool-surface.mjs +15 -7
- package/src/runtime/shared/turn-snapshot-store.mjs +168 -0
- package/src/runtime/shared/turn-snapshot.mjs +137 -13
- package/src/runtime/shared/turn-worktree-snapshot.mjs +110 -18
- package/src/runtime/shared/webhook-session-run.mjs +16 -2
- package/src/runtime/web-search/lib/http-fetch.mjs +34 -4
- package/src/runtime/web-search/lib/state.mjs +15 -15
- package/src/runtime/web-search/lib/web-tools.mjs +46 -5
- package/src/session-runtime/context-status.mjs +4 -12
- package/src/session-runtime/runtime-core.mjs +20 -1
- package/src/session-runtime/session-lifecycle.mjs +9 -0
- package/src/standalone/agent-dispatch-broker.mjs +33 -2
- package/src/standalone/agent-tool/job-task-reconcile.mjs +78 -0
- package/src/standalone/agent-tool/notify.mjs +3 -1
- package/src/standalone/agent-tool/spawn-flow.mjs +52 -70
- package/src/standalone/agent-tool/tag-registry.mjs +116 -33
- package/src/standalone/agent-tool/tool-def.mjs +2 -2
- package/src/standalone/agent-tool/worker-index.mjs +8 -11
- package/src/standalone/agent-tool/worker-rows.mjs +4 -1
- package/src/standalone/agent-tool.mjs +20 -55
- package/src/standalone/agent-watchdog-registry.mjs +28 -4
- package/src/standalone/channel-client.mjs +47 -15
- package/src/standalone/channel-transport.mjs +44 -25
- package/src/standalone/daemon.mjs +31 -2
- package/src/standalone/hook-bus/config.mjs +25 -9
- package/src/standalone/hook-bus/handlers.mjs +16 -52
- package/src/standalone/hook-bus.mjs +29 -23
- package/src/standalone/memory-runtime-proxy.mjs +1 -16
- package/src/standalone/session-client.mjs +86 -23
- package/src/standalone/session-protocol.mjs +0 -2
- package/src/standalone/session-runtime-host-health.test.mjs +5 -4
- package/src/standalone/session-runtime-host.mjs +101 -6
- package/src/standalone/session-runtime-record.mjs +1 -5
- package/src/standalone/session-runtime-worker.mjs +52 -57
- package/src/standalone/session-service.mjs +4 -1
- package/src/standalone/session-transport.mjs +56 -25
- package/src/tui/App.jsx +40 -49
- package/src/tui/app/app-view.jsx +14 -21
- package/src/tui/app/core-memory-picker.mjs +29 -16
- package/src/tui/app/create-app-pickers.mjs +15 -36
- package/src/tui/app/extension-pickers.mjs +60 -49
- package/src/tui/app/maintenance-pickers.mjs +100 -57
- package/src/tui/app/message-selector.mjs +6 -5
- package/src/tui/app/model-picker.mjs +30 -13
- package/src/tui/app/onboarding-steps.mjs +21 -12
- package/src/tui/app/panel-epoch.mjs +56 -0
- package/src/tui/app/panel-epoch.test.mjs +1009 -0
- package/src/tui/app/panel-surface.mjs +98 -0
- package/src/tui/app/project-picker.mjs +30 -29
- package/src/tui/app/prompt-submit.mjs +192 -97
- package/src/tui/app/prompt-submit.test.mjs +206 -0
- package/src/tui/app/provider-setup-picker.mjs +74 -43
- package/src/tui/app/resume-picker.mjs +11 -8
- package/src/tui/app/route-pickers.mjs +45 -28
- package/src/tui/app/settings-picker.mjs +69 -18
- package/src/tui/app/shell-layout.mjs +1 -3
- package/src/tui/app/slash-commands.mjs +0 -2
- package/src/tui/app/slash-dispatch.mjs +4 -2
- package/src/tui/app/text-entry-policy.mjs +25 -0
- package/src/tui/app/theme-effort-pickers.mjs +11 -16
- package/src/tui/app/usage-context-panels.mjs +21 -25
- package/src/tui/app/use-global-key-input.mjs +3 -2
- package/src/tui/app/use-prompt-draft-flow.mjs +6 -18
- package/src/tui/app/use-prompt-handlers.mjs +3 -2
- package/src/tui/app/use-welcome-prompt-hint.mjs +17 -3
- package/src/tui/components/Picker.jsx +1 -1
- package/src/tui/components/TextEntryPanel.jsx +7 -2
- package/src/tui/dist/index.mjs +652 -561
- package/src/tui/lib/voice-setup.mjs +1 -2
- package/src/tui/session/agent-job-feed.mjs +73 -11
- package/src/tui/session/session-api-ext.mjs +5 -6
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +15 -5
- package/src/tui/session/tui-steering-persist.mjs +33 -19
- package/src/tui/session/tui-steering-persist.test.mjs +104 -0
- package/src/tui/session-local.mjs +0 -22
- package/scripts/.tmp-coverage-report.mjs +0 -96
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.test.mjs +0 -57
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import http from 'node:http';
|
|
16
16
|
import { randomUUID } from 'node:crypto';
|
|
17
17
|
import { sendJson, sendError } from '../runtime/memory/lib/http-wire.mjs';
|
|
18
|
+
import { isPidAlive, parsePid } from '../runtime/shared/pid-liveness.mjs';
|
|
18
19
|
import {
|
|
19
20
|
compareRuntimeVersions,
|
|
20
21
|
SESSION_CAPABILITY_FINGERPRINT,
|
|
@@ -99,17 +100,6 @@ function callSignature(name, args) {
|
|
|
99
100
|
} catch { return null; }
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
function parsePid(value) {
|
|
103
|
-
const n = Number(value);
|
|
104
|
-
return Number.isInteger(n) && n > 0 ? n : null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function isPidAlive(pid) {
|
|
108
|
-
const n = parsePid(pid);
|
|
109
|
-
if (!n) return false;
|
|
110
|
-
try { process.kill(n, 0); return true; }
|
|
111
|
-
catch (error) { return error?.code === 'EPERM'; }
|
|
112
|
-
}
|
|
113
103
|
|
|
114
104
|
// A stalled reader collapses the backlog to one frame per key. That is right
|
|
115
105
|
// for SNAPSHOTS (the newest state is the whole truth) and wrong for a BYTE
|
|
@@ -313,17 +303,39 @@ export function createSessionTransport({
|
|
|
313
303
|
return Math.min(limit, bytes);
|
|
314
304
|
}
|
|
315
305
|
|
|
306
|
+
let callCachePressureLoggedAt = 0;
|
|
307
|
+
|
|
316
308
|
function pruneCallCache() {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
309
|
+
if (callCache.size <= CALL_CACHE_MAX && callCacheBytes <= CALL_CACHE_MAX_BYTES) return;
|
|
310
|
+
const now = nowMs();
|
|
311
|
+
// 1) Only an entry whose promised TTL has already elapsed may be forgotten
|
|
312
|
+
// completely (its own timer may not have fired yet).
|
|
313
|
+
for (const [key, record] of callCache) {
|
|
314
|
+
if (callCache.size <= CALL_CACHE_MAX && callCacheBytes <= CALL_CACHE_MAX_BYTES) return;
|
|
315
|
+
if (!record.settled || !record.settledAt) continue;
|
|
316
|
+
if (now - record.settledAt < CALL_CACHE_TTL_MS) continue;
|
|
317
|
+
deleteCallCacheEntry(key, record);
|
|
318
|
+
}
|
|
319
|
+
// 2) Byte pressure drops the retained RESULT of settled entries, oldest
|
|
320
|
+
// first, and KEEPS their dedup identity: evicting the identity inside
|
|
321
|
+
// the promised TTL is exactly what let a retried submit/abort/configure
|
|
322
|
+
// execute twice. A replay of a dropped result fails closed instead.
|
|
323
|
+
for (const record of callCache.values()) {
|
|
324
|
+
if (callCacheBytes <= CALL_CACHE_MAX_BYTES) break;
|
|
325
|
+
if (!record.settled || record.resultDropped) continue;
|
|
326
|
+
callCacheBytes = Math.max(0, callCacheBytes - (record.bytes || 0));
|
|
327
|
+
record.bytes = 0;
|
|
328
|
+
record.resultDropped = true;
|
|
329
|
+
record.promise = null;
|
|
330
|
+
}
|
|
331
|
+
// 3) Entry-count pressure never evicts a live dedup identity. What remains
|
|
332
|
+
// carries no payload and expires with its own TTL timer.
|
|
333
|
+
if (callCache.size > CALL_CACHE_MAX && now - callCachePressureLoggedAt > 60_000) {
|
|
334
|
+
callCachePressureLoggedAt = now;
|
|
335
|
+
log(
|
|
336
|
+
`session call dedup cache above its entry budget (${callCache.size}/${CALL_CACHE_MAX});`
|
|
337
|
+
+ ' every remaining entry is still inside its retry TTL',
|
|
338
|
+
);
|
|
327
339
|
}
|
|
328
340
|
}
|
|
329
341
|
|
|
@@ -738,7 +750,14 @@ export function createSessionTransport({
|
|
|
738
750
|
const cached = cacheKey ? callCache.get(cacheKey) : null;
|
|
739
751
|
let dispatch;
|
|
740
752
|
if (cached) {
|
|
741
|
-
if (
|
|
753
|
+
if (cached.resultDropped) {
|
|
754
|
+
// The identity survived memory pressure but its result did not.
|
|
755
|
+
// Fail closed: re-running a settled mutation is never correct.
|
|
756
|
+
dispatch = Promise.reject(Object.assign(
|
|
757
|
+
new Error(`callId '${callId}' already ran; its result is no longer retained`),
|
|
758
|
+
{ code: 'ECALLRESULTDROPPED' },
|
|
759
|
+
));
|
|
760
|
+
} else if (!signature || cached.signature !== signature) {
|
|
742
761
|
// callId is an idempotency key, not a caller-selected overwrite
|
|
743
762
|
// slot. Fail closed while the original keeps its cache identity;
|
|
744
763
|
// dispatching here could execute two side-effecting mutations.
|
|
@@ -762,7 +781,13 @@ export function createSessionTransport({
|
|
|
762
781
|
);
|
|
763
782
|
if (cacheKey) {
|
|
764
783
|
const record = {
|
|
765
|
-
promise: dispatch,
|
|
784
|
+
promise: dispatch,
|
|
785
|
+
at: nowMs(),
|
|
786
|
+
signature,
|
|
787
|
+
settled: false,
|
|
788
|
+
settledAt: 0,
|
|
789
|
+
resultDropped: false,
|
|
790
|
+
bytes: 0,
|
|
766
791
|
};
|
|
767
792
|
callCache.set(cacheKey, record);
|
|
768
793
|
pruneCallCache();
|
|
@@ -773,6 +798,7 @@ export function createSessionTransport({
|
|
|
773
798
|
callCacheBytes += record.bytes;
|
|
774
799
|
}, () => {}).then(() => {
|
|
775
800
|
record.settled = true;
|
|
801
|
+
record.settledAt = nowMs();
|
|
776
802
|
pruneCallCache();
|
|
777
803
|
const t = setTimeout(() => {
|
|
778
804
|
deleteCallCacheEntry(cacheKey, record);
|
|
@@ -786,8 +812,13 @@ export function createSessionTransport({
|
|
|
786
812
|
sendJson(res, { result });
|
|
787
813
|
} catch (err) {
|
|
788
814
|
// Session call errors travel as a 200 {error} envelope so the client can
|
|
789
|
-
// tell a failed CALL from a dead TRANSPORT (which must re-attach).
|
|
790
|
-
|
|
815
|
+
// tell a failed CALL from a dead TRANSPORT (which must re-attach). The
|
|
816
|
+
// machine-readable `code` (ECALLIDCONFLICT / ECALLRESULTDROPPED) travels
|
|
817
|
+
// with it: a client must be able to branch on it, not parse prose.
|
|
818
|
+
sendJson(res, {
|
|
819
|
+
error: err?.message || String(err),
|
|
820
|
+
...(err?.code ? { code: String(err.code) } : {}),
|
|
821
|
+
}, 200);
|
|
791
822
|
}
|
|
792
823
|
return;
|
|
793
824
|
}
|
package/src/tui/App.jsx
CHANGED
|
@@ -73,6 +73,8 @@ import {
|
|
|
73
73
|
memoryCoreResultErrorText,
|
|
74
74
|
} from './app/input-parsers.mjs';
|
|
75
75
|
import { copyToClipboard } from './app/clipboard.mjs';
|
|
76
|
+
import { shouldSupersedePanelEpoch, supersedePanelEpoch } from './app/panel-epoch.mjs';
|
|
77
|
+
import { createPanelSurface } from './app/panel-surface.mjs';
|
|
76
78
|
import { wrappedTextRows, promptContentRows, wrappedDetailRows, textEntryReservedRows, queuedBandRows } from './app/text-layout.mjs';
|
|
77
79
|
import stringWidth from 'string-width';
|
|
78
80
|
import { useMouseInput } from './app/use-mouse-input.mjs';
|
|
@@ -348,7 +350,17 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
348
350
|
const setPicker = useCallback((next) => {
|
|
349
351
|
// Synchronous ref update so out-of-band setPicker(null/other) is visible to
|
|
350
352
|
// in-flight async guards immediately, before React commits the next render.
|
|
351
|
-
|
|
353
|
+
const previousPicker = livePickerRef.current;
|
|
354
|
+
livePickerRef.current = typeof next === 'function' ? next(previousPicker) : next;
|
|
355
|
+
// A real handover — closing the panel (open → null) OR replacing it with a
|
|
356
|
+
// different panel — gives the surface to what the user is looking at now:
|
|
357
|
+
// supersede every deferred paint issued before this point so a daemon write
|
|
358
|
+
// settling afterwards cannot resurrect the dismissed panel or clobber the
|
|
359
|
+
// panel that replaced it. Rebuilding the SAME panel keeps ownership (its
|
|
360
|
+
// own refresh must land), and a redundant null → null (or a first open) is
|
|
361
|
+
// not a handover and must not invalidate the in-flight write of a
|
|
362
|
+
// text-entry prompt that is currently on screen.
|
|
363
|
+
if (shouldSupersedePanelEpoch(previousPicker, livePickerRef.current)) supersedePanelEpoch();
|
|
352
364
|
setPickerState((prev) => {
|
|
353
365
|
const resolved = typeof next === 'function' ? next(prev) : next;
|
|
354
366
|
if (resolved && typeof resolved === 'object' && pickerOpenedFromEnterRef.current) {
|
|
@@ -377,35 +389,40 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
377
389
|
livePickerRef.current = picker;
|
|
378
390
|
const [contextPanel, setContextPanel] = useState(null);
|
|
379
391
|
const [usagePanel, setUsagePanel] = useState(null);
|
|
380
|
-
|
|
392
|
+
// OWNING LAYER (app/panel-surface.mjs): the three sinks above are handed over
|
|
393
|
+
// exactly once and never leave that module. Every panel factory below gets
|
|
394
|
+
// `surface` instead, so painting or delegating without a claim that proves
|
|
395
|
+
// ownership is not expressible outside this file. Stable across renders — the
|
|
396
|
+
// usage generation lives inside it.
|
|
397
|
+
const surfaceRef = useRef(null);
|
|
398
|
+
if (!surfaceRef.current) {
|
|
399
|
+
surfaceRef.current = createPanelSurface({ setPicker, setContextPanel, setUsagePanel });
|
|
400
|
+
}
|
|
401
|
+
const surface = surfaceRef.current;
|
|
381
402
|
// Cache of the last computed heavy settings-picker status objects (MCP,
|
|
382
403
|
// hooks, plugins, skills, channel provider). ←/→ cycle/toggle handlers in
|
|
383
404
|
// openSettingsPicker() pass { light: true } to reuse this cache instead of
|
|
384
405
|
// re-querying these heavy getters on every keystroke; only a full open
|
|
385
406
|
// (initial /config or Esc-return) recomputes them.
|
|
386
407
|
const settingsHeavyCacheRef = useRef(null);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
408
|
+
// Settings build generation: every open/refresh takes a ticket and Esc bumps
|
|
409
|
+
// it, so a slow (daemon) snapshot from a superseded build cannot re-open the
|
|
410
|
+
// panel after it was closed.
|
|
411
|
+
const settingsRequestRef = useRef(0);
|
|
412
|
+
const closeUsagePanel = useCallback(() => surface.closeUsage(), [surface]);
|
|
391
413
|
const [providerPrompt, setProviderPrompt] = useState(null);
|
|
392
414
|
const oauthSubmitRef = useRef(false);
|
|
393
|
-
const [channelPrompt, setChannelPrompt] = useState(null);
|
|
394
|
-
const [hookPrompt, setHookPrompt] = useState(null);
|
|
395
415
|
const [settingsPrompt, setSettingsPrompt] = useState(null);
|
|
396
|
-
// Instantiate the project-picker cluster now that
|
|
416
|
+
// Instantiate the project-picker cluster now that the surface + every prompt
|
|
397
417
|
// setter and the usage-panel closer exist. projectPickerRef (declared above,
|
|
398
418
|
// before the picker useState) is populated here so first-mount build and all
|
|
399
419
|
// later callers resolve the same set of builders.
|
|
400
420
|
const projectPicker = createProjectPicker({
|
|
401
421
|
state,
|
|
402
422
|
store,
|
|
403
|
-
|
|
423
|
+
surface,
|
|
404
424
|
setProviderPrompt,
|
|
405
|
-
setChannelPrompt,
|
|
406
|
-
setHookPrompt,
|
|
407
425
|
setSettingsPrompt,
|
|
408
|
-
setContextPanel,
|
|
409
426
|
closeUsagePanel,
|
|
410
427
|
projectNameFromPath,
|
|
411
428
|
pickFolder,
|
|
@@ -609,12 +626,9 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
609
626
|
} = createAppPickers({
|
|
610
627
|
state,
|
|
611
628
|
store,
|
|
612
|
-
|
|
629
|
+
surface,
|
|
613
630
|
setProviderPrompt,
|
|
614
|
-
setChannelPrompt,
|
|
615
|
-
setHookPrompt,
|
|
616
631
|
setSettingsPrompt,
|
|
617
|
-
setContextPanel,
|
|
618
632
|
setOnboardingActive,
|
|
619
633
|
closeUsagePanel,
|
|
620
634
|
oauthSubmitRef,
|
|
@@ -625,6 +639,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
625
639
|
modelPickerRequestRef,
|
|
626
640
|
onboardingPrefetchSeqRef,
|
|
627
641
|
settingsHeavyCacheRef,
|
|
642
|
+
settingsRequestRef,
|
|
628
643
|
livePickerRef,
|
|
629
644
|
disabledSkills,
|
|
630
645
|
setDisabledSkills,
|
|
@@ -823,7 +838,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
823
838
|
const { hasUserMessages, openMessageSelector } = useMessageSelector({
|
|
824
839
|
store,
|
|
825
840
|
state,
|
|
826
|
-
|
|
841
|
+
surface,
|
|
827
842
|
setPromptDraftOverride,
|
|
828
843
|
syncPromptLayoutRows,
|
|
829
844
|
showPromptHint,
|
|
@@ -849,7 +864,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
849
864
|
setPastedImages,
|
|
850
865
|
setPastedTexts,
|
|
851
866
|
setPromptDraftOverride,
|
|
852
|
-
|
|
867
|
+
surface,
|
|
853
868
|
syncPromptLayoutRows,
|
|
854
869
|
showPromptHint,
|
|
855
870
|
clearPromptHint,
|
|
@@ -883,7 +898,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
883
898
|
picker,
|
|
884
899
|
usagePanel,
|
|
885
900
|
contextPanel,
|
|
886
|
-
|
|
901
|
+
surface,
|
|
887
902
|
closeUsagePanel,
|
|
888
903
|
isRawModeSupported,
|
|
889
904
|
resizeState,
|
|
@@ -906,14 +921,9 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
906
921
|
const { openUsagePanel, openContextPicker } = createUsageContextPanels({
|
|
907
922
|
store,
|
|
908
923
|
state,
|
|
909
|
-
|
|
910
|
-
setPicker,
|
|
924
|
+
surface,
|
|
911
925
|
setProviderPrompt,
|
|
912
|
-
setChannelPrompt,
|
|
913
|
-
setHookPrompt,
|
|
914
926
|
setSettingsPrompt,
|
|
915
|
-
setContextPanel,
|
|
916
|
-
setUsagePanel,
|
|
917
927
|
closeUsagePanel,
|
|
918
928
|
});
|
|
919
929
|
|
|
@@ -960,17 +970,12 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
960
970
|
store,
|
|
961
971
|
state,
|
|
962
972
|
providerPrompt,
|
|
963
|
-
channelPrompt,
|
|
964
|
-
hookPrompt,
|
|
965
973
|
settingsPrompt,
|
|
966
974
|
setProviderPrompt,
|
|
967
|
-
setChannelPrompt,
|
|
968
|
-
setHookPrompt,
|
|
969
975
|
setSettingsPrompt,
|
|
970
976
|
oauthSubmitRef,
|
|
971
977
|
clearModelCaches,
|
|
972
978
|
openProviderSetupPicker,
|
|
973
|
-
openHooksPicker,
|
|
974
979
|
openSettingsPicker,
|
|
975
980
|
openProjectPicker,
|
|
976
981
|
openAutoClearPicker,
|
|
@@ -988,7 +993,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
988
993
|
pastedTextsRef,
|
|
989
994
|
});
|
|
990
995
|
|
|
991
|
-
const activeSlashQuery = providerPrompt ||
|
|
996
|
+
const activeSlashQuery = providerPrompt || settingsPrompt || toolApproval || contextPanel || usagePanel ? null : slashQuery(promptDraft);
|
|
992
997
|
// "Slash mode" is live whenever a /token is being edited and no other
|
|
993
998
|
// surface owns the floating area. The palette stays OPEN for the whole
|
|
994
999
|
// slash session — including 0-match frames — so its 14-row layout never
|
|
@@ -1009,8 +1014,6 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1009
1014
|
contextPanel: !!contextPanel,
|
|
1010
1015
|
usagePanel: !!usagePanel,
|
|
1011
1016
|
providerPrompt: !!providerPrompt,
|
|
1012
|
-
channelPrompt: !!channelPrompt,
|
|
1013
|
-
hookPrompt: !!hookPrompt,
|
|
1014
1017
|
settingsPrompt: !!settingsPrompt,
|
|
1015
1018
|
};
|
|
1016
1019
|
|
|
@@ -1023,8 +1026,6 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1023
1026
|
const {
|
|
1024
1027
|
onPromptDraftChange,
|
|
1025
1028
|
cancelProviderPrompt,
|
|
1026
|
-
cancelChannelPrompt,
|
|
1027
|
-
cancelHookPrompt,
|
|
1028
1029
|
cancelSettingsPrompt,
|
|
1029
1030
|
acceptSlashPalette,
|
|
1030
1031
|
completeSlashPalette,
|
|
@@ -1044,12 +1045,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1044
1045
|
slashDismissedFor,
|
|
1045
1046
|
setSlashDismissedFor,
|
|
1046
1047
|
providerPrompt,
|
|
1047
|
-
channelPrompt,
|
|
1048
|
-
hookPrompt,
|
|
1049
1048
|
settingsPrompt,
|
|
1050
1049
|
setProviderPrompt,
|
|
1051
|
-
setChannelPrompt,
|
|
1052
|
-
setHookPrompt,
|
|
1053
1050
|
setSettingsPrompt,
|
|
1054
1051
|
oauthSubmitRef,
|
|
1055
1052
|
openProjectPicker,
|
|
@@ -1070,8 +1067,6 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1070
1067
|
// Transcript viewport + bottom-cluster row budget: app/shell-layout.mjs.
|
|
1071
1068
|
const layout = computeShellLayout({
|
|
1072
1069
|
providerPrompt,
|
|
1073
|
-
channelPrompt,
|
|
1074
|
-
hookPrompt,
|
|
1075
1070
|
settingsPrompt,
|
|
1076
1071
|
panelTransitionEpoch,
|
|
1077
1072
|
panelInkMaskEpoch,
|
|
@@ -1194,7 +1189,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1194
1189
|
setMeasuredRowsVersion,
|
|
1195
1190
|
});
|
|
1196
1191
|
const cycleWorkflowFromPrompt = useCallback(() => {
|
|
1197
|
-
if (slashPaletteOpen || toolApproval || picker || settingsPrompt || providerPrompt ||
|
|
1192
|
+
if (slashPaletteOpen || toolApproval || picker || settingsPrompt || providerPrompt || contextPanel || usagePanel) return true;
|
|
1198
1193
|
const repeatGuardMs = 300;
|
|
1199
1194
|
const cycleGuard = workflowTabCycleRef.current;
|
|
1200
1195
|
const now = Date.now();
|
|
@@ -1236,7 +1231,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1236
1231
|
cycleGuard.lastAt = Date.now();
|
|
1237
1232
|
});
|
|
1238
1233
|
return true;
|
|
1239
|
-
}, [slashPaletteOpen, toolApproval, picker, settingsPrompt, providerPrompt,
|
|
1234
|
+
}, [slashPaletteOpen, toolApproval, picker, settingsPrompt, providerPrompt, contextPanel, usagePanel, state.commandBusy, state.workflow, store]);
|
|
1240
1235
|
// The hardware/IME caret is parked by PromptInput from its OWN measured box
|
|
1241
1236
|
// position (ink useCursor + useBoxMetrics) — correct now that the transcript
|
|
1242
1237
|
// is a live column, so the live-frame line count ink relies on is accurate.
|
|
@@ -1247,12 +1242,9 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1247
1242
|
activeSlashQuery,
|
|
1248
1243
|
activeTools,
|
|
1249
1244
|
agentRevision,
|
|
1250
|
-
cancelChannelPrompt,
|
|
1251
|
-
cancelHookPrompt,
|
|
1252
1245
|
cancelProviderPrompt,
|
|
1253
1246
|
cancelSettingsPrompt,
|
|
1254
1247
|
cancelSlashPalette,
|
|
1255
|
-
channelPrompt,
|
|
1256
1248
|
clearPromptHint,
|
|
1257
1249
|
completeSlashPalette,
|
|
1258
1250
|
contextPanel,
|
|
@@ -1265,7 +1257,6 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1265
1257
|
handlePromptInterrupt,
|
|
1266
1258
|
handlePromptPaste,
|
|
1267
1259
|
hasUserMessages,
|
|
1268
|
-
hookPrompt,
|
|
1269
1260
|
initialStatusLine,
|
|
1270
1261
|
onPromptDraftChange,
|
|
1271
1262
|
onSubmit,
|
|
@@ -1288,7 +1279,6 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1288
1279
|
resizeEpoch,
|
|
1289
1280
|
resizeState,
|
|
1290
1281
|
restoreQueuedToPrompt,
|
|
1291
|
-
setPicker,
|
|
1292
1282
|
setSlashIndex,
|
|
1293
1283
|
setTextEntryLayoutRows,
|
|
1294
1284
|
settingsPrompt,
|
|
@@ -1298,6 +1288,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
1298
1288
|
state,
|
|
1299
1289
|
statuslineStats,
|
|
1300
1290
|
store,
|
|
1291
|
+
surface,
|
|
1301
1292
|
toolApproval,
|
|
1302
1293
|
toolOutputExpanded,
|
|
1303
1294
|
transcriptMeasureRef,
|
package/src/tui/app/app-view.jsx
CHANGED
|
@@ -18,9 +18,10 @@ import { SlashCommandPalette } from '../components/SlashCommandPalette.jsx';
|
|
|
18
18
|
import { ContextPanel } from '../components/ContextPanel.jsx';
|
|
19
19
|
import { UsagePanel } from '../components/UsagePanel.jsx';
|
|
20
20
|
import { TextEntryPanel } from '../components/TextEntryPanel.jsx';
|
|
21
|
+
import { textEntryClearsByEmpty } from './text-entry-policy.mjs';
|
|
21
22
|
import { Item } from '../components/TranscriptItem.jsx';
|
|
22
23
|
export function renderAppView(ctx) {
|
|
23
|
-
const { PANEL_MAX_VISIBLE, acceptSlashPalette, activeSlashQuery, activeTools, agentRevision,
|
|
24
|
+
const { PANEL_MAX_VISIBLE, acceptSlashPalette, activeSlashQuery, activeTools, agentRevision, cancelProviderPrompt, cancelSettingsPrompt, cancelSlashPalette, clearPromptHint, completeSlashPalette, contextPanel, cycleWorkflowFromPrompt, exiting, expandedOptionPanel, floatingPanelRows, frameColumns, gridSelectionActiveRef, guardHintWidth, handlePromptEscape, handlePromptHistoryNavigate, handlePromptInterrupt, handlePromptPaste, hasUserMessages, initialStatusLine, inputBoxHidden, inputHint, inputHintTone, liveSpinner, onPromptDraftChange, onSubmit, overlayHintAttachItemIndex, overlayHintBandRows, overlayHintFallbackRow, overlayHintOnLastItem, panelCloseMaskRows, panelInkMaskEpoch, panelTransitionClearRows, panelTransitionEpoch, picker, pickerOpenedFromEnterRef, pickerOpenedFromEnterTimerRef, pickerVisibleRows, promptBoxRectRef, promptBoxRows, promptDraft, promptDraftOverride, promptMetaVisible, promptMouseSelectionRef, promptSelectionRef, promptSpinnerColumns, promptValueRef, providerPrompt, queuedCompact, queuedVisible, renderedTranscriptItems, resizeEpoch, resizeState, restoreQueuedToPrompt, setSlashIndex, setTextEntryLayoutRows, settingsPrompt, showWelcomeBanner, slashCommands, slashIndex, slashPaletteOpen, state, statuslineStats, store, surface, toolApproval, toolOutputExpanded, transcriptContentHeight, transcriptGuardRows, transcriptMeasureRef, transcriptTailPinned, transcriptWindow, transientStatusWidth, tuiReady, usagePanel, viewportHeight, welcomePromptHintRows, welcomePromptHintText } = ctx; /* DESTRUCTURE */
|
|
24
25
|
const promptInputControl = (
|
|
25
26
|
<PromptInput
|
|
26
27
|
onSubmit={onSubmit}
|
|
@@ -277,7 +278,9 @@ export function renderAppView(ctx) {
|
|
|
277
278
|
onCancel={() => {
|
|
278
279
|
if (picker.onCancel) picker.onCancel();
|
|
279
280
|
else {
|
|
280
|
-
|
|
281
|
+
// Esc with no owner-supplied handler: this keypress owns
|
|
282
|
+
// the surface it clears (app/panel-surface.mjs).
|
|
283
|
+
surface.claim().close();
|
|
281
284
|
clearPromptHint();
|
|
282
285
|
}
|
|
283
286
|
}}
|
|
@@ -326,6 +329,9 @@ export function renderAppView(ctx) {
|
|
|
326
329
|
/>
|
|
327
330
|
) : providerPrompt ? (
|
|
328
331
|
<TextEntryPanel
|
|
332
|
+
// Remount on a restore so a repeated failed save re-seeds the
|
|
333
|
+
// editor even when the restored text is byte-identical.
|
|
334
|
+
key={`provider-prompt:${providerPrompt.restoreEpoch || 0}`}
|
|
329
335
|
title={providerPrompt.kind === 'api-key'
|
|
330
336
|
? `${providerPrompt.mode === 'replace' ? 'Replace' : 'Set'} API key · ${providerPrompt.label}`
|
|
331
337
|
: providerPrompt.kind === 'oauth-code'
|
|
@@ -347,6 +353,9 @@ export function renderAppView(ctx) {
|
|
|
347
353
|
detail={providerPrompt.detail || ''}
|
|
348
354
|
mask={providerPrompt.kind === 'api-key' || providerPrompt.kind === 'openai-usage-session'}
|
|
349
355
|
columns={frameColumns}
|
|
356
|
+
// Restored after a REJECTED daemon save so the entered secret
|
|
357
|
+
// is not lost with the failed round-trip (normally empty).
|
|
358
|
+
initialValue={providerPrompt.initialValue || ''}
|
|
350
359
|
actionLabel={providerPrompt.kind === 'oauth-code' ? 'continue' : 'save'}
|
|
351
360
|
promptLabel={providerPrompt.kind === 'api-key'
|
|
352
361
|
? 'API key > '
|
|
@@ -358,31 +367,15 @@ export function renderAppView(ctx) {
|
|
|
358
367
|
onSubmit={onSubmit}
|
|
359
368
|
onCancel={cancelProviderPrompt}
|
|
360
369
|
/>
|
|
361
|
-
) : channelPrompt ? (
|
|
362
|
-
<TextEntryPanel
|
|
363
|
-
title={channelPrompt.label}
|
|
364
|
-
hint={channelPrompt.hint || 'Save channel setting.'}
|
|
365
|
-
mask={channelPrompt.kind === 'webhook-token'}
|
|
366
|
-
columns={frameColumns}
|
|
367
|
-
promptLabel="Value > "
|
|
368
|
-
onSubmit={onSubmit}
|
|
369
|
-
onCancel={cancelChannelPrompt}
|
|
370
|
-
/>
|
|
371
|
-
) : hookPrompt ? (
|
|
372
|
-
<TextEntryPanel
|
|
373
|
-
title={hookPrompt.label}
|
|
374
|
-
hint={hookPrompt.hint || 'Save hook setting.'}
|
|
375
|
-
columns={frameColumns}
|
|
376
|
-
promptLabel="Value > "
|
|
377
|
-
onSubmit={onSubmit}
|
|
378
|
-
onCancel={cancelHookPrompt}
|
|
379
|
-
/>
|
|
380
370
|
) : settingsPrompt ? (
|
|
381
371
|
<TextEntryPanel
|
|
372
|
+
key={`settings-prompt:${settingsPrompt.kind}:${settingsPrompt.restoreEpoch || 0}`}
|
|
382
373
|
title={settingsPrompt.label}
|
|
383
374
|
hint={settingsPrompt.hint || 'Save setting.'}
|
|
384
375
|
columns={frameColumns}
|
|
385
376
|
initialValue={settingsPrompt.initialValue || ''}
|
|
377
|
+
// Reset/clear prompts document an empty submit as the action.
|
|
378
|
+
allowEmpty={textEntryClearsByEmpty(settingsPrompt.kind)}
|
|
386
379
|
multiline={settingsPrompt.kind === 'core-add' || settingsPrompt.kind === 'core-edit'}
|
|
387
380
|
maxContentRows={PANEL_MAX_VISIBLE}
|
|
388
381
|
onContentRowsChange={setTextEntryLayoutRows}
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
* core-memory-picker.mjs — the Core Memory picker + add/edit/delete flow.
|
|
3
3
|
*
|
|
4
4
|
* Extracted from App.jsx behavior-preservingly as a dependency-injection
|
|
5
|
-
* factory: these openers drive
|
|
6
|
-
* store state, so they can't be pure. Every function body is the original
|
|
7
|
-
* logic verbatim, with closure identifiers threaded through the factory
|
|
5
|
+
* factory: these openers drive the panel surface + setSettingsPrompt and read
|
|
6
|
+
* live store state, so they can't be pure. Every function body is the original
|
|
7
|
+
* App logic verbatim, with closure identifiers threaded through the factory
|
|
8
8
|
* argument. The Esc-return target is per-entry: the Settings row passes
|
|
9
9
|
* { returnTo }, the standalone /memory command passes { returnTo: null },
|
|
10
10
|
* and Esc either reopens the caller or simply closes the picker.
|
|
11
11
|
*/
|
|
12
12
|
export function createCoreMemoryPicker({
|
|
13
13
|
store,
|
|
14
|
-
|
|
14
|
+
surface,
|
|
15
15
|
setSettingsPrompt,
|
|
16
16
|
parseMemoryCoreRows,
|
|
17
17
|
}) {
|
|
@@ -22,13 +22,19 @@ export function createCoreMemoryPicker({
|
|
|
22
22
|
let escReturnTo = null;
|
|
23
23
|
const closeMemoryCorePicker = () => {
|
|
24
24
|
if (escReturnTo) escReturnTo();
|
|
25
|
-
|
|
25
|
+
// Esc on the panel: this keypress owns the surface it clears.
|
|
26
|
+
else surface.claim().close();
|
|
26
27
|
};
|
|
27
28
|
const openMemoryCorePicker = (options = {}) => {
|
|
28
29
|
if (options && Object.prototype.hasOwnProperty.call(options, 'returnTo')) {
|
|
29
30
|
escReturnTo = typeof options.returnTo === 'function' ? options.returnTo : null;
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
+
// Surface claim (panel-surface.mjs): every paint re-validates and re-arms
|
|
33
|
+
// it, so the loading frame can take the surface while a list (or failure
|
|
34
|
+
// close) landing after Esc cannot touch it.
|
|
35
|
+
const own = surface.claim();
|
|
36
|
+
const paintPanel = (panel) => own.paint(panel);
|
|
37
|
+
paintPanel({
|
|
32
38
|
title: 'Memory',
|
|
33
39
|
// Loading state lives in the header description row, not as a fake
|
|
34
40
|
// selectable menu item.
|
|
@@ -53,7 +59,7 @@ export function createCoreMemoryPicker({
|
|
|
53
59
|
_rows: coreRows,
|
|
54
60
|
},
|
|
55
61
|
];
|
|
56
|
-
|
|
62
|
+
paintPanel({
|
|
57
63
|
title: 'Memory',
|
|
58
64
|
description: 'User-curated core memories across projects.',
|
|
59
65
|
items: rows.length ? rows : [{ value: 'empty', label: 'Memory', description: 'empty' }],
|
|
@@ -71,14 +77,17 @@ export function createCoreMemoryPicker({
|
|
|
71
77
|
});
|
|
72
78
|
})
|
|
73
79
|
.catch((e) => {
|
|
74
|
-
|
|
80
|
+
own.close();
|
|
75
81
|
store.pushNotice(`core memory failed: ${e?.message || e}`, 'error');
|
|
76
82
|
});
|
|
77
83
|
};
|
|
78
84
|
|
|
79
85
|
const openCoreMemoryListPicker = (rows = null) => {
|
|
86
|
+
// Same ownership rule as openMemoryCorePicker.
|
|
87
|
+
const own = surface.claim();
|
|
88
|
+
const paintPanel = (panel) => own.paint(panel);
|
|
80
89
|
const renderList = (coreRows) => {
|
|
81
|
-
|
|
90
|
+
paintPanel({
|
|
82
91
|
title: 'Memory · List',
|
|
83
92
|
description: coreRows.length
|
|
84
93
|
? 'Select a memory to edit or delete.'
|
|
@@ -101,7 +110,7 @@ export function createCoreMemoryPicker({
|
|
|
101
110
|
return;
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
|
|
113
|
+
paintPanel({
|
|
105
114
|
title: 'Memory · List',
|
|
106
115
|
description: 'Loading memories…',
|
|
107
116
|
items: [],
|
|
@@ -109,15 +118,19 @@ export function createCoreMemoryPicker({
|
|
|
109
118
|
onCancel: () => openMemoryCorePicker(),
|
|
110
119
|
});
|
|
111
120
|
void store.memoryControl?.({ action: 'core', op: 'list', project_id: '*' }, { silent: true })
|
|
112
|
-
.then((result) =>
|
|
121
|
+
.then((result) => {
|
|
122
|
+
renderList(parseMemoryCoreRows(result));
|
|
123
|
+
})
|
|
113
124
|
.catch((e) => {
|
|
114
|
-
|
|
125
|
+
own.close();
|
|
115
126
|
store.pushNotice(`core memory failed: ${e?.message || e}`, 'error');
|
|
116
127
|
});
|
|
117
128
|
};
|
|
118
129
|
|
|
119
130
|
const openCoreEntryActionsPicker = (entryItem) => {
|
|
120
|
-
|
|
131
|
+
// Synchronous panel for an Enter on a list row: an ordinary claimed paint.
|
|
132
|
+
const own = surface.claim();
|
|
133
|
+
own.paint({
|
|
121
134
|
title: `Memory · #${entryItem._id}`,
|
|
122
135
|
description: entryItem._summary || entryItem._element || '',
|
|
123
136
|
items: [
|
|
@@ -133,7 +146,7 @@ export function createCoreMemoryPicker({
|
|
|
133
146
|
};
|
|
134
147
|
|
|
135
148
|
const beginAddCoreMemory = () => {
|
|
136
|
-
|
|
149
|
+
surface.claim().close();
|
|
137
150
|
setSettingsPrompt({
|
|
138
151
|
kind: 'core-add',
|
|
139
152
|
label: 'Add memory',
|
|
@@ -142,7 +155,7 @@ export function createCoreMemoryPicker({
|
|
|
142
155
|
};
|
|
143
156
|
|
|
144
157
|
const beginEditCoreMemory = (entryItem) => {
|
|
145
|
-
|
|
158
|
+
surface.claim().close();
|
|
146
159
|
setSettingsPrompt({
|
|
147
160
|
kind: 'core-edit',
|
|
148
161
|
label: `Memory · Edit #${entryItem._id}`,
|
|
@@ -158,7 +171,7 @@ export function createCoreMemoryPicker({
|
|
|
158
171
|
};
|
|
159
172
|
|
|
160
173
|
const beginDeleteCoreMemory = (entryItem) => {
|
|
161
|
-
|
|
174
|
+
surface.claim().close();
|
|
162
175
|
setSettingsPrompt({
|
|
163
176
|
kind: 'core-delete-confirm',
|
|
164
177
|
label: `Memory · Delete #${entryItem._id}?`,
|