@ai-sdk/harness-claude-code 1.0.171 → 1.0.173
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 +27 -0
- package/dist/bridge/index.mjs +297 -77
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -4
- 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 +341 -21
- package/src/bridge/json-schema-to-zod.ts +32 -2
- package/src/claude-code-bridge-protocol.ts +13 -0
- package/src/claude-code-harness.ts +40 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @ai-sdk/harness-claude-code
|
|
2
2
|
|
|
3
|
+
## 1.0.173
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- edd4d4f: fix(harness-claude-code): correctly map dotted AI Gateway model IDs to the ID shapes Claude Code expects
|
|
8
|
+
- Updated dependencies [7716e18]
|
|
9
|
+
- Updated dependencies [5c247e9]
|
|
10
|
+
- Updated dependencies [dcd2f8d]
|
|
11
|
+
- @ai-sdk/provider-utils@5.0.72
|
|
12
|
+
- @ai-sdk/provider@4.0.35
|
|
13
|
+
- @ai-sdk/harness@1.0.169
|
|
14
|
+
|
|
15
|
+
## 1.0.172
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 4ab93bb: feat(harness-claude-code): add `toolHints` to mark host tools read-only or always-loaded
|
|
20
|
+
- ede06e3: carry out an approval answered after a resume instead of asking again
|
|
21
|
+
- 0808bf4: fix(harness-claude-code): preserve host tool numeric bounds, string lengths, array item counts, and defaults in Zod schemas
|
|
22
|
+
- Updated dependencies [033f8d9]
|
|
23
|
+
- Updated dependencies [e7a8988]
|
|
24
|
+
- Updated dependencies [2c12e25]
|
|
25
|
+
- Updated dependencies [61a315e]
|
|
26
|
+
- Updated dependencies [6eab0f7]
|
|
27
|
+
- @ai-sdk/harness@1.0.168
|
|
28
|
+
- @ai-sdk/provider-utils@5.0.71
|
|
29
|
+
|
|
3
30
|
## 1.0.171
|
|
4
31
|
|
|
5
32
|
### 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,53 @@ function createPermissionSettings(input) {
|
|
|
1582
1603
|
sandbox: { autoAllowBashIfSandboxed: false }
|
|
1583
1604
|
};
|
|
1584
1605
|
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Claude Code picks a model's output limit, system prompt and default effort
|
|
1608
|
+
* from its own reading of the model id, and misreads AI Gateway ids with a
|
|
1609
|
+
* dotted version such as `anthropic/claude-opus-5.5`. `modelOverrides` maps
|
|
1610
|
+
* the id Claude Code expects (`claude-opus-5-5`) to the gateway id, which
|
|
1611
|
+
* stays the id sent to the gateway.
|
|
1612
|
+
*/
|
|
1613
|
+
function createGatewayModelOverrides(model) {
|
|
1614
|
+
if (!model?.startsWith("anthropic/")) return void 0;
|
|
1615
|
+
const id = model.slice(10).replace(/\[[^\]]*\]$/, "");
|
|
1616
|
+
return id.includes(".") ? { [id.replace(/\./g, "-")]: model } : void 0;
|
|
1617
|
+
}
|
|
1618
|
+
function needsHostApproval(input) {
|
|
1619
|
+
if (input.toolName.startsWith("mcp__harness-tools__")) return false;
|
|
1620
|
+
return input.inactiveNativeTools.has(input.toolName) || nativeToolRequiresApproval({
|
|
1621
|
+
nativeName: input.toolName,
|
|
1622
|
+
permissionMode: input.permissionMode
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
function createApprovalPreToolUseHook(input) {
|
|
1626
|
+
return async (hookInput, toolUseID) => {
|
|
1627
|
+
const toolName = hookInput.tool_name;
|
|
1628
|
+
if (toolUseID == null || typeof toolName !== "string" || toolName === "AskUserQuestion") return {};
|
|
1629
|
+
const decision = input.decisions.get(toolUseID);
|
|
1630
|
+
if (decision != null) {
|
|
1631
|
+
input.nativeToolCallNames.set(toolUseID, toolName);
|
|
1632
|
+
if (!decision.approved) input.decisions.delete(toolUseID);
|
|
1633
|
+
return { hookSpecificOutput: {
|
|
1634
|
+
hookEventName: "PreToolUse",
|
|
1635
|
+
...decision.approved ? { permissionDecision: "allow" } : {
|
|
1636
|
+
permissionDecision: "deny",
|
|
1637
|
+
permissionDecisionReason: decision.reason ?? "Denied"
|
|
1638
|
+
}
|
|
1639
|
+
} };
|
|
1640
|
+
}
|
|
1641
|
+
if (!needsHostApproval({
|
|
1642
|
+
toolName,
|
|
1643
|
+
permissionMode: input.permissionMode,
|
|
1644
|
+
inactiveNativeTools: input.inactiveNativeTools
|
|
1645
|
+
})) return {};
|
|
1646
|
+
input.deferredToolUseIds.add(toolUseID);
|
|
1647
|
+
return { hookSpecificOutput: {
|
|
1648
|
+
hookEventName: "PreToolUse",
|
|
1649
|
+
permissionDecision: "defer"
|
|
1650
|
+
} };
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1585
1653
|
function nativeToolRequiresApproval(input) {
|
|
1586
1654
|
if (input.permissionMode === "allow-all") return false;
|
|
1587
1655
|
const kind = NATIVE_TOOL_KINDS[input.nativeName] ?? "edit";
|
|
@@ -1606,70 +1674,75 @@ async function runTurn(start, turn) {
|
|
|
1606
1674
|
name: "harness-tools",
|
|
1607
1675
|
version: "1.0.0"
|
|
1608
1676
|
});
|
|
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,
|
|
1677
|
+
for (const tool of start.tools) {
|
|
1678
|
+
const hints = start.toolHints?.[tool.name];
|
|
1679
|
+
server.registerTool(tool.name, {
|
|
1680
|
+
description: tool.description ?? "",
|
|
1681
|
+
inputSchema: jsonSchemaToZodObject(tool.inputSchema),
|
|
1682
|
+
...hints?.readOnly == null ? {} : { annotations: { readOnlyHint: hints.readOnly } },
|
|
1683
|
+
...hints?.alwaysLoad == null ? {} : { _meta: { "anthropic/alwaysLoad": hints.alwaysLoad } }
|
|
1684
|
+
}, async (...handlerArgs) => {
|
|
1685
|
+
const [input, extra] = handlerArgs;
|
|
1686
|
+
const metadataToolCallId = extra._meta?.["claudecode/toolUseId"];
|
|
1687
|
+
const toolCallId = resolveMcpHandlerToolUseId({
|
|
1688
|
+
state: streamEventState,
|
|
1689
|
+
metadataToolUseId: typeof metadataToolCallId === "string" ? metadataToolCallId : void 0,
|
|
1690
|
+
toolName: tool.name,
|
|
1691
|
+
input
|
|
1692
|
+
}) ?? randomUUID();
|
|
1693
|
+
streamEventState.mcpHandlerCalls.set(toolCallId, {
|
|
1630
1694
|
toolName: tool.name,
|
|
1631
|
-
input
|
|
1632
|
-
providerExecuted: false
|
|
1695
|
+
input
|
|
1633
1696
|
});
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
emit
|
|
1637
|
-
|
|
1697
|
+
if (!streamEventState.emittedToolCallIds.has(toolCallId)) {
|
|
1698
|
+
streamEventState.emittedToolCallIds.add(toolCallId);
|
|
1699
|
+
emit({
|
|
1700
|
+
type: "tool-call",
|
|
1701
|
+
toolCallId,
|
|
1702
|
+
toolName: tool.name,
|
|
1703
|
+
input: JSON.stringify(input),
|
|
1704
|
+
providerExecuted: false
|
|
1705
|
+
});
|
|
1706
|
+
markWaitingOnHostInput({
|
|
1707
|
+
state: streamEventState,
|
|
1708
|
+
emit,
|
|
1709
|
+
toolUseId: toolCallId
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
const { output, isError, toolResult } = await turn.requestToolResult(toolCallId).finally(() => {
|
|
1713
|
+
clearWaitingOnHostInput({
|
|
1714
|
+
state: streamEventState,
|
|
1715
|
+
toolUseId: toolCallId
|
|
1716
|
+
});
|
|
1717
|
+
if (!streamEventState.mcpToolCalls.has(toolCallId)) streamEventState.pendingStepToolUseIds.delete(toolCallId);
|
|
1638
1718
|
});
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1719
|
+
emit({
|
|
1720
|
+
type: "tool-result",
|
|
1721
|
+
toolCallId,
|
|
1722
|
+
toolName: tool.name,
|
|
1723
|
+
result: output ?? null,
|
|
1724
|
+
isError: !!isError
|
|
1644
1725
|
});
|
|
1645
|
-
if (
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1726
|
+
if (toolResult != null) {
|
|
1727
|
+
const converted = convertHarnessToolModelOutput({ output: toolResult.output });
|
|
1728
|
+
return {
|
|
1729
|
+
content: converted.content.map((part) => part.type === "text" ? part : {
|
|
1730
|
+
type: "image",
|
|
1731
|
+
data: part.data,
|
|
1732
|
+
mimeType: part.mediaType
|
|
1733
|
+
}),
|
|
1734
|
+
isError: isError === true || converted.isError
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1656
1737
|
return {
|
|
1657
|
-
content:
|
|
1658
|
-
type: "
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
isError: isError === true || converted.isError
|
|
1738
|
+
content: [{
|
|
1739
|
+
type: "text",
|
|
1740
|
+
text: JSON.stringify(output ?? null)
|
|
1741
|
+
}],
|
|
1742
|
+
isError
|
|
1663
1743
|
};
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
content: [{
|
|
1667
|
-
type: "text",
|
|
1668
|
-
text: JSON.stringify(output ?? null)
|
|
1669
|
-
}],
|
|
1670
|
-
isError
|
|
1671
|
-
};
|
|
1672
|
-
});
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1673
1746
|
mcpServers["harness-tools"] = {
|
|
1674
1747
|
type: "sdk",
|
|
1675
1748
|
name: "harness-tools",
|
|
@@ -1693,7 +1766,10 @@ async function runTurn(start, turn) {
|
|
|
1693
1766
|
const skillsOption = toClaudeSkillsOption(start.skills);
|
|
1694
1767
|
const nativeTools = resolveNativeTools(start.builtinToolFiltering);
|
|
1695
1768
|
const inactiveNativeTools = resolveInactiveNativeTools(start.builtinToolFiltering);
|
|
1769
|
+
const permissionMode = start.permissionMode ?? "allow-all";
|
|
1770
|
+
const approvalDecisions = /* @__PURE__ */ new Map();
|
|
1696
1771
|
const permissionOptions = createPermissionOptions({
|
|
1772
|
+
approvalDecisions,
|
|
1697
1773
|
start,
|
|
1698
1774
|
inactiveNativeTools,
|
|
1699
1775
|
turn,
|
|
@@ -1711,6 +1787,15 @@ async function runTurn(start, turn) {
|
|
|
1711
1787
|
approvalRequestedToolUseIds: streamEventState.approvalRequestedToolUseIds,
|
|
1712
1788
|
emittedToolCallIds: streamEventState.emittedToolCallIds
|
|
1713
1789
|
});
|
|
1790
|
+
const modelOverrides = createGatewayModelOverrides(start.model);
|
|
1791
|
+
const deferredToolUseIds = /* @__PURE__ */ new Set();
|
|
1792
|
+
const approvalPreToolUseHook = createApprovalPreToolUseHook({
|
|
1793
|
+
permissionMode,
|
|
1794
|
+
inactiveNativeTools: new Set(inactiveNativeTools),
|
|
1795
|
+
decisions: approvalDecisions,
|
|
1796
|
+
deferredToolUseIds,
|
|
1797
|
+
nativeToolCallNames: streamEventState.nativeToolCallNames
|
|
1798
|
+
});
|
|
1714
1799
|
const questionPreToolUseHook = createQuestionPreToolUseHook({
|
|
1715
1800
|
turn,
|
|
1716
1801
|
emit,
|
|
@@ -1725,7 +1810,7 @@ async function runTurn(start, turn) {
|
|
|
1725
1810
|
toolUseId
|
|
1726
1811
|
})
|
|
1727
1812
|
});
|
|
1728
|
-
const
|
|
1813
|
+
const queryArgs = {
|
|
1729
1814
|
prompt: queryInput.input,
|
|
1730
1815
|
options: {
|
|
1731
1816
|
...start.model ? { model: start.model } : {},
|
|
@@ -1750,7 +1835,7 @@ async function runTurn(start, turn) {
|
|
|
1750
1835
|
} } : {},
|
|
1751
1836
|
includePartialMessages: true,
|
|
1752
1837
|
hooks: {
|
|
1753
|
-
PreToolUse: [{
|
|
1838
|
+
PreToolUse: [...permissionMode === "allow-all" ? [] : [{ hooks: [approvalPreToolUseHook] }], {
|
|
1754
1839
|
matcher: "AskUserQuestion",
|
|
1755
1840
|
hooks: [questionPreToolUseHook],
|
|
1756
1841
|
...start.questionTimeoutMs !== void 0 ? { timeout: Math.ceil(start.questionTimeoutMs / 1e3) } : {}
|
|
@@ -1762,18 +1847,70 @@ async function runTurn(start, turn) {
|
|
|
1762
1847
|
},
|
|
1763
1848
|
...start.resumeSessionId ?? lastClaudeSessionId ? { resume: start.resumeSessionId ?? lastClaudeSessionId } : start.continue === true || !turn.firstTurn ? { continue: true } : {},
|
|
1764
1849
|
...permissionOptions,
|
|
1850
|
+
...modelOverrides ? { settings: {
|
|
1851
|
+
...permissionOptions.settings,
|
|
1852
|
+
modelOverrides
|
|
1853
|
+
} } : {},
|
|
1765
1854
|
mcpServers,
|
|
1766
1855
|
cwd: workdir,
|
|
1767
1856
|
abortSignal: abortCtl.signal
|
|
1768
1857
|
}
|
|
1858
|
+
};
|
|
1859
|
+
let deferredToolUseId = start.deferredToolUseId;
|
|
1860
|
+
let resumingDeferredCall = false;
|
|
1861
|
+
let q = deferredToolUseId == null ? claudeSdk.query(queryArgs) : void 0;
|
|
1862
|
+
const hostAborted = new Promise((resolve) => {
|
|
1863
|
+
if (abortCtl.signal.aborted) resolve(void 0);
|
|
1864
|
+
abortCtl.signal.addEventListener("abort", () => resolve(void 0), { once: true });
|
|
1769
1865
|
});
|
|
1770
1866
|
gracefulAbort = () => {
|
|
1771
1867
|
hardAbortTimer = setTimeout(() => abortCtl.abort(), 5e3);
|
|
1772
1868
|
hardAbortTimer.unref?.();
|
|
1773
|
-
Promise.resolve().then(() => q
|
|
1869
|
+
Promise.resolve().then(() => q?.interrupt()).catch(() => abortCtl.abort());
|
|
1774
1870
|
};
|
|
1775
1871
|
let turnUsage;
|
|
1776
1872
|
let totalCostUsd;
|
|
1873
|
+
let deferredDecision;
|
|
1874
|
+
const requestDeferredApproval = (call) => {
|
|
1875
|
+
for (const droppedToolUseId of deferredToolUseIds) {
|
|
1876
|
+
if (droppedToolUseId === call.toolUseId) continue;
|
|
1877
|
+
const nativeName = streamEventState.nativeToolCallNames.get(droppedToolUseId) ?? "unknown";
|
|
1878
|
+
emit({
|
|
1879
|
+
type: "tool-result",
|
|
1880
|
+
toolCallId: droppedToolUseId,
|
|
1881
|
+
toolName: toCommonName(nativeName),
|
|
1882
|
+
result: "Claude Code can wait for only one approval at a time, so this call did not run.",
|
|
1883
|
+
isError: true
|
|
1884
|
+
});
|
|
1885
|
+
streamEventState.pendingStepToolUseIds.delete(droppedToolUseId);
|
|
1886
|
+
}
|
|
1887
|
+
deferredToolUseIds.clear();
|
|
1888
|
+
streamEventState.approvalRequestedToolUseIds.add(call.toolUseId);
|
|
1889
|
+
streamEventState.nativeToolCallNames.set(call.toolUseId, call.toolName);
|
|
1890
|
+
if (!streamEventState.emittedToolCallIds.has(call.toolUseId)) {
|
|
1891
|
+
streamEventState.emittedToolCallIds.add(call.toolUseId);
|
|
1892
|
+
emit({
|
|
1893
|
+
type: "tool-call",
|
|
1894
|
+
toolCallId: call.toolUseId,
|
|
1895
|
+
toolName: toCommonName(call.toolName),
|
|
1896
|
+
nativeName: call.toolName,
|
|
1897
|
+
input: JSON.stringify(call.toolInput ?? {}),
|
|
1898
|
+
providerExecuted: true,
|
|
1899
|
+
...isExternalMcpTool(call.toolName) ? { dynamic: true } : {}
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
deferredDecision = turn.requestToolApproval(call.toolUseId);
|
|
1903
|
+
emit({
|
|
1904
|
+
type: "tool-approval-request",
|
|
1905
|
+
approvalId: call.toolUseId,
|
|
1906
|
+
toolCallId: call.toolUseId
|
|
1907
|
+
});
|
|
1908
|
+
markWaitingOnHostInput({
|
|
1909
|
+
state: streamEventState,
|
|
1910
|
+
emit,
|
|
1911
|
+
toolUseId: call.toolUseId
|
|
1912
|
+
});
|
|
1913
|
+
};
|
|
1777
1914
|
let emittedTerminalError = false;
|
|
1778
1915
|
let emittedTerminalFinish = false;
|
|
1779
1916
|
const emitTerminalError = (message) => {
|
|
@@ -1796,14 +1933,59 @@ async function runTurn(start, turn) {
|
|
|
1796
1933
|
onCompactionBoundary: (boundary) => compaction.onBoundary(boundary),
|
|
1797
1934
|
toCommonName
|
|
1798
1935
|
});
|
|
1936
|
+
async function* messages() {
|
|
1937
|
+
for (;;) {
|
|
1938
|
+
if (deferredToolUseId != null) {
|
|
1939
|
+
const toolUseId = deferredToolUseId;
|
|
1940
|
+
const decision = await Promise.race([deferredDecision ?? turn.requestToolApproval(toolUseId), hostAborted]).finally(() => clearWaitingOnHostInput({
|
|
1941
|
+
state: streamEventState,
|
|
1942
|
+
toolUseId
|
|
1943
|
+
}));
|
|
1944
|
+
deferredDecision = void 0;
|
|
1945
|
+
if (decision == null || abortCtl.signal.aborted) return;
|
|
1946
|
+
approvalDecisions.set(toolUseId, decision);
|
|
1947
|
+
deferredToolUseId = void 0;
|
|
1948
|
+
resumingDeferredCall = true;
|
|
1949
|
+
q = claudeSdk.query({
|
|
1950
|
+
prompt: withoutInitialMessage(queryInput.input),
|
|
1951
|
+
options: {
|
|
1952
|
+
...queryArgs.options,
|
|
1953
|
+
...lastClaudeSessionId != null ? {
|
|
1954
|
+
continue: void 0,
|
|
1955
|
+
resume: lastClaudeSessionId
|
|
1956
|
+
} : {}
|
|
1957
|
+
}
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
for await (const msg of q) {
|
|
1961
|
+
yield msg;
|
|
1962
|
+
if (deferredToolUseId != null) break;
|
|
1963
|
+
}
|
|
1964
|
+
if (deferredToolUseId == null || abortCtl.signal.aborted) return;
|
|
1965
|
+
queryInput.retire();
|
|
1966
|
+
await disposeQuery(q);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1799
1969
|
try {
|
|
1800
|
-
for await (const msg of
|
|
1970
|
+
for await (const msg of messages()) {
|
|
1801
1971
|
if (abortCtl.signal.aborted) break;
|
|
1802
1972
|
const type = msg.type;
|
|
1803
1973
|
if (type === "command_lifecycle") queryInput.handleLifecycle(msg);
|
|
1804
1974
|
const sessionId = msg.session_id;
|
|
1805
1975
|
if (typeof sessionId === "string" && sessionId.length > 0) lastClaudeSessionId = sessionId;
|
|
1806
1976
|
emitStreamEvent(msg);
|
|
1977
|
+
if (type === "result" && msg.terminal_reason === "tool_deferred" && msg.deferred_tool_use?.id != null) {
|
|
1978
|
+
const harnessUsage = mapUsage(msg.usage ?? msg.message?.usage);
|
|
1979
|
+
if (harnessUsage) turnUsage = addUsage(turnUsage, harnessUsage);
|
|
1980
|
+
if (typeof msg.total_cost_usd === "number") totalCostUsd = msg.total_cost_usd;
|
|
1981
|
+
deferredToolUseId = msg.deferred_tool_use.id;
|
|
1982
|
+
requestDeferredApproval({
|
|
1983
|
+
toolUseId: msg.deferred_tool_use.id,
|
|
1984
|
+
toolName: msg.deferred_tool_use.name,
|
|
1985
|
+
toolInput: msg.deferred_tool_use.input
|
|
1986
|
+
});
|
|
1987
|
+
continue;
|
|
1988
|
+
}
|
|
1807
1989
|
if (type === "result") {
|
|
1808
1990
|
if (msg.subtype === "success") {
|
|
1809
1991
|
if (msg.is_error) {
|
|
@@ -1839,7 +2021,7 @@ async function runTurn(start, turn) {
|
|
|
1839
2021
|
emit,
|
|
1840
2022
|
usage: streamEventState.pendingStepUsage ?? harnessUsage
|
|
1841
2023
|
});
|
|
1842
|
-
if (!queryInput.answersSentMessage(msg)) continue;
|
|
2024
|
+
if (!resumingDeferredCall && !queryInput.answersSentMessage(msg)) continue;
|
|
1843
2025
|
queryInput.observeResult();
|
|
1844
2026
|
if (!queryInput.hasActiveUserMessages()) {
|
|
1845
2027
|
queryInput.close();
|
|
@@ -1864,9 +2046,7 @@ async function runTurn(start, turn) {
|
|
|
1864
2046
|
if (hardAbortTimer != null) clearTimeout(hardAbortTimer);
|
|
1865
2047
|
turn.abortSignal.removeEventListener("abort", onHostAbort);
|
|
1866
2048
|
queryInput.close();
|
|
1867
|
-
|
|
1868
|
-
await q.return?.(void 0);
|
|
1869
|
-
} catch {}
|
|
2049
|
+
await disposeQuery(q);
|
|
1870
2050
|
}
|
|
1871
2051
|
if (emittedTerminalError) return;
|
|
1872
2052
|
emittedTerminalFinish = true;
|
|
@@ -1923,6 +2103,24 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1923
2103
|
...options.priority == null ? {} : { priority: options.priority }
|
|
1924
2104
|
});
|
|
1925
2105
|
const messageIterator = userMessages[Symbol.asyncIterator]();
|
|
2106
|
+
let liveInputId = 0;
|
|
2107
|
+
let bufferedMessage;
|
|
2108
|
+
let pulling;
|
|
2109
|
+
const takeMessage = async (inputId) => {
|
|
2110
|
+
for (;;) {
|
|
2111
|
+
if (inputId !== liveInputId) return void 0;
|
|
2112
|
+
if (bufferedMessage != null) {
|
|
2113
|
+
const message = bufferedMessage;
|
|
2114
|
+
bufferedMessage = void 0;
|
|
2115
|
+
return message;
|
|
2116
|
+
}
|
|
2117
|
+
pulling ??= messageIterator.next().then((result) => {
|
|
2118
|
+
bufferedMessage = result;
|
|
2119
|
+
pulling = void 0;
|
|
2120
|
+
});
|
|
2121
|
+
await pulling;
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
1926
2124
|
return {
|
|
1927
2125
|
close,
|
|
1928
2126
|
handleLifecycle: (message) => {
|
|
@@ -1947,10 +2145,14 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1947
2145
|
observeResult: () => {
|
|
1948
2146
|
observedResult = true;
|
|
1949
2147
|
},
|
|
2148
|
+
retire: () => {
|
|
2149
|
+
liveInputId++;
|
|
2150
|
+
},
|
|
1950
2151
|
get hasObservedResult() {
|
|
1951
2152
|
return observedResult;
|
|
1952
2153
|
},
|
|
1953
2154
|
input: { [Symbol.asyncIterator]() {
|
|
2155
|
+
const inputId = ++liveInputId;
|
|
1954
2156
|
let sentInitial = false;
|
|
1955
2157
|
return { async next() {
|
|
1956
2158
|
if (closed || abortSignal.aborted) return {
|
|
@@ -1967,8 +2169,8 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1967
2169
|
done: false
|
|
1968
2170
|
};
|
|
1969
2171
|
}
|
|
1970
|
-
const nextMessage = await
|
|
1971
|
-
if (nextMessage.done) return {
|
|
2172
|
+
const nextMessage = await takeMessage(inputId);
|
|
2173
|
+
if (nextMessage == null || nextMessage.done) return {
|
|
1972
2174
|
value: void 0,
|
|
1973
2175
|
done: true
|
|
1974
2176
|
};
|
|
@@ -1986,6 +2188,24 @@ function createQueryInput({ initialUserMessage, userMessages, abortSignal }) {
|
|
|
1986
2188
|
} }
|
|
1987
2189
|
};
|
|
1988
2190
|
}
|
|
2191
|
+
function withoutInitialMessage(input) {
|
|
2192
|
+
return { [Symbol.asyncIterator]() {
|
|
2193
|
+
const iterator = input[Symbol.asyncIterator]();
|
|
2194
|
+
let skipped = false;
|
|
2195
|
+
return { async next() {
|
|
2196
|
+
if (!skipped) {
|
|
2197
|
+
skipped = true;
|
|
2198
|
+
await iterator.next();
|
|
2199
|
+
}
|
|
2200
|
+
return iterator.next();
|
|
2201
|
+
} };
|
|
2202
|
+
} };
|
|
2203
|
+
}
|
|
2204
|
+
async function disposeQuery(query) {
|
|
2205
|
+
try {
|
|
2206
|
+
await query?.return?.(void 0);
|
|
2207
|
+
} catch {}
|
|
2208
|
+
}
|
|
1989
2209
|
function addUsage(total, usage) {
|
|
1990
2210
|
if (total == null) return usage;
|
|
1991
2211
|
const result = { ...total };
|