@ai-sdk/harness-claude-code 1.0.170 → 1.0.172
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/CHANGELOG.md +22 -0
- package/dist/bridge/index.mjs +280 -77
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/bridge/pnpm-lockfile.yaml +1036 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/bridge/create-emit-stream-event.ts +6 -0
- package/src/bridge/index.ts +313 -21
- package/src/bridge/json-schema-to-zod.ts +32 -2
- package/src/claude-code-bootstrap.ts +3 -1
- package/src/claude-code-bridge-protocol.ts +13 -0
- package/src/claude-code-harness.ts +40 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @ai-sdk/harness-claude-code
|
|
2
2
|
|
|
3
|
+
## 1.0.172
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4ab93bb: feat(harness-claude-code): add `toolHints` to mark host tools read-only or always-loaded
|
|
8
|
+
- ede06e3: carry out an approval answered after a resume instead of asking again
|
|
9
|
+
- 0808bf4: fix(harness-claude-code): preserve host tool numeric bounds, string lengths, array item counts, and defaults in Zod schemas
|
|
10
|
+
- Updated dependencies [033f8d9]
|
|
11
|
+
- Updated dependencies [e7a8988]
|
|
12
|
+
- Updated dependencies [2c12e25]
|
|
13
|
+
- Updated dependencies [61a315e]
|
|
14
|
+
- Updated dependencies [6eab0f7]
|
|
15
|
+
- @ai-sdk/harness@1.0.168
|
|
16
|
+
- @ai-sdk/provider-utils@5.0.71
|
|
17
|
+
|
|
18
|
+
## 1.0.171
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- 726a1cc: fix(harness): copy bridge lockfile as `pnpm-lockfile.yaml` in `dist/` so `pnpm pack` includes it in published tarballs
|
|
23
|
+
- @ai-sdk/harness@1.0.167
|
|
24
|
+
|
|
3
25
|
## 1.0.170
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -1224,7 +1224,8 @@ function defaultUsage() {
|
|
|
1224
1224
|
//#endregion
|
|
1225
1225
|
//#region src/bridge/json-schema-to-zod.ts
|
|
1226
1226
|
function jsonSchemaToZodObject(input) {
|
|
1227
|
-
|
|
1227
|
+
const schema = isJsonSchemaObject(input) ? input : {};
|
|
1228
|
+
return withJsonSchemaMetadata(toZodObject(schema), schema);
|
|
1228
1229
|
}
|
|
1229
1230
|
function toZodObject(schema) {
|
|
1230
1231
|
const object = z.object(toZodShape(schema));
|
|
@@ -1248,7 +1249,22 @@ function toZodType(schema) {
|
|
|
1248
1249
|
let zType = zodForConst(schema) ?? zodForEnum(schema) ?? zodForUnion(schema) ?? zodForType(schema);
|
|
1249
1250
|
if (isNullable(schema)) zType = zType.nullable();
|
|
1250
1251
|
if (schema.description) zType = zType.describe(schema.description);
|
|
1251
|
-
return zType;
|
|
1252
|
+
return withJsonSchemaMetadata(zType, schema);
|
|
1253
|
+
}
|
|
1254
|
+
function withJsonSchemaMetadata(zType, schema) {
|
|
1255
|
+
const metadata = {};
|
|
1256
|
+
for (const key of [
|
|
1257
|
+
"minimum",
|
|
1258
|
+
"maximum",
|
|
1259
|
+
"exclusiveMinimum",
|
|
1260
|
+
"exclusiveMaximum",
|
|
1261
|
+
"minLength",
|
|
1262
|
+
"maxLength",
|
|
1263
|
+
"minItems",
|
|
1264
|
+
"maxItems",
|
|
1265
|
+
"default"
|
|
1266
|
+
]) if (schema[key] !== void 0) metadata[key] = schema[key];
|
|
1267
|
+
return zType.meta(metadata);
|
|
1252
1268
|
}
|
|
1253
1269
|
function zodForType(schema) {
|
|
1254
1270
|
const types = getNonNullTypes(schema);
|
|
@@ -1487,18 +1503,23 @@ function createPermissionOptions(input) {
|
|
|
1487
1503
|
return {
|
|
1488
1504
|
...baseOptions,
|
|
1489
1505
|
canUseTool: async (toolName, toolInput, options) => {
|
|
1490
|
-
if (
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
if (!inactiveNativeTools.has(toolName) && !nativeToolRequiresApproval({
|
|
1495
|
-
nativeName: toolName,
|
|
1496
|
-
permissionMode
|
|
1506
|
+
if (!needsHostApproval({
|
|
1507
|
+
toolName,
|
|
1508
|
+
permissionMode,
|
|
1509
|
+
inactiveNativeTools
|
|
1497
1510
|
})) return {
|
|
1498
1511
|
behavior: "allow",
|
|
1499
1512
|
updatedInput: toolInput
|
|
1500
1513
|
};
|
|
1501
1514
|
const approvalId = options.toolUseID;
|
|
1515
|
+
if (input.approvalDecisions.get(approvalId)?.approved === true) {
|
|
1516
|
+
input.approvalDecisions.delete(approvalId);
|
|
1517
|
+
return {
|
|
1518
|
+
behavior: "allow",
|
|
1519
|
+
updatedInput: toolInput,
|
|
1520
|
+
toolUseID: approvalId
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1502
1523
|
input.approvalRequestedToolUseIds.add(approvalId);
|
|
1503
1524
|
input.nativeToolCallNames.set(approvalId, toolName);
|
|
1504
1525
|
if (!input.emittedToolCallIds.has(approvalId)) {
|
|
@@ -1582,6 +1603,41 @@ function createPermissionSettings(input) {
|
|
|
1582
1603
|
sandbox: { autoAllowBashIfSandboxed: false }
|
|
1583
1604
|
};
|
|
1584
1605
|
}
|
|
1606
|
+
function needsHostApproval(input) {
|
|
1607
|
+
if (input.toolName.startsWith("mcp__harness-tools__")) return false;
|
|
1608
|
+
return input.inactiveNativeTools.has(input.toolName) || nativeToolRequiresApproval({
|
|
1609
|
+
nativeName: input.toolName,
|
|
1610
|
+
permissionMode: input.permissionMode
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
function createApprovalPreToolUseHook(input) {
|
|
1614
|
+
return async (hookInput, toolUseID) => {
|
|
1615
|
+
const toolName = hookInput.tool_name;
|
|
1616
|
+
if (toolUseID == null || typeof toolName !== "string" || toolName === "AskUserQuestion") return {};
|
|
1617
|
+
const decision = input.decisions.get(toolUseID);
|
|
1618
|
+
if (decision != null) {
|
|
1619
|
+
input.nativeToolCallNames.set(toolUseID, toolName);
|
|
1620
|
+
if (!decision.approved) input.decisions.delete(toolUseID);
|
|
1621
|
+
return { hookSpecificOutput: {
|
|
1622
|
+
hookEventName: "PreToolUse",
|
|
1623
|
+
...decision.approved ? { permissionDecision: "allow" } : {
|
|
1624
|
+
permissionDecision: "deny",
|
|
1625
|
+
permissionDecisionReason: decision.reason ?? "Denied"
|
|
1626
|
+
}
|
|
1627
|
+
} };
|
|
1628
|
+
}
|
|
1629
|
+
if (!needsHostApproval({
|
|
1630
|
+
toolName,
|
|
1631
|
+
permissionMode: input.permissionMode,
|
|
1632
|
+
inactiveNativeTools: input.inactiveNativeTools
|
|
1633
|
+
})) return {};
|
|
1634
|
+
input.deferredToolUseIds.add(toolUseID);
|
|
1635
|
+
return { hookSpecificOutput: {
|
|
1636
|
+
hookEventName: "PreToolUse",
|
|
1637
|
+
permissionDecision: "defer"
|
|
1638
|
+
} };
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1585
1641
|
function nativeToolRequiresApproval(input) {
|
|
1586
1642
|
if (input.permissionMode === "allow-all") return false;
|
|
1587
1643
|
const kind = NATIVE_TOOL_KINDS[input.nativeName] ?? "edit";
|
|
@@ -1606,70 +1662,75 @@ async function runTurn(start, turn) {
|
|
|
1606
1662
|
name: "harness-tools",
|
|
1607
1663
|
version: "1.0.0"
|
|
1608
1664
|
});
|
|
1609
|
-
for (const tool of start.tools)
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
streamEventState.emittedToolCallIds.add(toolCallId);
|
|
1627
|
-
emit({
|
|
1628
|
-
type: "tool-call",
|
|
1629
|
-
toolCallId,
|
|
1665
|
+
for (const tool of start.tools) {
|
|
1666
|
+
const hints = start.toolHints?.[tool.name];
|
|
1667
|
+
server.registerTool(tool.name, {
|
|
1668
|
+
description: tool.description ?? "",
|
|
1669
|
+
inputSchema: jsonSchemaToZodObject(tool.inputSchema),
|
|
1670
|
+
...hints?.readOnly == null ? {} : { annotations: { readOnlyHint: hints.readOnly } },
|
|
1671
|
+
...hints?.alwaysLoad == null ? {} : { _meta: { "anthropic/alwaysLoad": hints.alwaysLoad } }
|
|
1672
|
+
}, async (...handlerArgs) => {
|
|
1673
|
+
const [input, extra] = handlerArgs;
|
|
1674
|
+
const metadataToolCallId = extra._meta?.["claudecode/toolUseId"];
|
|
1675
|
+
const toolCallId = resolveMcpHandlerToolUseId({
|
|
1676
|
+
state: streamEventState,
|
|
1677
|
+
metadataToolUseId: typeof metadataToolCallId === "string" ? metadataToolCallId : void 0,
|
|
1678
|
+
toolName: tool.name,
|
|
1679
|
+
input
|
|
1680
|
+
}) ?? randomUUID();
|
|
1681
|
+
streamEventState.mcpHandlerCalls.set(toolCallId, {
|
|
1630
1682
|
toolName: tool.name,
|
|
1631
|
-
input
|
|
1632
|
-
providerExecuted: false
|
|
1683
|
+
input
|
|
1633
1684
|
});
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
emit
|
|
1637
|
-
|
|
1685
|
+
if (!streamEventState.emittedToolCallIds.has(toolCallId)) {
|
|
1686
|
+
streamEventState.emittedToolCallIds.add(toolCallId);
|
|
1687
|
+
emit({
|
|
1688
|
+
type: "tool-call",
|
|
1689
|
+
toolCallId,
|
|
1690
|
+
toolName: tool.name,
|
|
1691
|
+
input: JSON.stringify(input),
|
|
1692
|
+
providerExecuted: false
|
|
1693
|
+
});
|
|
1694
|
+
markWaitingOnHostInput({
|
|
1695
|
+
state: streamEventState,
|
|
1696
|
+
emit,
|
|
1697
|
+
toolUseId: toolCallId
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
const { output, isError, toolResult } = await turn.requestToolResult(toolCallId).finally(() => {
|
|
1701
|
+
clearWaitingOnHostInput({
|
|
1702
|
+
state: streamEventState,
|
|
1703
|
+
toolUseId: toolCallId
|
|
1704
|
+
});
|
|
1705
|
+
if (!streamEventState.mcpToolCalls.has(toolCallId)) streamEventState.pendingStepToolUseIds.delete(toolCallId);
|
|
1638
1706
|
});
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1707
|
+
emit({
|
|
1708
|
+
type: "tool-result",
|
|
1709
|
+
toolCallId,
|
|
1710
|
+
toolName: tool.name,
|
|
1711
|
+
result: output ?? null,
|
|
1712
|
+
isError: !!isError
|
|
1644
1713
|
});
|
|
1645
|
-
if (
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1714
|
+
if (toolResult != null) {
|
|
1715
|
+
const converted = convertHarnessToolModelOutput({ output: toolResult.output });
|
|
1716
|
+
return {
|
|
1717
|
+
content: converted.content.map((part) => part.type === "text" ? part : {
|
|
1718
|
+
type: "image",
|
|
1719
|
+
data: part.data,
|
|
1720
|
+
mimeType: part.mediaType
|
|
1721
|
+
}),
|
|
1722
|
+
isError: isError === true || converted.isError
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1656
1725
|
return {
|
|
1657
|
-
content:
|
|
1658
|
-
type: "
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
isError: isError === true || converted.isError
|
|
1726
|
+
content: [{
|
|
1727
|
+
type: "text",
|
|
1728
|
+
text: JSON.stringify(output ?? null)
|
|
1729
|
+
}],
|
|
1730
|
+
isError
|
|
1663
1731
|
};
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
content: [{
|
|
1667
|
-
type: "text",
|
|
1668
|
-
text: JSON.stringify(output ?? null)
|
|
1669
|
-
}],
|
|
1670
|
-
isError
|
|
1671
|
-
};
|
|
1672
|
-
});
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1673
1734
|
mcpServers["harness-tools"] = {
|
|
1674
1735
|
type: "sdk",
|
|
1675
1736
|
name: "harness-tools",
|
|
@@ -1693,7 +1754,10 @@ async function runTurn(start, turn) {
|
|
|
1693
1754
|
const skillsOption = toClaudeSkillsOption(start.skills);
|
|
1694
1755
|
const nativeTools = resolveNativeTools(start.builtinToolFiltering);
|
|
1695
1756
|
const inactiveNativeTools = resolveInactiveNativeTools(start.builtinToolFiltering);
|
|
1757
|
+
const permissionMode = start.permissionMode ?? "allow-all";
|
|
1758
|
+
const approvalDecisions = /* @__PURE__ */ new Map();
|
|
1696
1759
|
const permissionOptions = createPermissionOptions({
|
|
1760
|
+
approvalDecisions,
|
|
1697
1761
|
start,
|
|
1698
1762
|
inactiveNativeTools,
|
|
1699
1763
|
turn,
|
|
@@ -1711,6 +1775,14 @@ async function runTurn(start, turn) {
|
|
|
1711
1775
|
approvalRequestedToolUseIds: streamEventState.approvalRequestedToolUseIds,
|
|
1712
1776
|
emittedToolCallIds: streamEventState.emittedToolCallIds
|
|
1713
1777
|
});
|
|
1778
|
+
const deferredToolUseIds = /* @__PURE__ */ new Set();
|
|
1779
|
+
const approvalPreToolUseHook = createApprovalPreToolUseHook({
|
|
1780
|
+
permissionMode,
|
|
1781
|
+
inactiveNativeTools: new Set(inactiveNativeTools),
|
|
1782
|
+
decisions: approvalDecisions,
|
|
1783
|
+
deferredToolUseIds,
|
|
1784
|
+
nativeToolCallNames: streamEventState.nativeToolCallNames
|
|
1785
|
+
});
|
|
1714
1786
|
const questionPreToolUseHook = createQuestionPreToolUseHook({
|
|
1715
1787
|
turn,
|
|
1716
1788
|
emit,
|
|
@@ -1725,7 +1797,7 @@ async function runTurn(start, turn) {
|
|
|
1725
1797
|
toolUseId
|
|
1726
1798
|
})
|
|
1727
1799
|
});
|
|
1728
|
-
const
|
|
1800
|
+
const queryArgs = {
|
|
1729
1801
|
prompt: queryInput.input,
|
|
1730
1802
|
options: {
|
|
1731
1803
|
...start.model ? { model: start.model } : {},
|
|
@@ -1750,7 +1822,7 @@ async function runTurn(start, turn) {
|
|
|
1750
1822
|
} } : {},
|
|
1751
1823
|
includePartialMessages: true,
|
|
1752
1824
|
hooks: {
|
|
1753
|
-
PreToolUse: [{
|
|
1825
|
+
PreToolUse: [...permissionMode === "allow-all" ? [] : [{ hooks: [approvalPreToolUseHook] }], {
|
|
1754
1826
|
matcher: "AskUserQuestion",
|
|
1755
1827
|
hooks: [questionPreToolUseHook],
|
|
1756
1828
|
...start.questionTimeoutMs !== void 0 ? { timeout: Math.ceil(start.questionTimeoutMs / 1e3) } : {}
|
|
@@ -1766,14 +1838,62 @@ async function runTurn(start, turn) {
|
|
|
1766
1838
|
cwd: workdir,
|
|
1767
1839
|
abortSignal: abortCtl.signal
|
|
1768
1840
|
}
|
|
1841
|
+
};
|
|
1842
|
+
let deferredToolUseId = start.deferredToolUseId;
|
|
1843
|
+
let resumingDeferredCall = false;
|
|
1844
|
+
let q = deferredToolUseId == null ? claudeSdk.query(queryArgs) : void 0;
|
|
1845
|
+
const hostAborted = new Promise((resolve) => {
|
|
1846
|
+
if (abortCtl.signal.aborted) resolve(void 0);
|
|
1847
|
+
abortCtl.signal.addEventListener("abort", () => resolve(void 0), { once: true });
|
|
1769
1848
|
});
|
|
1770
1849
|
gracefulAbort = () => {
|
|
1771
1850
|
hardAbortTimer = setTimeout(() => abortCtl.abort(), 5e3);
|
|
1772
1851
|
hardAbortTimer.unref?.();
|
|
1773
|
-
Promise.resolve().then(() => q
|
|
1852
|
+
Promise.resolve().then(() => q?.interrupt()).catch(() => abortCtl.abort());
|
|
1774
1853
|
};
|
|
1775
1854
|
let turnUsage;
|
|
1776
1855
|
let totalCostUsd;
|
|
1856
|
+
let deferredDecision;
|
|
1857
|
+
const requestDeferredApproval = (call) => {
|
|
1858
|
+
for (const droppedToolUseId of deferredToolUseIds) {
|
|
1859
|
+
if (droppedToolUseId === call.toolUseId) continue;
|
|
1860
|
+
const nativeName = streamEventState.nativeToolCallNames.get(droppedToolUseId) ?? "unknown";
|
|
1861
|
+
emit({
|
|
1862
|
+
type: "tool-result",
|
|
1863
|
+
toolCallId: droppedToolUseId,
|
|
1864
|
+
toolName: toCommonName(nativeName),
|
|
1865
|
+
result: "Claude Code can wait for only one approval at a time, so this call did not run.",
|
|
1866
|
+
isError: true
|
|
1867
|
+
});
|
|
1868
|
+
streamEventState.pendingStepToolUseIds.delete(droppedToolUseId);
|
|
1869
|
+
}
|
|
1870
|
+
deferredToolUseIds.clear();
|
|
1871
|
+
streamEventState.approvalRequestedToolUseIds.add(call.toolUseId);
|
|
1872
|
+
streamEventState.nativeToolCallNames.set(call.toolUseId, call.toolName);
|
|
1873
|
+
if (!streamEventState.emittedToolCallIds.has(call.toolUseId)) {
|
|
1874
|
+
streamEventState.emittedToolCallIds.add(call.toolUseId);
|
|
1875
|
+
emit({
|
|
1876
|
+
type: "tool-call",
|
|
1877
|
+
toolCallId: call.toolUseId,
|
|
1878
|
+
toolName: toCommonName(call.toolName),
|
|
1879
|
+
nativeName: call.toolName,
|
|
1880
|
+
input: JSON.stringify(call.toolInput ?? {}),
|
|
1881
|
+
providerExecuted: true,
|
|
1882
|
+
...isExternalMcpTool(call.toolName) ? { dynamic: true } : {}
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
deferredDecision = turn.requestToolApproval(call.toolUseId);
|
|
1886
|
+
emit({
|
|
1887
|
+
type: "tool-approval-request",
|
|
1888
|
+
approvalId: call.toolUseId,
|
|
1889
|
+
toolCallId: call.toolUseId
|
|
1890
|
+
});
|
|
1891
|
+
markWaitingOnHostInput({
|
|
1892
|
+
state: streamEventState,
|
|
1893
|
+
emit,
|
|
1894
|
+
toolUseId: call.toolUseId
|
|
1895
|
+
});
|
|
1896
|
+
};
|
|
1777
1897
|
let emittedTerminalError = false;
|
|
1778
1898
|
let emittedTerminalFinish = false;
|
|
1779
1899
|
const emitTerminalError = (message) => {
|
|
@@ -1796,14 +1916,59 @@ async function runTurn(start, turn) {
|
|
|
1796
1916
|
onCompactionBoundary: (boundary) => compaction.onBoundary(boundary),
|
|
1797
1917
|
toCommonName
|
|
1798
1918
|
});
|
|
1919
|
+
async function* messages() {
|
|
1920
|
+
for (;;) {
|
|
1921
|
+
if (deferredToolUseId != null) {
|
|
1922
|
+
const toolUseId = deferredToolUseId;
|
|
1923
|
+
const decision = await Promise.race([deferredDecision ?? turn.requestToolApproval(toolUseId), hostAborted]).finally(() => clearWaitingOnHostInput({
|
|
1924
|
+
state: streamEventState,
|
|
1925
|
+
toolUseId
|
|
1926
|
+
}));
|
|
1927
|
+
deferredDecision = void 0;
|
|
1928
|
+
if (decision == null || abortCtl.signal.aborted) return;
|
|
1929
|
+
approvalDecisions.set(toolUseId, decision);
|
|
1930
|
+
deferredToolUseId = void 0;
|
|
1931
|
+
resumingDeferredCall = true;
|
|
1932
|
+
q = claudeSdk.query({
|
|
1933
|
+
prompt: withoutInitialMessage(queryInput.input),
|
|
1934
|
+
options: {
|
|
1935
|
+
...queryArgs.options,
|
|
1936
|
+
...lastClaudeSessionId != null ? {
|
|
1937
|
+
continue: void 0,
|
|
1938
|
+
resume: lastClaudeSessionId
|
|
1939
|
+
} : {}
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
for await (const msg of q) {
|
|
1944
|
+
yield msg;
|
|
1945
|
+
if (deferredToolUseId != null) break;
|
|
1946
|
+
}
|
|
1947
|
+
if (deferredToolUseId == null || abortCtl.signal.aborted) return;
|
|
1948
|
+
queryInput.retire();
|
|
1949
|
+
await disposeQuery(q);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1799
1952
|
try {
|
|
1800
|
-
for await (const msg of
|
|
1953
|
+
for await (const msg of messages()) {
|
|
1801
1954
|
if (abortCtl.signal.aborted) break;
|
|
1802
1955
|
const type = msg.type;
|
|
1803
1956
|
if (type === "command_lifecycle") queryInput.handleLifecycle(msg);
|
|
1804
1957
|
const sessionId = msg.session_id;
|
|
1805
1958
|
if (typeof sessionId === "string" && sessionId.length > 0) lastClaudeSessionId = sessionId;
|
|
1806
1959
|
emitStreamEvent(msg);
|
|
1960
|
+
if (type === "result" && msg.terminal_reason === "tool_deferred" && msg.deferred_tool_use?.id != null) {
|
|
1961
|
+
const harnessUsage = mapUsage(msg.usage ?? msg.message?.usage);
|
|
1962
|
+
if (harnessUsage) turnUsage = addUsage(turnUsage, harnessUsage);
|
|
1963
|
+
if (typeof msg.total_cost_usd === "number") totalCostUsd = msg.total_cost_usd;
|
|
1964
|
+
deferredToolUseId = msg.deferred_tool_use.id;
|
|
1965
|
+
requestDeferredApproval({
|
|
1966
|
+
toolUseId: msg.deferred_tool_use.id,
|
|
1967
|
+
toolName: msg.deferred_tool_use.name,
|
|
1968
|
+
toolInput: msg.deferred_tool_use.input
|
|
1969
|
+
});
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1807
1972
|
if (type === "result") {
|
|
1808
1973
|
if (msg.subtype === "success") {
|
|
1809
1974
|
if (msg.is_error) {
|
|
@@ -1839,7 +2004,7 @@ async function runTurn(start, turn) {
|
|
|
1839
2004
|
emit,
|
|
1840
2005
|
usage: streamEventState.pendingStepUsage ?? harnessUsage
|
|
1841
2006
|
});
|
|
1842
|
-
if (!queryInput.answersSentMessage(msg)) continue;
|
|
2007
|
+
if (!resumingDeferredCall && !queryInput.answersSentMessage(msg)) continue;
|
|
1843
2008
|
queryInput.observeResult();
|
|
1844
2009
|
if (!queryInput.hasActiveUserMessages()) {
|
|
1845
2010
|
queryInput.close();
|
|
@@ -1864,9 +2029,7 @@ async function runTurn(start, turn) {
|
|
|
1864
2029
|
if (hardAbortTimer != null) clearTimeout(hardAbortTimer);
|
|
1865
2030
|
turn.abortSignal.removeEventListener("abort", onHostAbort);
|
|
1866
2031
|
queryInput.close();
|
|
1867
|
-
|
|
1868
|
-
await q.return?.(void 0);
|
|
1869
|
-
} catch {}
|
|
2032
|
+
await disposeQuery(q);
|
|
1870
2033
|
}
|
|
1871
2034
|
if (emittedTerminalError) return;
|
|
1872
2035
|
emittedTerminalFinish = true;
|
|
@@ -1923,6 +2086,24 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1923
2086
|
...options.priority == null ? {} : { priority: options.priority }
|
|
1924
2087
|
});
|
|
1925
2088
|
const messageIterator = userMessages[Symbol.asyncIterator]();
|
|
2089
|
+
let liveInputId = 0;
|
|
2090
|
+
let bufferedMessage;
|
|
2091
|
+
let pulling;
|
|
2092
|
+
const takeMessage = async (inputId) => {
|
|
2093
|
+
for (;;) {
|
|
2094
|
+
if (inputId !== liveInputId) return void 0;
|
|
2095
|
+
if (bufferedMessage != null) {
|
|
2096
|
+
const message = bufferedMessage;
|
|
2097
|
+
bufferedMessage = void 0;
|
|
2098
|
+
return message;
|
|
2099
|
+
}
|
|
2100
|
+
pulling ??= messageIterator.next().then((result) => {
|
|
2101
|
+
bufferedMessage = result;
|
|
2102
|
+
pulling = void 0;
|
|
2103
|
+
});
|
|
2104
|
+
await pulling;
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
1926
2107
|
return {
|
|
1927
2108
|
close,
|
|
1928
2109
|
handleLifecycle: (message) => {
|
|
@@ -1947,10 +2128,14 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1947
2128
|
observeResult: () => {
|
|
1948
2129
|
observedResult = true;
|
|
1949
2130
|
},
|
|
2131
|
+
retire: () => {
|
|
2132
|
+
liveInputId++;
|
|
2133
|
+
},
|
|
1950
2134
|
get hasObservedResult() {
|
|
1951
2135
|
return observedResult;
|
|
1952
2136
|
},
|
|
1953
2137
|
input: { [Symbol.asyncIterator]() {
|
|
2138
|
+
const inputId = ++liveInputId;
|
|
1954
2139
|
let sentInitial = false;
|
|
1955
2140
|
return { async next() {
|
|
1956
2141
|
if (closed || abortSignal.aborted) return {
|
|
@@ -1967,8 +2152,8 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1967
2152
|
done: false
|
|
1968
2153
|
};
|
|
1969
2154
|
}
|
|
1970
|
-
const nextMessage = await
|
|
1971
|
-
if (nextMessage.done) return {
|
|
2155
|
+
const nextMessage = await takeMessage(inputId);
|
|
2156
|
+
if (nextMessage == null || nextMessage.done) return {
|
|
1972
2157
|
value: void 0,
|
|
1973
2158
|
done: true
|
|
1974
2159
|
};
|
|
@@ -1986,6 +2171,24 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1986
2171
|
} }
|
|
1987
2172
|
};
|
|
1988
2173
|
}
|
|
2174
|
+
function withoutInitialMessage(input) {
|
|
2175
|
+
return { [Symbol.asyncIterator]() {
|
|
2176
|
+
const iterator = input[Symbol.asyncIterator]();
|
|
2177
|
+
let skipped = false;
|
|
2178
|
+
return { async next() {
|
|
2179
|
+
if (!skipped) {
|
|
2180
|
+
skipped = true;
|
|
2181
|
+
await iterator.next();
|
|
2182
|
+
}
|
|
2183
|
+
return iterator.next();
|
|
2184
|
+
} };
|
|
2185
|
+
} };
|
|
2186
|
+
}
|
|
2187
|
+
async function disposeQuery(query) {
|
|
2188
|
+
try {
|
|
2189
|
+
await query?.return?.(void 0);
|
|
2190
|
+
} catch {}
|
|
2191
|
+
}
|
|
1989
2192
|
function addUsage(total, usage) {
|
|
1990
2193
|
if (total == null) return usage;
|
|
1991
2194
|
const result = { ...total };
|