mixdog 1.3.2 → 1.3.3
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 +7 -7
- package/scripts/verify-runtime-dependency-versions.mjs +158 -0
- package/src/agents/reviewer/AGENT.md +5 -7
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +4 -0
- package/src/defaults/skills/browser-use/SKILL.md +6 -3
- package/src/defaults/skills/local-provider/SKILL.md +33 -6
- package/src/rules/lead/LEAD.md +5 -8
- package/src/rules/shared/45-destructive.md +5 -1
- package/src/rules/shared/60-verification.md +6 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +17 -4
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +40 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +32 -11
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +13 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +68 -12
- package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +31 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +117 -19
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +147 -137
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +37 -74
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +13 -7
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +12 -20
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +24 -15
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +7 -5
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +76 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +18 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +8 -2
- package/src/runtime/attachments/legacy-office.mjs +110 -0
- package/src/runtime/attachments/prompt-restore.mjs +70 -0
- package/src/runtime/attachments/store.mjs +60 -3
- package/src/runtime/effort-judge/judge-client.mjs +61 -14
- package/src/runtime/effort-judge/model-install.mjs +34 -2
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
- package/src/runtime/office/portable/portable-soffice.mjs +132 -30
- package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
- package/src/runtime/shared/agent-avatar.mjs +3 -2
- package/src/runtime/shared/agent-room-host.d.mts +31 -0
- package/src/runtime/shared/agent-room-host.mjs +58 -0
- package/src/runtime/shared/bot-hops.mjs +16 -11
- package/src/runtime/shared/redact-directories.mjs +18 -0
- package/src/runtime/shared/remove-directory.mjs +28 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
- package/src/runtime/shared/session-sidebar-host.mjs +16 -3
- package/src/runtime/shared/tool-execution-contract.mjs +4 -0
- package/src/runtime/shared/tool-work-units.mjs +12 -9
- package/src/runtime/shared/turn-snapshot.mjs +233 -122
- package/src/runtime/shared/user-data-guard.mjs +51 -19
- package/src/runtime/shared/worktrees/activity-gate.mjs +209 -227
- package/src/runtime/shared/worktrees/contract.mjs +11 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
- package/src/runtime/shared/worktrees/manager.mjs +419 -52
- package/src/runtime/shared/worktrees/registry.mjs +3 -1
- package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
- package/src/runtime/tidy/process.mjs +41 -8
- package/src/runtime/tidy/resolve.mjs +10 -7
- package/src/session-runtime/agent-home-delivery.mjs +73 -91
- package/src/session-runtime/agent-profile-changes.mjs +16 -0
- package/src/session-runtime/agent-room-delivery.mjs +2 -63
- package/src/session-runtime/agent-task-sessions.mjs +194 -64
- package/src/session-runtime/boot/apis.mjs +3 -0
- package/src/session-runtime/boot/facade.mjs +5 -6
- package/src/session-runtime/boot/tools.mjs +21 -1
- package/src/session-runtime/held-requests.mjs +20 -25
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +5 -22
- package/src/session-runtime/model-recency.mjs +1 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
- package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
- package/src/session-runtime/runtime-review-api.mjs +4 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -0
- package/src/session-runtime/services/provider-admin.mjs +12 -2
- package/src/session-runtime/services/worktree-access.mjs +27 -0
- package/src/session-runtime/session-hooks.mjs +9 -17
- package/src/session-runtime/settings-builtin-tools-api.mjs +2 -0
- package/src/session-runtime/setup-tool/executor.mjs +4 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +3 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +98 -14
- package/src/session-runtime/tool-policy/policy-stage.mjs +23 -5
- package/src/session-runtime/tool-policy-refresh.mjs +1 -0
- package/src/session-runtime/tool-surface.mjs +9 -5
- package/src/session-runtime/turn/session-ops.mjs +8 -0
- package/src/session-runtime/turn/turn-run.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +10 -2
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +39 -17
- package/src/session-runtime/workspace/transition-steps.mjs +1 -18
- package/src/session-runtime/workspace/transitions.mjs +279 -67
- package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
- package/src/standalone/daemon-lag-profiler.mjs +3 -1
- package/src/standalone/daemon-session-control.mjs +140 -23
- package/src/standalone/daemon-telemetry.mjs +9 -0
- package/src/standalone/daemon.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +11 -2
- package/src/standalone/session-protocol.mjs +1 -0
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
- package/src/standalone/session-service/agent-route-follow.mjs +69 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
- package/src/standalone/session-service.mjs +10 -0
- package/src/standalone/session-tool-output.mjs +1 -0
- package/src/standalone/staged-import.mjs +47 -0
- package/src/tui/App.jsx +2 -0
- package/src/tui/app/message-selector.mjs +15 -2
- package/src/tui/app/model-options.mjs +16 -2
- package/src/tui/app/model-picker.mjs +2 -1
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/dist/index.mjs +92 -40
- package/src/tui/paste-attachments.mjs +0 -1
- package/src/tui/paste-text-policy.mjs +4 -5
- package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
- package/src/tui/session/notice-surface.mjs +3 -0
- package/src/tui/session/session-api/intake/abort.mjs +33 -17
- package/src/tui/session/session-api/intake/rewind.mjs +39 -4
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +11 -48
- package/src/session-runtime/room-member-gate.mjs +0 -61
|
@@ -23,6 +23,7 @@ import { createViewerRegistry } from './session-service/viewers.mjs';
|
|
|
23
23
|
import { createSessionEntries } from './session-service/entries.mjs';
|
|
24
24
|
import { createSessionCalls } from './session-service/session-calls.mjs';
|
|
25
25
|
import { createQuotaResumeScheduler } from './session-service/quota-resume.mjs';
|
|
26
|
+
import { createAgentRouteFollow } from './session-service/agent-route-follow.mjs';
|
|
26
27
|
import { registerDelegatedApprovalOpener } from '../runtime/shared/delegated-approval-host.mjs';
|
|
27
28
|
|
|
28
29
|
const EXTERNAL_SESSION_ACTIONS = new Set([
|
|
@@ -209,6 +210,14 @@ export function createSessionService({
|
|
|
209
210
|
// A sub-agent's approval shows in its top-level chat; one not loaded is
|
|
210
211
|
// brought up so the copy (and its notifications) appear there.
|
|
211
212
|
const unregisterApprovalOpener = registerDelegatedApprovalOpener((sessionId) => calls.materializeSession(sessionId));
|
|
213
|
+
// A bot's saved route reaches its hosted home and room-member chats from their next turn.
|
|
214
|
+
const agentRouteFollow = createAgentRouteFollow({
|
|
215
|
+
sessionsById,
|
|
216
|
+
sessionOwner: projection.sessionOwner,
|
|
217
|
+
advance: projection.advance,
|
|
218
|
+
publishStep: projection.publishStep,
|
|
219
|
+
log,
|
|
220
|
+
});
|
|
212
221
|
const quotaUsableReader = typeof readQuotaUsable === 'function' ? readQuotaUsable : async () => false;
|
|
213
222
|
const quotaResume = quotaResumeStore
|
|
214
223
|
? createQuotaResumeScheduler({
|
|
@@ -224,6 +233,7 @@ export function createSessionService({
|
|
|
224
233
|
if (stopPromise) return stopPromise;
|
|
225
234
|
closed = true;
|
|
226
235
|
unregisterApprovalOpener();
|
|
236
|
+
agentRouteFollow.unsubscribe();
|
|
227
237
|
quotaResume?.stop();
|
|
228
238
|
stopPromise = Promise.resolve().then(async () => {
|
|
229
239
|
try {
|
|
@@ -24,6 +24,7 @@ export function sessionListOutput(page, limit, { rich = true } = {}) {
|
|
|
24
24
|
: {}),
|
|
25
25
|
...(row.status ? { status: row.status } : {}),
|
|
26
26
|
...(row.cwd ? { cwd: row.cwd } : {}),
|
|
27
|
+
...(row.archived === true ? { archived: true } : {}),
|
|
27
28
|
};
|
|
28
29
|
if (!rich) return base;
|
|
29
30
|
// The bot it belongs to: its chat, room seat or the task's creator.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Staged dynamic import of a large module graph. Loading a graph (file reads,
|
|
2
|
+
// compilation, evaluation) runs synchronously inside one import() call, so the
|
|
3
|
+
// daemon's session-runtime warm-up held the event loop for ~0.5 s in one
|
|
4
|
+
// piece right after `daemon-ready`. Importing the graph's top-level stages one
|
|
5
|
+
// loop turn apart splits that block without changing what is loaded.
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
// A static `import … from './x'`, `export … from './x'` or bare `import './x'`
|
|
10
|
+
// at the start of a line, in source order.
|
|
11
|
+
const STATIC_RELATIVE_IMPORT =
|
|
12
|
+
/^(?:import|export)\b[^;]*?\bfrom\s*['"](\.{1,2}\/[^'"]+)['"]|^import\s*['"](\.{1,2}\/[^'"]+)['"]/gm;
|
|
13
|
+
|
|
14
|
+
/** The relative static imports of the module at `url`, resolved, in source order. */
|
|
15
|
+
export function staticRelativeImports(url) {
|
|
16
|
+
const source = readFileSync(fileURLToPath(url), 'utf8');
|
|
17
|
+
return [...source.matchAll(STATIC_RELATIVE_IMPORT)].map((match) => new URL(match[1] || match[2], url).href);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const nextTurn = () => new Promise((resolve) => setImmediate(resolve));
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* import(url), after importing each static relative dependency of `stagesOf`
|
|
24
|
+
* (in its source order, one loop turn apart). Module evaluation order is the
|
|
25
|
+
* one a single import gives — dependencies depth-first in source order — as
|
|
26
|
+
* long as no module of a stage's graph imports `stagesOf` or an importer of it
|
|
27
|
+
* back. Staging stops at the first stage that fails, so no later stage
|
|
28
|
+
* evaluates; the final import then reports the failure as a single import
|
|
29
|
+
* would.
|
|
30
|
+
*/
|
|
31
|
+
export async function importInStages(url, stagesOf) {
|
|
32
|
+
let stages = [];
|
|
33
|
+
try {
|
|
34
|
+
stages = staticRelativeImports(stagesOf);
|
|
35
|
+
} catch {
|
|
36
|
+
// Unreadable source: one import, as before.
|
|
37
|
+
}
|
|
38
|
+
for (const stage of stages) {
|
|
39
|
+
try {
|
|
40
|
+
await import(stage);
|
|
41
|
+
} catch {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
await nextTurn();
|
|
45
|
+
}
|
|
46
|
+
return import(url);
|
|
47
|
+
}
|
package/src/tui/App.jsx
CHANGED
|
@@ -516,6 +516,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
516
516
|
syncPromptLayoutRows,
|
|
517
517
|
showPromptHint,
|
|
518
518
|
clearPromptHint,
|
|
519
|
+
installPastedImages,
|
|
520
|
+
installPastedTexts,
|
|
519
521
|
});
|
|
520
522
|
|
|
521
523
|
// PROMPT HANDLER cluster extracted to app/use-prompt-handlers.mjs.
|
|
@@ -45,6 +45,8 @@ export function useMessageSelector({
|
|
|
45
45
|
syncPromptLayoutRows,
|
|
46
46
|
showPromptHint,
|
|
47
47
|
clearPromptHint,
|
|
48
|
+
installPastedImages,
|
|
49
|
+
installPastedTexts,
|
|
48
50
|
}) {
|
|
49
51
|
// Cheap enough to recompute only when the transcript array identity changes;
|
|
50
52
|
// the prompt asks for this on every Escape, never per keystroke.
|
|
@@ -64,11 +66,22 @@ export function useMessageSelector({
|
|
|
64
66
|
return false;
|
|
65
67
|
}
|
|
66
68
|
clearPromptHint();
|
|
69
|
+
// The prompt's pasted blocks return with it, so its tokens still expand.
|
|
70
|
+
if (restored.pastedImages) installPastedImages?.(restored.pastedImages, { merge: true });
|
|
71
|
+
if (restored.pastedTexts) installPastedTexts?.(restored.pastedTexts, { merge: true });
|
|
67
72
|
syncPromptLayoutRows(text);
|
|
68
73
|
setPromptDraftOverride({ id: Date.now(), value: text });
|
|
74
|
+
if (restored.notice) showPromptHint(restored.notice, 'error');
|
|
69
75
|
return true;
|
|
70
76
|
},
|
|
71
|
-
[
|
|
77
|
+
[
|
|
78
|
+
clearPromptHint,
|
|
79
|
+
syncPromptLayoutRows,
|
|
80
|
+
setPromptDraftOverride,
|
|
81
|
+
showPromptHint,
|
|
82
|
+
installPastedImages,
|
|
83
|
+
installPastedTexts,
|
|
84
|
+
]
|
|
72
85
|
);
|
|
73
86
|
|
|
74
87
|
const openMessageSelector = useCallback(() => {
|
|
@@ -89,7 +102,7 @@ export function useMessageSelector({
|
|
|
89
102
|
own.close();
|
|
90
103
|
let restored;
|
|
91
104
|
try {
|
|
92
|
-
restored = store.rewindToItem?.(value);
|
|
105
|
+
restored = store.rewindToItem?.(value, { restoreFiles: false });
|
|
93
106
|
} catch (error) {
|
|
94
107
|
showPromptHint(`rewind failed: ${error?.message || error}`, 'error');
|
|
95
108
|
return;
|
|
@@ -221,6 +221,18 @@ export const loadProviderDisplayNames = async (store) => {
|
|
|
221
221
|
return names;
|
|
222
222
|
};
|
|
223
223
|
|
|
224
|
+
/** Provider names for the picker: custom rows' own `providerName` first, then
|
|
225
|
+
* setup names over them (setup wins). Works when setup is missing or failed. */
|
|
226
|
+
export const catalogProviderDisplayNames = (models, setupNames = null) => {
|
|
227
|
+
const names = {};
|
|
228
|
+
for (const model of Array.isArray(models) ? models : []) {
|
|
229
|
+
const provider = String(model?.provider || '').trim();
|
|
230
|
+
const name = String(model?.providerName || '').trim();
|
|
231
|
+
if (provider.startsWith('custom-') && name) names[provider] = name;
|
|
232
|
+
}
|
|
233
|
+
return { ...names, ...setupNames };
|
|
234
|
+
};
|
|
235
|
+
|
|
224
236
|
/** `{ id, text }` for each provider whose model list failed, with the reason
|
|
225
237
|
* (the setup row's `catalogError`) and what to do about it. */
|
|
226
238
|
export const loadProviderCatalogFailures = async (store) => {
|
|
@@ -368,9 +380,11 @@ const routeModelDetails = (route) =>
|
|
|
368
380
|
.filter(Boolean)
|
|
369
381
|
.join(' · ');
|
|
370
382
|
|
|
371
|
-
export const routeLabel = (route) => {
|
|
383
|
+
export const routeLabel = (route, providerNames = null) => {
|
|
372
384
|
if (!route?.provider || !route?.model) return '(unset)';
|
|
373
|
-
return [
|
|
385
|
+
return [resolveProviderDisplayName(route.provider, providerNames), routeModelDetails(route)]
|
|
386
|
+
.filter(Boolean)
|
|
387
|
+
.join(' · ');
|
|
374
388
|
};
|
|
375
389
|
|
|
376
390
|
export const routeModelLabel = (route) => (route?.model ? routeModelDetails(route) : '(unset)');
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
normalizeModelOptions,
|
|
15
15
|
buildCatalogFailureItems,
|
|
16
16
|
buildModelProviderItems,
|
|
17
|
+
catalogProviderDisplayNames,
|
|
17
18
|
loadProviderCatalogFailures,
|
|
18
19
|
loadProviderDisplayNames,
|
|
19
20
|
} from './model-options.mjs';
|
|
@@ -83,7 +84,7 @@ export function createModelPicker({
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
const models = normalizeModelOptions(providerModels);
|
|
86
|
-
const providerNames = await loadProviderDisplayNames(store);
|
|
87
|
+
const providerNames = catalogProviderDisplayNames(models, await loadProviderDisplayNames(store));
|
|
87
88
|
const catalogFailures = await loadProviderCatalogFailures(store);
|
|
88
89
|
const activeRoute = options.currentRoute || {
|
|
89
90
|
provider: state.provider,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* openers (openModelPicker) thread as lazy getter wrappers so they resolve the
|
|
8
8
|
* live binding at call time.
|
|
9
9
|
*/
|
|
10
|
+
import { loadProviderDisplayNames } from './model-options.mjs';
|
|
10
11
|
import { createAgentsPicker } from './route-pickers/agents-picker.mjs';
|
|
11
12
|
import { createOutputStylePicker } from './route-pickers/output-style-picker.mjs';
|
|
12
13
|
import { createWorkflowPicker } from './route-pickers/workflow-picker.mjs';
|
|
@@ -59,7 +60,8 @@ export function createRoutePickers({
|
|
|
59
60
|
store.pushNotice('Web-search model save is already running.', 'warn');
|
|
60
61
|
return;
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
+
const providerNames = await loadProviderDisplayNames(store);
|
|
64
|
+
store.pushNotice(`Web-search model set to ${routeLabel(result, providerNames)}`, 'info');
|
|
63
65
|
return result;
|
|
64
66
|
},
|
|
65
67
|
onAfterSelect: null,
|
package/src/tui/dist/index.mjs
CHANGED
|
@@ -41529,6 +41529,14 @@ function gitStageUnit(a) {
|
|
|
41529
41529
|
effect: true
|
|
41530
41530
|
});
|
|
41531
41531
|
}
|
|
41532
|
+
function gitWorktreeUnit(a) {
|
|
41533
|
+
return unitDescriptor("Git", {
|
|
41534
|
+
active: "Managing",
|
|
41535
|
+
done: "Managed",
|
|
41536
|
+
noun: "worktree",
|
|
41537
|
+
effect: String(a.worktree_action || "").toLowerCase() !== "list"
|
|
41538
|
+
});
|
|
41539
|
+
}
|
|
41532
41540
|
function cwdUnit(a) {
|
|
41533
41541
|
const action = String(a.action || a.type || "").toLowerCase();
|
|
41534
41542
|
if (action === "set" || action === "set-project" || !action && a.path) {
|
|
@@ -41559,14 +41567,6 @@ function cwdUnit(a) {
|
|
|
41559
41567
|
const [active, done] = words;
|
|
41560
41568
|
return unitDescriptor("Setup", { active, done, noun: "session", effect: true });
|
|
41561
41569
|
}
|
|
41562
|
-
if (action.endsWith("-worktree") || action.endsWith("-worktrees")) {
|
|
41563
|
-
return unitDescriptor("Setup", {
|
|
41564
|
-
active: "Managing",
|
|
41565
|
-
done: "Managed",
|
|
41566
|
-
noun: "worktree",
|
|
41567
|
-
effect: !action.startsWith("list")
|
|
41568
|
-
});
|
|
41569
|
-
}
|
|
41570
41570
|
if (action === "status") {
|
|
41571
41571
|
return unitDescriptor("Setup", { active: "Checking", done: "Checked", noun: "session" });
|
|
41572
41572
|
}
|
|
@@ -41661,7 +41661,7 @@ var TOOL_UNITS = /* @__PURE__ */ new Map([
|
|
|
41661
41661
|
["job_wait", shellUnit],
|
|
41662
41662
|
[
|
|
41663
41663
|
"git",
|
|
41664
|
-
(a) => a.action === "stage" ? gitStageUnit(a) : unitDescriptor("Git", { count: queryCount(a, "command", "commands") || 1, noun: "Git command" })
|
|
41664
|
+
(a) => a.action === "stage" ? gitStageUnit(a) : a.action === "worktree" ? gitWorktreeUnit(a) : unitDescriptor("Git", { count: queryCount(a, "command", "commands") || 1, noun: "Git command" })
|
|
41665
41665
|
],
|
|
41666
41666
|
["github", githubUnit],
|
|
41667
41667
|
// Preserve the staging work unit when rendering historical transcripts.
|
|
@@ -47542,17 +47542,14 @@ async function resizeImageBuffer(buffer, ext, { maxTokens = DEFAULT_IMAGE_MAX_TO
|
|
|
47542
47542
|
var TOOL_IMAGE_TARGET_BASE64_SIZE = 256 * 1024;
|
|
47543
47543
|
|
|
47544
47544
|
// src/tui/paste-text-policy.mjs
|
|
47545
|
-
var
|
|
47546
|
-
var PASTE_TOKEN_MIN_CHARS = 200;
|
|
47545
|
+
var PASTE_TOKEN_MIN_CHARS = 1e3;
|
|
47547
47546
|
function pastedTextLineCount(text) {
|
|
47548
47547
|
const value = String(text ?? "").replace(/\r\n?/g, "\n").replace(/\n+$/, "");
|
|
47549
47548
|
if (!value) return 0;
|
|
47550
47549
|
return value.split("\n").length;
|
|
47551
47550
|
}
|
|
47552
47551
|
function shouldFoldPastedText(text) {
|
|
47553
|
-
|
|
47554
|
-
if (!value) return false;
|
|
47555
|
-
return pastedTextLineCount(value) >= PASTE_TOKEN_MIN_LINES || value.length > PASTE_TOKEN_MIN_CHARS;
|
|
47552
|
+
return String(text ?? "").length > PASTE_TOKEN_MIN_CHARS;
|
|
47556
47553
|
}
|
|
47557
47554
|
function formatPastedTextRef(id, text) {
|
|
47558
47555
|
return `[Pasted text #${id} +${pastedTextLineCount(text)} lines]`;
|
|
@@ -59570,6 +59567,15 @@ var loadProviderDisplayNames = async (store) => {
|
|
|
59570
59567
|
}
|
|
59571
59568
|
return names;
|
|
59572
59569
|
};
|
|
59570
|
+
var catalogProviderDisplayNames = (models, setupNames = null) => {
|
|
59571
|
+
const names = {};
|
|
59572
|
+
for (const model of Array.isArray(models) ? models : []) {
|
|
59573
|
+
const provider = String(model?.provider || "").trim();
|
|
59574
|
+
const name = String(model?.providerName || "").trim();
|
|
59575
|
+
if (provider.startsWith("custom-") && name) names[provider] = name;
|
|
59576
|
+
}
|
|
59577
|
+
return { ...names, ...setupNames };
|
|
59578
|
+
};
|
|
59573
59579
|
var loadProviderCatalogFailures = async (store) => {
|
|
59574
59580
|
let setup = null;
|
|
59575
59581
|
try {
|
|
@@ -59683,9 +59689,9 @@ var routeSpeedLabel = (route) => {
|
|
|
59683
59689
|
return route.modelParameters?.serviceTier === "ultrafast" ? "Ultrafast" : "Fast";
|
|
59684
59690
|
};
|
|
59685
59691
|
var routeModelDetails = (route) => [routeModelDisplayName(route), route.effort ? effortDisplayLabel(route.effort) : "", routeSpeedLabel(route)].filter(Boolean).join(" \xB7 ");
|
|
59686
|
-
var routeLabel = (route) => {
|
|
59692
|
+
var routeLabel = (route, providerNames = null) => {
|
|
59687
59693
|
if (!route?.provider || !route?.model) return "(unset)";
|
|
59688
|
-
return [
|
|
59694
|
+
return [resolveProviderDisplayName(route.provider, providerNames), routeModelDetails(route)].filter(Boolean).join(" \xB7 ");
|
|
59689
59695
|
};
|
|
59690
59696
|
var routeModelLabel = (route) => route?.model ? routeModelDetails(route) : "(unset)";
|
|
59691
59697
|
var agentModelProfile = (route) => route?.model ? routeModelDetails(route) : "";
|
|
@@ -61162,7 +61168,7 @@ function createModelPicker({
|
|
|
61162
61168
|
return;
|
|
61163
61169
|
}
|
|
61164
61170
|
const models = normalizeModelOptions(providerModels);
|
|
61165
|
-
const providerNames = await loadProviderDisplayNames(store);
|
|
61171
|
+
const providerNames = catalogProviderDisplayNames(models, await loadProviderDisplayNames(store));
|
|
61166
61172
|
const catalogFailures = await loadProviderCatalogFailures(store);
|
|
61167
61173
|
const activeRoute = options.currentRoute || {
|
|
61168
61174
|
provider: state.provider,
|
|
@@ -61590,7 +61596,8 @@ function createRoutePickers({
|
|
|
61590
61596
|
store.pushNotice("Web-search model save is already running.", "warn");
|
|
61591
61597
|
return;
|
|
61592
61598
|
}
|
|
61593
|
-
|
|
61599
|
+
const providerNames = await loadProviderDisplayNames(store);
|
|
61600
|
+
store.pushNotice(`Web-search model set to ${routeLabel2(result, providerNames)}`, "info");
|
|
61594
61601
|
return result;
|
|
61595
61602
|
},
|
|
61596
61603
|
onAfterSelect: null
|
|
@@ -61950,8 +61957,16 @@ function copyTree(src, dst, copied) {
|
|
|
61950
61957
|
writeFileAtomicSync(dst, readFileSync10(src), { mode: st.mode & 511 });
|
|
61951
61958
|
copied.push(dst);
|
|
61952
61959
|
}
|
|
61960
|
+
var manifestPath = (scopeDir, name) => join11(scopeDir, name, BACKUP_MANIFEST);
|
|
61953
61961
|
function pruneBackups(scopeDir, names, own) {
|
|
61954
|
-
|
|
61962
|
+
const completed = names.filter((name) => {
|
|
61963
|
+
try {
|
|
61964
|
+
return statSync4(manifestPath(scopeDir, name)).isFile();
|
|
61965
|
+
} catch {
|
|
61966
|
+
return false;
|
|
61967
|
+
}
|
|
61968
|
+
});
|
|
61969
|
+
for (const name of backupsToPrune(completed, own)) {
|
|
61955
61970
|
try {
|
|
61956
61971
|
rmSync(join11(scopeDir, name), { recursive: true, force: true });
|
|
61957
61972
|
} catch {
|
|
@@ -62001,16 +62016,24 @@ function backupUserData(dataDir2, reason = "snapshot", { dirs = [] } = {}) {
|
|
|
62001
62016
|
const scopeDir = scopeDirFor(dataDir2);
|
|
62002
62017
|
const { backupDir, names } = reserveBackupDir(scopeDir, reason);
|
|
62003
62018
|
const copied = [];
|
|
62004
|
-
|
|
62005
|
-
const
|
|
62006
|
-
|
|
62007
|
-
|
|
62008
|
-
|
|
62009
|
-
const
|
|
62010
|
-
|
|
62019
|
+
try {
|
|
62020
|
+
for (const rel of USER_DATA_FILES) {
|
|
62021
|
+
const src = join11(dataDir2, rel);
|
|
62022
|
+
if (existsSync9(src)) copyTree(src, join11(backupDir, rel), copied);
|
|
62023
|
+
}
|
|
62024
|
+
for (const rel of [...USER_DATA_DIRS, ...dirs]) {
|
|
62025
|
+
const src = join11(dataDir2, rel);
|
|
62026
|
+
if (existsSync9(src)) copyTree(src, join11(backupDir, rel), copied);
|
|
62027
|
+
}
|
|
62028
|
+
if (copied.length > 0) finishBackup(dataDir2, backupDir, reason);
|
|
62029
|
+
} catch (error) {
|
|
62030
|
+
try {
|
|
62031
|
+
rmSync(backupDir, { recursive: true, force: true });
|
|
62032
|
+
} catch {
|
|
62033
|
+
}
|
|
62034
|
+
throw error;
|
|
62011
62035
|
}
|
|
62012
62036
|
if (copied.length > 0) {
|
|
62013
|
-
finishBackup(dataDir2, backupDir, reason);
|
|
62014
62037
|
pruneBackups(scopeDir, names, basename5(backupDir));
|
|
62015
62038
|
if (process.env.MIXDOG_SETUP_QUIET !== "1") {
|
|
62016
62039
|
process.stderr.write(`[user-data-backup] ${reason}: copied ${copied.length} file(s) to ${backupDir}
|
|
@@ -62034,7 +62057,14 @@ async function copyTreeAsync(src, dst, copied) {
|
|
|
62034
62057
|
copied.push(dst);
|
|
62035
62058
|
}
|
|
62036
62059
|
async function pruneBackupsAsync(scopeDir, names, own) {
|
|
62037
|
-
|
|
62060
|
+
const completed = [];
|
|
62061
|
+
for (const name of names) {
|
|
62062
|
+
try {
|
|
62063
|
+
if ((await statP(manifestPath(scopeDir, name))).isFile()) completed.push(name);
|
|
62064
|
+
} catch {
|
|
62065
|
+
}
|
|
62066
|
+
}
|
|
62067
|
+
for (const name of backupsToPrune(completed, own)) {
|
|
62038
62068
|
try {
|
|
62039
62069
|
await rmP(join11(scopeDir, name), { recursive: true, force: true });
|
|
62040
62070
|
} catch {
|
|
@@ -62047,16 +62077,24 @@ async function backupUserDataAsync(dataDir2, reason = "snapshot") {
|
|
|
62047
62077
|
const scopeDir = scopeDirFor(dataDir2);
|
|
62048
62078
|
const { backupDir, names } = await reserveBackupDirAsync(scopeDir, reason);
|
|
62049
62079
|
const copied = [];
|
|
62050
|
-
|
|
62051
|
-
const
|
|
62052
|
-
|
|
62053
|
-
|
|
62054
|
-
|
|
62055
|
-
const
|
|
62056
|
-
|
|
62080
|
+
try {
|
|
62081
|
+
for (const rel of USER_DATA_FILES) {
|
|
62082
|
+
const src = join11(dataDir2, rel);
|
|
62083
|
+
if (existsSync9(src)) await copyTreeAsync(src, join11(backupDir, rel), copied);
|
|
62084
|
+
}
|
|
62085
|
+
for (const rel of USER_DATA_DIRS) {
|
|
62086
|
+
const src = join11(dataDir2, rel);
|
|
62087
|
+
if (existsSync9(src)) await copyTreeAsync(src, join11(backupDir, rel), copied);
|
|
62088
|
+
}
|
|
62089
|
+
if (copied.length > 0) finishBackup(dataDir2, backupDir, reason);
|
|
62090
|
+
} catch (error) {
|
|
62091
|
+
try {
|
|
62092
|
+
await rmP(backupDir, { recursive: true, force: true });
|
|
62093
|
+
} catch {
|
|
62094
|
+
}
|
|
62095
|
+
throw error;
|
|
62057
62096
|
}
|
|
62058
62097
|
if (copied.length > 0) {
|
|
62059
|
-
finishBackup(dataDir2, backupDir, reason);
|
|
62060
62098
|
await pruneBackupsAsync(scopeDir, names, basename5(backupDir));
|
|
62061
62099
|
if (process.env.MIXDOG_SETUP_QUIET !== "1") {
|
|
62062
62100
|
process.stderr.write(`[user-data-backup] ${reason}: copied ${copied.length} file(s) to ${backupDir}
|
|
@@ -63969,7 +64007,9 @@ function useMessageSelector({
|
|
|
63969
64007
|
setPromptDraftOverride,
|
|
63970
64008
|
syncPromptLayoutRows,
|
|
63971
64009
|
showPromptHint,
|
|
63972
|
-
clearPromptHint
|
|
64010
|
+
clearPromptHint,
|
|
64011
|
+
installPastedImages,
|
|
64012
|
+
installPastedTexts
|
|
63973
64013
|
}) {
|
|
63974
64014
|
const hasUserMessages = useMemo7(
|
|
63975
64015
|
() => (Array.isArray(state.items) ? state.items : []).some(
|
|
@@ -63985,11 +64025,21 @@ function useMessageSelector({
|
|
|
63985
64025
|
return false;
|
|
63986
64026
|
}
|
|
63987
64027
|
clearPromptHint();
|
|
64028
|
+
if (restored.pastedImages) installPastedImages?.(restored.pastedImages, { merge: true });
|
|
64029
|
+
if (restored.pastedTexts) installPastedTexts?.(restored.pastedTexts, { merge: true });
|
|
63988
64030
|
syncPromptLayoutRows(text);
|
|
63989
64031
|
setPromptDraftOverride({ id: Date.now(), value: text });
|
|
64032
|
+
if (restored.notice) showPromptHint(restored.notice, "error");
|
|
63990
64033
|
return true;
|
|
63991
64034
|
},
|
|
63992
|
-
[
|
|
64035
|
+
[
|
|
64036
|
+
clearPromptHint,
|
|
64037
|
+
syncPromptLayoutRows,
|
|
64038
|
+
setPromptDraftOverride,
|
|
64039
|
+
showPromptHint,
|
|
64040
|
+
installPastedImages,
|
|
64041
|
+
installPastedTexts
|
|
64042
|
+
]
|
|
63993
64043
|
);
|
|
63994
64044
|
const openMessageSelector = useCallback20(() => {
|
|
63995
64045
|
const items = store.getState?.().items ?? state.items;
|
|
@@ -64009,7 +64059,7 @@ function useMessageSelector({
|
|
|
64009
64059
|
own.close();
|
|
64010
64060
|
let restored;
|
|
64011
64061
|
try {
|
|
64012
|
-
restored = store.rewindToItem?.(value);
|
|
64062
|
+
restored = store.rewindToItem?.(value, { restoreFiles: false });
|
|
64013
64063
|
} catch (error) {
|
|
64014
64064
|
showPromptHint(`rewind failed: ${error?.message || error}`, "error");
|
|
64015
64065
|
return;
|
|
@@ -65920,7 +65970,9 @@ function App2({ store, initialStatusLine = "", forceOnboarding = false, onboardi
|
|
|
65920
65970
|
setPromptDraftOverride,
|
|
65921
65971
|
syncPromptLayoutRows,
|
|
65922
65972
|
showPromptHint,
|
|
65923
|
-
clearPromptHint
|
|
65973
|
+
clearPromptHint,
|
|
65974
|
+
installPastedImages,
|
|
65975
|
+
installPastedTexts
|
|
65924
65976
|
});
|
|
65925
65977
|
const { handlePromptPaste, handlePromptHistoryNavigate, handlePromptEscape, handlePromptInterrupt } = usePromptHandlers({
|
|
65926
65978
|
store,
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
// A big paste is replaced in the prompt by a compact token so the editor does
|
|
4
4
|
// not choke on thousands of raw characters; the original text lives in the
|
|
5
5
|
// surface's pasted-text map and is expanded back on submit.
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// Same rule as Claude Code / Codex (LARGE_PASTE_CHAR_THRESHOLD): only a paste
|
|
7
|
+
// longer than this many characters folds; the line count never matters.
|
|
8
|
+
export const PASTE_TOKEN_MIN_CHARS = 1000;
|
|
8
9
|
|
|
9
10
|
// VISIBLE lines: a copy that ends in a newline ("a\nb\n") is two lines, not
|
|
10
11
|
// three. Counting the empty trailing chunk folded ordinary two-line copies
|
|
@@ -18,9 +19,7 @@ export function pastedTextLineCount(text) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export function shouldFoldPastedText(text) {
|
|
21
|
-
|
|
22
|
-
if (!value) return false;
|
|
23
|
-
return pastedTextLineCount(value) >= PASTE_TOKEN_MIN_LINES || value.length > PASTE_TOKEN_MIN_CHARS;
|
|
22
|
+
return String(text ?? '').length > PASTE_TOKEN_MIN_CHARS;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
export function formatPastedTextRef(id, text) {
|
|
@@ -86,10 +86,13 @@ export function createExecutionDelivery({
|
|
|
86
86
|
// active loop can attach them after the next tool batch. The immediate
|
|
87
87
|
// response card was already pushed above, so the queued twin stays
|
|
88
88
|
// model-visible but suppresses its drain-time transcript card.
|
|
89
|
-
const { execution } = markCompletionEntry(resumeBody, { executionId, meta: event?.meta });
|
|
89
|
+
const { execution, hop } = markCompletionEntry(resumeBody, { executionId, meta: event?.meta });
|
|
90
90
|
const enqueued = enqueue(resumeBody, {
|
|
91
91
|
mode: 'task-notification',
|
|
92
92
|
...(execution ? { execution } : {}),
|
|
93
|
+
// The launching turn's bot-chain hop: the turn this wakes, or the running
|
|
94
|
+
// turn this joins, resumes at it (bot-hops.mjs).
|
|
95
|
+
...(hop ? { transcriptMeta: { hop } } : {}),
|
|
93
96
|
priority: 'next',
|
|
94
97
|
key: notificationKey || undefined,
|
|
95
98
|
abortDiscardOnAbort: true,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/tui/session/notice-surface.mjs - toasts, transcript notices, progress hint.
|
|
3
3
|
*/
|
|
4
|
+
import { roomRowSender } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
4
5
|
import { polishNoticeText } from './notice-text.mjs';
|
|
5
6
|
import { nextId } from './transcript-spill.mjs';
|
|
6
7
|
|
|
@@ -59,6 +60,8 @@ export function createNoticeSurface({ getState, set, isDisposed, pushItem, repla
|
|
|
59
60
|
// A schedule result posted into a home thread (routine-result.mjs).
|
|
60
61
|
...(options.routine ? { routine: options.routine } : {}),
|
|
61
62
|
...(options.room ? { room: options.room } : {}),
|
|
63
|
+
// A room message another session sent shows its sender, as restored.
|
|
64
|
+
...(userRow ? roomRowSender(options.room) : {}),
|
|
62
65
|
// A room user row's files, in the fields a prompt row draws them from;
|
|
63
66
|
// a task report's or member speech's returned files likewise.
|
|
64
67
|
...(Array.isArray(options.attachments) && options.attachments.length ? { attachments: options.attachments } : {}),
|
|
@@ -8,7 +8,7 @@ import { promptHistoryWithout } from '../../prompt-history.mjs';
|
|
|
8
8
|
import { isQueuedEntryEditable } from '../../queue-helpers.mjs';
|
|
9
9
|
|
|
10
10
|
// An attachment whose blob is gone is dropped alone; the prompt is kept.
|
|
11
|
-
function hydrateTolerantly(pastedImages, pastedTexts, content = null) {
|
|
11
|
+
export function hydrateTolerantly(pastedImages, pastedTexts, content = null) {
|
|
12
12
|
let unreadable = 0;
|
|
13
13
|
const onUnreadable = () => {
|
|
14
14
|
unreadable += 1;
|
|
@@ -57,9 +57,13 @@ export function createAbortAction(bag) {
|
|
|
57
57
|
|
|
58
58
|
// Pull the interrupted prompt back out of the transcript and history and
|
|
59
59
|
// put its unsent requeue entries at the front of the queue.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
// `keepPrompt` only requeues: the submitted rows stay in the transcript.
|
|
61
|
+
const reclaimInFlightPrompt = (
|
|
62
|
+
restoreState,
|
|
63
|
+
{ restoreText, requeueEntries, handoff = false, keepPrompt = false }
|
|
64
|
+
) => {
|
|
65
|
+
if (!keepPrompt) restoreState.reclaimed = true;
|
|
66
|
+
const idSet = new Set((keepPrompt ? [] : restoreState.submittedIds || []).filter((id) => id != null));
|
|
63
67
|
const patch = { spinner: null, thinking: null, lastTurn: null };
|
|
64
68
|
// Another device cancelled this turn: the prompt travels to the device that
|
|
65
69
|
// sent it through the state, never through the canceller's reply.
|
|
@@ -125,14 +129,26 @@ export function createAbortAction(bag) {
|
|
|
125
129
|
const canceller = typeof options?.device === 'string' ? options.device : '';
|
|
126
130
|
const crossDevice = Boolean(canceller && restoreState?.device && canceller !== restoreState.device);
|
|
127
131
|
const hasAttachments = Boolean(restoreState?.pastedImages || restoreState?.pastedTexts || restoreState?.content);
|
|
128
|
-
const
|
|
132
|
+
const wantsRestore =
|
|
129
133
|
options?.restorePrompt !== false &&
|
|
130
134
|
restoreState?.restorable &&
|
|
131
135
|
!hasPendingSteering &&
|
|
132
136
|
!(crossDevice && hasAttachments);
|
|
137
|
+
// Hydrate BEFORE the turn is aborted or the prompt reclaimed: an attachment
|
|
138
|
+
// read error then cannot strand a prompt that left the transcript with no
|
|
139
|
+
// draft to land in. On failure the prompt is kept (like a Stop that hands
|
|
140
|
+
// nothing back) and the error is surfaced as the notice.
|
|
141
|
+
let restored = null;
|
|
142
|
+
let hydrateFailure = '';
|
|
143
|
+
if (wantsRestore) {
|
|
144
|
+
try {
|
|
145
|
+
restored = hydrateTolerantly(restoreState?.pastedImages, restoreState?.pastedTexts, restoreState?.content);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
hydrateFailure = `Could not restore the attachments (${error?.message || error}); the prompt was kept.`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const canRestore = wantsRestore && !hydrateFailure;
|
|
133
151
|
const restoreText = canRestore ? restoreState.text : '';
|
|
134
|
-
const restorePastedImages = canRestore && restoreState?.pastedImages ? restoreState.pastedImages : null;
|
|
135
|
-
const restorePastedTexts = canRestore && restoreState?.pastedTexts ? restoreState.pastedTexts : null;
|
|
136
152
|
// When steering suppresses the restore, the interrupted prompt's pasted
|
|
137
153
|
// images never get committed (onCommitted won't fire) nor re-installed into
|
|
138
154
|
// the draft, so hand them back for cleanup to avoid a stale `[Image #id]`
|
|
@@ -170,28 +186,28 @@ export function createAbortAction(bag) {
|
|
|
170
186
|
discardExecutionPendingResume?.(restoreState.discardExecutionPendingResumeKeys);
|
|
171
187
|
}
|
|
172
188
|
if ((restoreText || requeueEntries.length > 0) && aborted !== false) {
|
|
173
|
-
reclaimInFlightPrompt(restoreState, {
|
|
189
|
+
reclaimInFlightPrompt(restoreState, {
|
|
190
|
+
restoreText,
|
|
191
|
+
requeueEntries,
|
|
192
|
+
handoff: crossDevice,
|
|
193
|
+
keepPrompt: Boolean(hydrateFailure),
|
|
194
|
+
});
|
|
174
195
|
}
|
|
175
196
|
restoreState.restorable = false;
|
|
176
197
|
restoreState.requeueEntries = [];
|
|
177
198
|
restoreState.discardExecutionPendingResumeKeys = [];
|
|
178
199
|
}
|
|
179
200
|
kickDrainAfterAbort();
|
|
180
|
-
const restored = hydrateTolerantly(
|
|
181
|
-
restorePastedImages,
|
|
182
|
-
restorePastedTexts,
|
|
183
|
-
canRestore ? restoreState?.content : null
|
|
184
|
-
);
|
|
185
201
|
// The canceller never receives another device's prompt.
|
|
186
202
|
const handedOver = crossDevice && Boolean(restoreText) && aborted !== false;
|
|
187
203
|
return {
|
|
188
204
|
aborted,
|
|
189
205
|
restoreText: handedOver ? '' : restoreText,
|
|
190
|
-
...(restored
|
|
191
|
-
...(restored
|
|
192
|
-
pastedImages: restored
|
|
206
|
+
...(restored?.content ? { content: restored.content } : {}),
|
|
207
|
+
...(restored?.notice || hydrateFailure ? { notice: restored?.notice || hydrateFailure } : {}),
|
|
208
|
+
pastedImages: restored?.pastedImages ?? null,
|
|
193
209
|
discardPastedImages,
|
|
194
|
-
pastedTexts: restored
|
|
210
|
+
pastedTexts: restored?.pastedTexts ?? null,
|
|
195
211
|
discardPastedTexts,
|
|
196
212
|
restoredSubmissionIds:
|
|
197
213
|
restoreText && !handedOver ? (restoreState?.submittedIds || []).map(String).filter(Boolean) : [],
|