mixdog 1.3.2 → 1.3.4
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 +8 -8
- 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/llm/usage-rollup.mjs +4 -383
- 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/agent-tool-routing.mjs +49 -4
- 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 +23 -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/close-flow.mjs +14 -2
- package/src/session-runtime/services/agent-tool/execute.mjs +6 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +44 -20
- package/src/session-runtime/services/agent-tool/render.mjs +3 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +1 -1
- 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/usage-stats-api.mjs +1 -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 +403 -210
- 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/vendor/statusline/src/gateway/claude-current.mjs +0 -58
- package/src/vendor/statusline/src/gateway/route-meta.mjs +1 -361
- package/src/vendor/statusline/src/gateway/session-routes.mjs +0 -55
- package/src/session-runtime/room-member-gate.mjs +0 -61
- package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +0 -285
|
@@ -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) : [],
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
// Message selector ("jump back to a previous message"): rewind the
|
|
2
2
|
// conversation to just before a user prompt and hand its text back for
|
|
3
3
|
// editing. Idle-only — a live turn must be interrupted first.
|
|
4
|
+
import { restorableAttachmentsFromPrompt } from '../../../../runtime/attachments/prompt-restore.mjs';
|
|
4
5
|
import { promptHistoryWithout } from '../../prompt-history.mjs';
|
|
6
|
+
import { hydrateTolerantly } from './abort.mjs';
|
|
5
7
|
|
|
6
8
|
export function createRewindAction(bag) {
|
|
7
9
|
const { runtime, getState, set, flushEmitImmediate, replaceItems, syncContextStats } = bag;
|
|
8
10
|
|
|
9
11
|
return {
|
|
10
|
-
|
|
12
|
+
// `restoreFiles: false` is a composer that cannot hold PDF/Office file
|
|
13
|
+
// parts (the terminal one): a prompt carrying any is refused untouched.
|
|
14
|
+
rewindToItem: async (itemId, { restoreFiles = true } = {}) => {
|
|
11
15
|
const target = String(itemId ?? '').trim();
|
|
12
16
|
if (!target) return null;
|
|
13
17
|
if (getState().busy || getState().commandBusy) return null;
|
|
@@ -19,8 +23,31 @@ export function createRewindAction(bag) {
|
|
|
19
23
|
if (!text) return null;
|
|
20
24
|
set({ commandBusy: true });
|
|
21
25
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
// Matched by the row's stable message id (the stored prompt is the
|
|
27
|
+
// expanded text); rows without one fall back to the text.
|
|
28
|
+
const messageId = String(items[index]?.messageId || '');
|
|
29
|
+
const rewindOptions = { text, ...(messageId ? { messageId } : {}) };
|
|
30
|
+
// Resolve the prompt and build the whole restore payload BEFORE any
|
|
31
|
+
// history changes: a refusal or an attachment read error then leaves the
|
|
32
|
+
// model history and the transcript both untouched.
|
|
33
|
+
const preview = await runtime.rewindMessages?.({ ...rewindOptions, dryRun: true });
|
|
34
|
+
if (!preview) return null;
|
|
35
|
+
// The stored prompt's attachment references become the restore payload
|
|
36
|
+
// the abort/queue restores use (pastedTexts / pastedImages / file parts).
|
|
37
|
+
const content = preview.message?.content;
|
|
38
|
+
if (!restoreFiles && Array.isArray(content) && content.some((part) => part?.type === 'file')) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
'this message has PDF/Office attachments the terminal composer cannot restore; edit it in the desktop app'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const refs = restorableAttachmentsFromPrompt(content, text);
|
|
44
|
+
const attachments = hydrateTolerantly(refs.pastedImages, refs.pastedTexts, content);
|
|
45
|
+
// Refused when the history changed since the preview.
|
|
46
|
+
const rewound = await runtime.rewindMessages?.({
|
|
47
|
+
...rewindOptions,
|
|
48
|
+
...(preview.historyMarker ? { expectedHistoryMarker: preview.historyMarker } : {}),
|
|
49
|
+
});
|
|
50
|
+
if (!rewound) throw new Error('the conversation changed; select the message again');
|
|
24
51
|
set({
|
|
25
52
|
items: replaceItems(items.slice(0, index), {
|
|
26
53
|
preserveSpill: true,
|
|
@@ -36,7 +63,15 @@ export function createRewindAction(bag) {
|
|
|
36
63
|
syncContextStats({ allowEstimated: true });
|
|
37
64
|
set({ stats: { ...getState().stats } });
|
|
38
65
|
flushEmitImmediate?.();
|
|
39
|
-
return {
|
|
66
|
+
return {
|
|
67
|
+
text,
|
|
68
|
+
removed: items.length - index,
|
|
69
|
+
messages: rewound.removed,
|
|
70
|
+
...(attachments.pastedImages ? { pastedImages: attachments.pastedImages } : {}),
|
|
71
|
+
...(attachments.pastedTexts ? { pastedTexts: attachments.pastedTexts } : {}),
|
|
72
|
+
...(attachments.content ? { content: attachments.content } : {}),
|
|
73
|
+
...(attachments.notice ? { notice: attachments.notice } : {}),
|
|
74
|
+
};
|
|
40
75
|
} finally {
|
|
41
76
|
set({ commandBusy: false });
|
|
42
77
|
}
|
|
@@ -55,6 +55,7 @@ export function createAgentDefinitionApi(runtime) {
|
|
|
55
55
|
saveLeadAgentBody: async (text) => runtime.saveLeadAgentBody?.(text) ?? '',
|
|
56
56
|
getLeadStatus: () =>
|
|
57
57
|
runtime.getLeadStatus?.() ?? { created: null, name: '', avatar: null, homeChatted: false, route: null },
|
|
58
|
+
getLeadHomeRoute: () => runtime.getLeadHomeRoute?.() ?? null,
|
|
58
59
|
saveLeadIdentity: async (payload) => runtime.saveLeadIdentity?.(payload) ?? null,
|
|
59
60
|
setLeadCreated: async (created, description) => runtime.setLeadCreated?.(created, description) ?? null,
|
|
60
61
|
saveAgentDefinition: async (payload, options) => runtime.saveAgentDefinition?.(payload, options) ?? null,
|
|
@@ -96,17 +96,14 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
96
96
|
// Settle one entry: drop its timeout, then hand the waiter its answer. The
|
|
97
97
|
// resolve stays guarded — a waiter whose caller already went away must never
|
|
98
98
|
// take the rest of the queue down with it.
|
|
99
|
-
function settleToolApproval(entry, approved, reason, answers, reply, extra, device = ''
|
|
99
|
+
function settleToolApproval(entry, approved, reason, answers, reply, extra, device = '') {
|
|
100
100
|
if (entry?.timer) clearTimeout(entry.timer);
|
|
101
101
|
const result = { approved, reason };
|
|
102
102
|
if (answers !== undefined) result.answers = answers;
|
|
103
103
|
if (reply !== undefined) result.reply = reply;
|
|
104
|
-
// A human-kind request answers with its canonical response (never a value)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const answered = forwarded ? entry?.full || entry?.request : entry?.request;
|
|
108
|
-
if (answered?.remember) {
|
|
109
|
-
Object.assign(result, normalizeHumanResponse(answered, approved, extra));
|
|
104
|
+
// A human-kind request answers with its canonical response (never a value).
|
|
105
|
+
if (entry?.request?.remember) {
|
|
106
|
+
Object.assign(result, normalizeHumanResponse(entry.request, approved, extra));
|
|
110
107
|
if (device) result.device = device;
|
|
111
108
|
}
|
|
112
109
|
if (extra?.timedOut === true) result.timedOut = true;
|
|
@@ -120,28 +117,15 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
120
117
|
* owner had already settled it. */
|
|
121
118
|
function forwardToOwner(entry, approved, reason, device, answers, reply, extra) {
|
|
122
119
|
try {
|
|
123
|
-
return entry.mirror.answer(approved, reason, device, answers, reply,
|
|
120
|
+
return entry.mirror.answer(approved, reason, device, answers, reply, extra) === true;
|
|
124
121
|
} catch {
|
|
125
122
|
return false;
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
|
-
/** `request` as this session shows it: a remembering scope is offered only
|
|
129
|
-
* in the one session the request names for it (`rememberAt`, the bot's own
|
|
130
|
-
* chat); everywhere else — and when it names none — it asks once. */
|
|
131
|
-
function viewFor(request) {
|
|
132
|
-
if (!Array.isArray(request?.remember) || request.remember.length <= 1) return request;
|
|
133
|
-
const here = ownSessionId();
|
|
134
|
-
return here && request.rememberAt === here ? request : { ...request, remember: ['once'] };
|
|
135
|
-
}
|
|
136
|
-
/** `extra` without a remember scope `view` did not offer. */
|
|
137
|
-
function onlyOffered(view, extra) {
|
|
138
|
-
if (!extra || extra.remember === undefined) return extra;
|
|
139
|
-
return (view?.remember || ['once']).includes(extra.remember) ? extra : { ...extra, remember: 'once' };
|
|
140
|
-
}
|
|
141
125
|
function offerDelegatedApproval(delegated) {
|
|
142
126
|
if (getDisposed() || delegated.sessionId !== ownSessionId()) return;
|
|
143
127
|
if (activeToolApproval?.id === delegated.id || toolApprovalQueue.some((entry) => entry.id === delegated.id)) return;
|
|
144
|
-
toolApprovalQueue.push({ id: delegated.id, request:
|
|
128
|
+
toolApprovalQueue.push({ id: delegated.id, request: delegated.request, mirror: delegated, timer: null });
|
|
145
129
|
presentNextToolApproval();
|
|
146
130
|
}
|
|
147
131
|
function withdrawDelegatedCopy(id, result) {
|
|
@@ -183,8 +167,7 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
183
167
|
device = '',
|
|
184
168
|
answers = undefined,
|
|
185
169
|
reply = undefined,
|
|
186
|
-
extra = undefined
|
|
187
|
-
{ forwarded = false } = {}
|
|
170
|
+
extra = undefined
|
|
188
171
|
) {
|
|
189
172
|
const targetId = String(id || '');
|
|
190
173
|
if (activeToolApproval && activeToolApproval.id === targetId) {
|
|
@@ -200,16 +183,7 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
200
183
|
if (entry.mirror) {
|
|
201
184
|
accepted = forwardToOwner(entry, approved, reason, device, answers, reply, extra);
|
|
202
185
|
} else {
|
|
203
|
-
settleToolApproval(
|
|
204
|
-
entry,
|
|
205
|
-
approved === true,
|
|
206
|
-
String(reason || ''),
|
|
207
|
-
answers,
|
|
208
|
-
reply,
|
|
209
|
-
extra,
|
|
210
|
-
String(device || ''),
|
|
211
|
-
forwarded
|
|
212
|
-
);
|
|
186
|
+
settleToolApproval(entry, approved === true, String(reason || ''), answers, reply, extra, String(device || ''));
|
|
213
187
|
}
|
|
214
188
|
presentNextToolApproval();
|
|
215
189
|
return accepted;
|
|
@@ -218,16 +192,7 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
218
192
|
if (index >= 0) {
|
|
219
193
|
const [entry] = toolApprovalQueue.splice(index, 1);
|
|
220
194
|
if (entry.mirror) return forwardToOwner(entry, approved, reason, device, answers, reply, extra);
|
|
221
|
-
settleToolApproval(
|
|
222
|
-
entry,
|
|
223
|
-
approved === true,
|
|
224
|
-
String(reason || ''),
|
|
225
|
-
answers,
|
|
226
|
-
reply,
|
|
227
|
-
extra,
|
|
228
|
-
String(device || ''),
|
|
229
|
-
forwarded
|
|
230
|
-
);
|
|
195
|
+
settleToolApproval(entry, approved === true, String(reason || ''), answers, reply, extra, String(device || ''));
|
|
231
196
|
return true;
|
|
232
197
|
}
|
|
233
198
|
return false;
|
|
@@ -265,9 +230,7 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
265
230
|
full.chain = surface.chain;
|
|
266
231
|
full.surfaceSessionId = surface.sessionId;
|
|
267
232
|
}
|
|
268
|
-
|
|
269
|
-
// `full` what the copy in the top-level chat is made from.
|
|
270
|
-
const entry = { id, request: viewFor(full), full, resolve, timer: null, delegated: Boolean(surface) };
|
|
233
|
+
const entry = { id, request: full, resolve, timer: null, delegated: Boolean(surface) };
|
|
271
234
|
toolApprovalQueue.push(entry);
|
|
272
235
|
armToolApprovalTimeout(entry);
|
|
273
236
|
presentNextToolApproval();
|
|
@@ -277,7 +240,7 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
277
240
|
sessionId: surface.sessionId,
|
|
278
241
|
request: full,
|
|
279
242
|
answer: (approved, reason, device, answers, reply, extra) =>
|
|
280
|
-
finishToolApproval(id, approved, reason, device, answers, reply, extra
|
|
243
|
+
finishToolApproval(id, approved, reason, device, answers, reply, extra),
|
|
281
244
|
});
|
|
282
245
|
}
|
|
283
246
|
});
|
|
@@ -5,7 +5,6 @@ import { getModelMetadataSync } from '../../../../runtime/shared/llm/model-catal
|
|
|
5
5
|
import { providerCachedModelsSync } from '../../../../runtime/shared/llm/provider-catalog-cache.mjs';
|
|
6
6
|
|
|
7
7
|
export const CLAUDE_CURRENT_MODE = 'claude-current';
|
|
8
|
-
export const CLAUDE_CURRENT_CHOICE_ID = 'claude-current';
|
|
9
8
|
|
|
10
9
|
const DEFAULT_FAMILY_MODEL = Object.freeze({
|
|
11
10
|
opus: 'claude-opus-4-8',
|
|
@@ -20,7 +19,6 @@ const EFFORT_BUDGET = Object.freeze({
|
|
|
20
19
|
xhigh: 32768,
|
|
21
20
|
max: 32768,
|
|
22
21
|
});
|
|
23
|
-
const SNAPSHOT_REFRESH_MS = 30_000;
|
|
24
22
|
|
|
25
23
|
function cleanString(v) {
|
|
26
24
|
return typeof v === 'string' && v.trim() ? v.trim() : '';
|
|
@@ -33,15 +31,6 @@ export function isMixdogModelSelection(value) {
|
|
|
33
31
|
return cleanString(raw).toLowerCase().startsWith('mixdog/');
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
export function isClaudeNativeModelSelection(route) {
|
|
37
|
-
return !!(
|
|
38
|
-
route &&
|
|
39
|
-
route.provider === 'anthropic-oauth' &&
|
|
40
|
-
cleanString(route.model) &&
|
|
41
|
-
!isMixdogModelSelection(route)
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
34
|
function num(v, fallback = null) {
|
|
46
35
|
if (v === null || v === undefined || v === '') return fallback;
|
|
47
36
|
const n = Number(v);
|
|
@@ -80,23 +69,6 @@ function readFreshJsonFile(file, maxAgeMs) {
|
|
|
80
69
|
}
|
|
81
70
|
}
|
|
82
71
|
|
|
83
|
-
function snapshotEquivalent(a, b) {
|
|
84
|
-
if (!a || !b) return false;
|
|
85
|
-
const keys = [
|
|
86
|
-
'mode',
|
|
87
|
-
'source',
|
|
88
|
-
'provider',
|
|
89
|
-
'model',
|
|
90
|
-
'requestedModel',
|
|
91
|
-
'modelDisplay',
|
|
92
|
-
'effort',
|
|
93
|
-
'fast',
|
|
94
|
-
'thinkingBudgetTokens',
|
|
95
|
-
'contextWindow',
|
|
96
|
-
];
|
|
97
|
-
return keys.every(k => (a[k] ?? null) === (b[k] ?? null));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
72
|
// The newest roster model of a family, by the roster's own release stamps.
|
|
101
73
|
function newestRosterModel(family) {
|
|
102
74
|
const stamp = (row) => Date.parse(row.releaseDate) || (Number(row.created) > 0 ? Number(row.created) : 0);
|
|
@@ -298,33 +270,3 @@ export function readClaudeCodeCurrentRoute({ request = null, maxAgeMs = 10 * 60
|
|
|
298
270
|
if (route.fast === null || route.fast === undefined) route.fast = false;
|
|
299
271
|
return route;
|
|
300
272
|
}
|
|
301
|
-
|
|
302
|
-
export function writeClaudeCodeCurrentSnapshot(ccJsonInput) {
|
|
303
|
-
if (!ccJsonInput) return null;
|
|
304
|
-
let payload = null;
|
|
305
|
-
try { payload = JSON.parse(String(ccJsonInput)); } catch { return null; }
|
|
306
|
-
const route = routeFromPayload(payload, 'statusline');
|
|
307
|
-
if (!route || (!route.model && !route.requestedModel)) return null;
|
|
308
|
-
const file = path.join(pluginDataDir(), 'claude-current-model.json');
|
|
309
|
-
try {
|
|
310
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
311
|
-
const st = fs.existsSync(file) ? fs.statSync(file) : null;
|
|
312
|
-
const current = st ? readJsonFile(file) : null;
|
|
313
|
-
if (
|
|
314
|
-
st &&
|
|
315
|
-
Date.now() - st.mtimeMs < SNAPSHOT_REFRESH_MS &&
|
|
316
|
-
snapshotEquivalent(current, route)
|
|
317
|
-
) {
|
|
318
|
-
return route;
|
|
319
|
-
}
|
|
320
|
-
fs.writeFileSync(file, JSON.stringify(route, null, 2) + '\n');
|
|
321
|
-
} catch { /* best-effort statusline cache */ }
|
|
322
|
-
return route;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export function formatClaudeCurrentChoiceLabel(route = readClaudeCodeCurrentRoute()) {
|
|
326
|
-
const parts = [`Current selection: ${route.modelDisplay || route.model || 'current model'}`];
|
|
327
|
-
if (route.effort) parts.push(String(route.effort).toUpperCase());
|
|
328
|
-
if (route.fast === true) parts.push('fast');
|
|
329
|
-
return parts.join(' · ');
|
|
330
|
-
}
|