@nexrall/code-core 1.4.13 → 1.4.15
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/dist/agent/loop.d.ts +13 -0
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +150 -16
- package/dist/api/client.d.ts +31 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +636 -57
- package/dist/types.d.ts +111 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +44 -0
- package/package.json +1 -1
package/dist/agent/loop.d.ts
CHANGED
|
@@ -102,4 +102,17 @@ export declare function compactMessagesForResume(messages: Message[], opts: {
|
|
|
102
102
|
onNotice?: (text: string) => void;
|
|
103
103
|
}): Promise<boolean>;
|
|
104
104
|
export declare function runAgentLoop(initialMessages: Message[], options: AgentLoopOptions): Promise<Message[]>;
|
|
105
|
+
/**
|
|
106
|
+
* Cut a history back to the last point the API will accept as a resumable
|
|
107
|
+
* prefix, WITHOUT mutating the input.
|
|
108
|
+
*
|
|
109
|
+
* Anthropic rejects a conversation whose final assistant turn contains
|
|
110
|
+
* `tool_use` blocks that never got a matching `tool_result`. The main loop
|
|
111
|
+
* always appends in complete pairs so it can't produce that shape, but a crash
|
|
112
|
+
* in the middle of executing a tool round can: the assistant turn was already
|
|
113
|
+
* pushed, the tool_result carrier never was. Dropping that dangling assistant
|
|
114
|
+
* turn costs one model response and makes the rest of the (often very long)
|
|
115
|
+
* history resumable, instead of throwing all of it away.
|
|
116
|
+
*/
|
|
117
|
+
export declare function trimToResumableBoundary(messages: Message[]): Message[];
|
|
105
118
|
//# sourceMappingURL=loop.d.ts.map
|
package/dist/agent/loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAMP,gBAAgB,EAChB,UAAU,EACX,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAMP,gBAAgB,EAChB,UAAU,EACX,MAAM,UAAU,CAAC;AAyKlB,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAWR;AA+UD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAM7D;AAsBD,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,aAA+G,CAAC;AAC7I,gGAAgG;AAChG,eAAO,MAAM,aAAa,QAA2J,CAAC;AAEtL;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK5E;AAUD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAwBxD;AAoBD,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,aAAa,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,qFAAqF;IACrF,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,YAAY,IAAI,cAAc,CAE7C;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,EAAE,EAAE,OAAO,EACX,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,CAoDN;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA6B5D;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,eAAe,SAAI,GAAG,MAAM,CAgCpF;AAsKD,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAE/D;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,GACA,OAAO,CAAC,OAAO,CAAC,CAqElB;AAID,wBAAsB,YAAY,CAChC,eAAe,EAAE,OAAO,EAAE,EAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,OAAO,EAAE,CAAC,CA+sBpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAqCtE"}
|
package/dist/agent/loop.js
CHANGED
|
@@ -45,6 +45,8 @@ exports.pruneOldToolResults = pruneOldToolResults;
|
|
|
45
45
|
exports.estimateTokensRough = estimateTokensRough;
|
|
46
46
|
exports.compactMessagesForResume = compactMessagesForResume;
|
|
47
47
|
exports.runAgentLoop = runAgentLoop;
|
|
48
|
+
exports.trimToResumableBoundary = trimToResumableBoundary;
|
|
49
|
+
const types_1 = require("../types");
|
|
48
50
|
const client_1 = require("../api/client");
|
|
49
51
|
const executor_1 = require("../tools/executor");
|
|
50
52
|
const agentTypes_1 = require("./agentTypes");
|
|
@@ -366,6 +368,22 @@ async function runSubTask(input, options, agentTypes) {
|
|
|
366
368
|
nexrallMd: subNexrallMd,
|
|
367
369
|
requestPermission: gatedPermission,
|
|
368
370
|
onText: () => { }, // sub-agent text is returned as the tool result, not streamed live
|
|
371
|
+
// Forwarded DELIBERATELY, and it must be a real handler rather than a no-op.
|
|
372
|
+
//
|
|
373
|
+
// A sub-agent streams no text (onText above is a no-op) but it DOES stream thinking
|
|
374
|
+
// through the parent's UI (see onThinking/onThinkingDelta below), and thinking sets
|
|
375
|
+
// `emittedToCaller`. So a sub-agent stream that dies after reasoning genuinely has
|
|
376
|
+
// rendered output to discard — a no-op here would let the restart proceed and then
|
|
377
|
+
// re-stream that reasoning on top of the copy still on screen, the exact duplication
|
|
378
|
+
// the opt-in exists to prevent.
|
|
379
|
+
//
|
|
380
|
+
// Forwarding is safe because the parent's own output is already closed by this
|
|
381
|
+
// point: dispatching the `task` tool goes through options.onToolUse, which finalizes
|
|
382
|
+
// the parent's bubble (VS Code) / flushes the renderer (CLI) before the sub-agent
|
|
383
|
+
// starts. The only live, discardable element at restart time is the sub-agent's own
|
|
384
|
+
// thinking block. Completed tool rows are left alone — those are real side effects
|
|
385
|
+
// that actually happened.
|
|
386
|
+
onStreamRestart: (reason, chars) => options.onStreamRestart?.(reason, chars),
|
|
369
387
|
// Forward tool events with isSubTask=true so the UI can render a badge
|
|
370
388
|
// instead of prepending "[sub-task]" to the tool name (which caused double-prefix
|
|
371
389
|
// when the name was already labelled, and mixed display concerns into the data layer).
|
|
@@ -802,6 +820,11 @@ async function autoCompactMessages(messages, options, ledger) {
|
|
|
802
820
|
env: options.env,
|
|
803
821
|
clientType: options.clientType,
|
|
804
822
|
abortSignal: options.abortSignal,
|
|
823
|
+
// The summariser renders NOTHING (onEvent below is a no-op) and its result is
|
|
824
|
+
// read only from the returned message, so a restart has nothing to roll back —
|
|
825
|
+
// always safe. Worth enabling: a blip here used to abandon compaction entirely,
|
|
826
|
+
// which then let the very next turn hit the context wall it was meant to prevent.
|
|
827
|
+
allowRestartAfterRender: true,
|
|
805
828
|
}, () => { });
|
|
806
829
|
summary = reply.content
|
|
807
830
|
.filter((b) => b.type === 'text')
|
|
@@ -858,6 +881,9 @@ async function maybeCompactMemory(scope, options) {
|
|
|
858
881
|
env: options.env,
|
|
859
882
|
clientType: options.clientType,
|
|
860
883
|
abortSignal: options.abortSignal,
|
|
884
|
+
// Same as the transcript summariser: no rendered output, result read only from
|
|
885
|
+
// the returned message, so restarting on a blip is always safe.
|
|
886
|
+
allowRestartAfterRender: true,
|
|
861
887
|
}, () => { });
|
|
862
888
|
return reply.content
|
|
863
889
|
.filter((b) => b.type === 'text')
|
|
@@ -1010,6 +1036,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1010
1036
|
// iteration backstop, so we can tell the user what happened rather than
|
|
1011
1037
|
// silently returning a half-done task.
|
|
1012
1038
|
let completedCleanly = false;
|
|
1039
|
+
// Tool rounds actually completed in this call. Counted explicitly rather than derived
|
|
1040
|
+
// from `messages.length` because auto-compaction splices the history SHORTER mid-run:
|
|
1041
|
+
// a long session that compacted and then lost its connection would show a negative
|
|
1042
|
+
// length delta and be judged "no progress", discarding the work it most needs to keep.
|
|
1043
|
+
let completedRounds = 0;
|
|
1013
1044
|
let stalledOut = false; // tripped the runaway guard (all-failed rounds)
|
|
1014
1045
|
let consecutiveErrorRounds = 0; // rounds where every tool call errored
|
|
1015
1046
|
let budget = maxIterations; // extended by auto-continue, capped at hardCap
|
|
@@ -1110,8 +1141,6 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1110
1141
|
if (!autoContinue && iteration === budget - 5 && budget > 5) {
|
|
1111
1142
|
options.onText(`\n⚠️ Approaching the ${budget}-step limit (step ${iteration + 1}). Please wrap up and summarise what has been done.\n`);
|
|
1112
1143
|
}
|
|
1113
|
-
// Collect tool_use blocks emitted during this stream
|
|
1114
|
-
const pendingToolUse = [];
|
|
1115
1144
|
// Build SSE event handler
|
|
1116
1145
|
const onEvent = (event) => {
|
|
1117
1146
|
switch (event.type) {
|
|
@@ -1128,21 +1157,33 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1128
1157
|
options.onThinkingProgress?.(event.tokens);
|
|
1129
1158
|
break;
|
|
1130
1159
|
case 'tool_use':
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
input: event.input,
|
|
1138
|
-
});
|
|
1160
|
+
// Nothing to do here. Streamed tool_use events are a PREVIEW only (their
|
|
1161
|
+
// `input` can still be partial JSON mid-stream); the blocks actually
|
|
1162
|
+
// executed are read from the completed message's content at the end of
|
|
1163
|
+
// this iteration. An accumulator here used to exist but was never read —
|
|
1164
|
+
// keeping it invited the assumption that dispatch happens from the stream,
|
|
1165
|
+
// which would break the restart guarantee below.
|
|
1139
1166
|
break;
|
|
1140
1167
|
case 'usage':
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1168
|
+
// A `partial` report belongs to an attempt that was cut short and restarted.
|
|
1169
|
+
// Its token counts are real spend (already billed upstream) and are forwarded
|
|
1170
|
+
// so a cost display stays honest — but they must NOT drive `lastPromptTokens`,
|
|
1171
|
+
// which feeds the auto-compaction trigger: a truncated attempt understates the
|
|
1172
|
+
// prompt size, and letting it overwrite a correct reading would suppress a
|
|
1173
|
+
// compaction the next turn actually needs.
|
|
1174
|
+
if (!event.partial) {
|
|
1175
|
+
// A `replayed` report DOES update this: it describes the real, complete turn
|
|
1176
|
+
// (served from the backend's idempotency cache after a dropped connection),
|
|
1177
|
+
// so its prompt size is accurate and needed for the compaction decision.
|
|
1178
|
+
// Only `partial` — a truncated attempt — must be excluded.
|
|
1179
|
+
lastPromptTokens =
|
|
1180
|
+
(event.usage.input_tokens ?? 0) +
|
|
1181
|
+
(event.usage.cache_creation_input_tokens ?? 0) +
|
|
1182
|
+
(event.usage.cache_read_input_tokens ?? 0);
|
|
1183
|
+
}
|
|
1184
|
+
// Both flags mean "do not add these tokens to a running cost total": `partial`
|
|
1185
|
+
// was a discarded attempt, `replayed` was already counted when first billed.
|
|
1186
|
+
options.onUsage(event.usage, event.partial === true || event.replayed === true);
|
|
1146
1187
|
break;
|
|
1147
1188
|
case 'message_complete':
|
|
1148
1189
|
// The complete message is returned from streamChat, we handle it below
|
|
@@ -1153,11 +1194,29 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1153
1194
|
case 'retry_resolved':
|
|
1154
1195
|
options.onRetryResolved?.();
|
|
1155
1196
|
break;
|
|
1197
|
+
case 'stream_restart':
|
|
1198
|
+
// The attempt that died had already rendered text/thinking; the replacement
|
|
1199
|
+
// attempt re-sends the turn in full, so the caller must drop that partial
|
|
1200
|
+
// render or it shows up twice.
|
|
1201
|
+
//
|
|
1202
|
+
// Nothing else needs undoing, and that is a property of the code below, not
|
|
1203
|
+
// an assumption: tools are dispatched from `assistantMessage.content` AFTER
|
|
1204
|
+
// streamChat() returns, and a restarted attempt threw instead of returning.
|
|
1205
|
+
// So no tool ran, no file changed, no checkpoint or hook fired.
|
|
1206
|
+
options.onStreamRestart?.(event.reason, event.discardedChars);
|
|
1207
|
+
break;
|
|
1156
1208
|
case 'balance_status':
|
|
1157
1209
|
options.onBalanceStatus?.(event.balance, event.zero);
|
|
1158
1210
|
break;
|
|
1159
1211
|
case 'done':
|
|
1212
|
+
break;
|
|
1160
1213
|
case 'error':
|
|
1214
|
+
// Reaching the loop means the stream did NOT reject on this — i.e. the turn's
|
|
1215
|
+
// message had already been delivered and the error arrived late (typically the
|
|
1216
|
+
// backend's post-turn billing write failing). The answer stands, so this must
|
|
1217
|
+
// not fail the turn, but it must not vanish either: route it to the system-
|
|
1218
|
+
// notice channel so it is visible and logged rather than silently swallowed.
|
|
1219
|
+
options.onNotice?.(`⚠️ ${event.message}`);
|
|
1161
1220
|
break;
|
|
1162
1221
|
}
|
|
1163
1222
|
};
|
|
@@ -1176,6 +1235,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1176
1235
|
extraTools: options.mcpManager?.getAnthropicTools(),
|
|
1177
1236
|
agents: agentsCatalogue || undefined,
|
|
1178
1237
|
skills: skillsCatalogue || undefined,
|
|
1238
|
+
// Only allow a post-render restart when the caller actually implements the
|
|
1239
|
+
// rollback. Without a handler the partial output can't be un-rendered, so we
|
|
1240
|
+
// keep the old conservative behaviour (fail the turn) rather than duplicate
|
|
1241
|
+
// text on screen.
|
|
1242
|
+
allowRestartAfterRender: !!options.onStreamRestart,
|
|
1179
1243
|
}, onEvent);
|
|
1180
1244
|
}
|
|
1181
1245
|
catch (err) {
|
|
@@ -1192,7 +1256,14 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1192
1256
|
break;
|
|
1193
1257
|
}
|
|
1194
1258
|
runSimpleHooks(hooks.OnError, options.workDir);
|
|
1195
|
-
|
|
1259
|
+
// Carry the work already done in this turn out with the error. The loop
|
|
1260
|
+
// owns a COPY of the caller's history, so a plain throw would strand every
|
|
1261
|
+
// completed tool round inside this function and the caller would fall back
|
|
1262
|
+
// to a history containing only the original user message — the user pays
|
|
1263
|
+
// for N minutes of agent work and then has to start over because
|
|
1264
|
+
// "continue" remembers none of it. `messages` always ends on a complete
|
|
1265
|
+
// assistant→tool_result pair here, so it is valid to resend as-is.
|
|
1266
|
+
throw new types_1.AgentTurnError(`Stream failed: ${err.message}`, trimToResumableBoundary(messages), completedRounds, err);
|
|
1196
1267
|
}
|
|
1197
1268
|
if (options.abortSignal?.aborted)
|
|
1198
1269
|
break;
|
|
@@ -1524,6 +1595,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1524
1595
|
content: toolResultContent,
|
|
1525
1596
|
};
|
|
1526
1597
|
messages.push(toolResultMessage);
|
|
1598
|
+
// A complete assistant→tool_result pair just landed: the history is valid to
|
|
1599
|
+
// resend from here, so this is exactly one unit of salvageable progress.
|
|
1600
|
+
completedRounds++;
|
|
1527
1601
|
// Incremental persistence hook: history is valid here (ends on a
|
|
1528
1602
|
// tool_result user turn). Let the caller checkpoint progress so a crash
|
|
1529
1603
|
// mid-run loses only the in-flight step, not the whole session.
|
|
@@ -1564,9 +1638,69 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1564
1638
|
}
|
|
1565
1639
|
}
|
|
1566
1640
|
}
|
|
1641
|
+
catch (err) {
|
|
1642
|
+
// Every other failure path (a tool executor blowing up, a hook throwing, an
|
|
1643
|
+
// out-of-memory in a large read) gets the same treatment as a dead stream:
|
|
1644
|
+
// the turn's completed work leaves with the error instead of dying inside
|
|
1645
|
+
// this function's private copy of the history. Already-tagged errors pass
|
|
1646
|
+
// straight through so we don't double-wrap and lose the original message.
|
|
1647
|
+
if (err instanceof types_1.AgentTurnError)
|
|
1648
|
+
throw err;
|
|
1649
|
+
if (options.abortSignal?.aborted || err.name === 'AbortError')
|
|
1650
|
+
throw err;
|
|
1651
|
+
throw new types_1.AgentTurnError(err?.message || 'The turn ended unexpectedly', trimToResumableBoundary(messages), completedRounds, err);
|
|
1652
|
+
}
|
|
1567
1653
|
finally {
|
|
1568
1654
|
runSimpleHooks(hooks.OnStop, options.workDir);
|
|
1569
1655
|
}
|
|
1570
1656
|
return messages;
|
|
1571
1657
|
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Cut a history back to the last point the API will accept as a resumable
|
|
1660
|
+
* prefix, WITHOUT mutating the input.
|
|
1661
|
+
*
|
|
1662
|
+
* Anthropic rejects a conversation whose final assistant turn contains
|
|
1663
|
+
* `tool_use` blocks that never got a matching `tool_result`. The main loop
|
|
1664
|
+
* always appends in complete pairs so it can't produce that shape, but a crash
|
|
1665
|
+
* in the middle of executing a tool round can: the assistant turn was already
|
|
1666
|
+
* pushed, the tool_result carrier never was. Dropping that dangling assistant
|
|
1667
|
+
* turn costs one model response and makes the rest of the (often very long)
|
|
1668
|
+
* history resumable, instead of throwing all of it away.
|
|
1669
|
+
*/
|
|
1670
|
+
function trimToResumableBoundary(messages) {
|
|
1671
|
+
// A trailing assistant turn is unresumable if it invoked ANY tool whose result is
|
|
1672
|
+
// missing. That includes server-side invocations (`server_tool_use`, produced by
|
|
1673
|
+
// web search / tool search), which carry a DIFFERENT block type than a client tool
|
|
1674
|
+
// call: matching only 'tool_use' would let a dangling web-search turn through, and
|
|
1675
|
+
// it would 400 on resend exactly like a dangling client call. `chooseFinalContent`
|
|
1676
|
+
// in api/client.ts deliberately preserves these blocks verbatim, so they really do
|
|
1677
|
+
// reach this function.
|
|
1678
|
+
const isUnansweredInvocation = (m) => {
|
|
1679
|
+
if (m?.role !== 'assistant' || !Array.isArray(m.content))
|
|
1680
|
+
return false;
|
|
1681
|
+
const blocks = m.content;
|
|
1682
|
+
// A client tool_use is answered by a tool_result in the NEXT (user) message, which
|
|
1683
|
+
// by definition doesn't exist if this is the last turn — so any of them is dangling.
|
|
1684
|
+
if (blocks.some((b) => b?.type === 'tool_use'))
|
|
1685
|
+
return true;
|
|
1686
|
+
// Server-side invocations are different: Anthropic pairs each `server_tool_use` with
|
|
1687
|
+
// its `*_tool_result` inside this SAME message, so a completed search turn is
|
|
1688
|
+
// perfectly resumable and must not be discarded.
|
|
1689
|
+
//
|
|
1690
|
+
// Pair by id rather than asking "is there any result block at all". A turn cut off
|
|
1691
|
+
// after two searches where only the first returned would otherwise look complete and
|
|
1692
|
+
// 400 on resend — exactly the mid-turn-death case this helper exists for. Matching on
|
|
1693
|
+
// the `_tool_result` suffix also covers result types beyond web/tool search
|
|
1694
|
+
// (code_execution, mcp) without needing to enumerate them.
|
|
1695
|
+
const answeredIds = new Set(blocks.filter((b) => /_tool_result$/.test(b?.type ?? '')).map((b) => b?.tool_use_id));
|
|
1696
|
+
return blocks.some((b) => b?.type === 'server_tool_use' && !answeredIds.has(b?.id));
|
|
1697
|
+
};
|
|
1698
|
+
// Loop rather than a single check: this is an exported helper that accepts any
|
|
1699
|
+
// history, not only the strictly-paired ones the agent loop builds, so consecutive
|
|
1700
|
+
// dangling assistant turns must all be removed for the result to be resendable.
|
|
1701
|
+
let end = messages.length;
|
|
1702
|
+
while (end > 0 && isUnansweredInvocation(messages[end - 1]))
|
|
1703
|
+
end--;
|
|
1704
|
+
return end === messages.length ? messages : messages.slice(0, end);
|
|
1705
|
+
}
|
|
1572
1706
|
//# sourceMappingURL=loop.js.map
|
package/dist/api/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Message, SSEEvent, AuthConfig, EnvContext, EditorContext } from '../types';
|
|
2
|
-
export declare const API_BASE
|
|
2
|
+
export declare const API_BASE: string;
|
|
3
3
|
/**
|
|
4
4
|
* Decide the final assistant `content` array to store in history for a completed
|
|
5
5
|
* turn. Pure + exported so it can be unit-tested without a live SSE stream.
|
|
@@ -44,8 +44,38 @@ export interface StreamChatOptions {
|
|
|
44
44
|
agents?: string;
|
|
45
45
|
/** Catalogue of auto-invokable skills (description only) — injected into the system prompt so the model can call use_skill when relevant. */
|
|
46
46
|
skills?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Opt in to restarting a turn that died AFTER partial output was rendered.
|
|
49
|
+
*
|
|
50
|
+
* Historically any transport failure past the first token was fatal: retrying
|
|
51
|
+
* would have re-streamed text the user could already see, duplicating it. That
|
|
52
|
+
* rule is correct but it protects almost nothing in practice — a long turn emits
|
|
53
|
+
* a thinking delta within seconds, so the retry machinery was effectively dead
|
|
54
|
+
* code exactly when it mattered most (deep into an expensive agentic run).
|
|
55
|
+
*
|
|
56
|
+
* When the caller can UNDO its rendered output for the current turn, it sets
|
|
57
|
+
* this and handles the `stream_restart` event by discarding that output. Then a
|
|
58
|
+
* mid-render disconnect becomes recoverable instead of turn-fatal. This is safe
|
|
59
|
+
* for side effects: tool calls are dispatched by the agent loop only after
|
|
60
|
+
* `message_complete`, and a restart by definition means that never arrived — so
|
|
61
|
+
* no tool ran and nothing but rendered characters is rolled back.
|
|
62
|
+
*/
|
|
63
|
+
allowRestartAfterRender?: boolean;
|
|
47
64
|
}
|
|
48
65
|
export declare function streamChat(messages: Message[], options: StreamChatOptions, onEvent: (e: SSEEvent) => void): Promise<Message>;
|
|
66
|
+
/**
|
|
67
|
+
* Ask the server to stop generating a turn.
|
|
68
|
+
*
|
|
69
|
+
* This is REQUIRED for Stop to work, not an optimisation. A dropped socket used to abort
|
|
70
|
+
* the model; now it detaches and keeps generating so a network blip doesn't discard the
|
|
71
|
+
* turn (see the resume machinery above). Since a deliberate Stop looks identical to a
|
|
72
|
+
* blip from the server's side, it has to be signalled explicitly — otherwise the model
|
|
73
|
+
* would run to completion and bill in full after the user pressed Stop.
|
|
74
|
+
*
|
|
75
|
+
* Best-effort and never throws: Stop must feel instant, and the server's detach grace
|
|
76
|
+
* bounds the cost if this request never lands.
|
|
77
|
+
*/
|
|
78
|
+
export declare function cancelTurn(turnId: string): Promise<void>;
|
|
49
79
|
export declare function getBalance(): Promise<number>;
|
|
50
80
|
export declare function exchangeVscodeCode(code: string): Promise<AuthConfig>;
|
|
51
81
|
export declare function login(email: string, password: string): Promise<AuthConfig>;
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAA8B,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAiErH,eAAO,MAAM,QAAQ,QAAmB,CAAC;AAIzC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5D,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACtD,CAAC,EAAE,CAeL;AAeD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACnC,oFAAoF;IACpF,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACjG,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6IAA6I;IAC7I,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;;OAeG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAiDD,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CA05BlB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9D;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAalD;AAID,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgB1E;AAID,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkBhF"}
|