@ai-sdk/harness-opencode 1.0.41 → 1.0.43
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 +16 -0
- package/dist/bridge/index.mjs +447 -404
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +512 -0
- package/src/bridge/index.ts +54 -495
- package/src/bridge/opencode-events.ts +22 -0
package/dist/bridge/index.mjs
CHANGED
|
@@ -497,6 +497,7 @@ import {
|
|
|
497
497
|
// src/bridge/opencode-events.ts
|
|
498
498
|
function createTranslationState() {
|
|
499
499
|
return {
|
|
500
|
+
streamStarted: false,
|
|
500
501
|
textDeltas: /* @__PURE__ */ new Map(),
|
|
501
502
|
reasoningDeltas: /* @__PURE__ */ new Map(),
|
|
502
503
|
toolInputs: /* @__PURE__ */ new Map(),
|
|
@@ -512,6 +513,19 @@ function createTranslationState() {
|
|
|
512
513
|
legacyStepFinishPartIds: /* @__PURE__ */ new Set()
|
|
513
514
|
};
|
|
514
515
|
}
|
|
516
|
+
function emitOpenCodeStreamStart({
|
|
517
|
+
info,
|
|
518
|
+
state,
|
|
519
|
+
emit
|
|
520
|
+
}) {
|
|
521
|
+
if (state.streamStarted || !isRecord(info)) return;
|
|
522
|
+
if (info.role !== "assistant" && info.type !== "assistant") return;
|
|
523
|
+
const providerID = stringValue(info.providerID);
|
|
524
|
+
const modelID = stringValue(info.modelID);
|
|
525
|
+
const modelId = providerID && modelID ? `${providerID}/${modelID}` : void 0;
|
|
526
|
+
state.streamStarted = true;
|
|
527
|
+
emit({ type: "stream-start", ...modelId ? { modelId } : {} });
|
|
528
|
+
}
|
|
515
529
|
function unwrapOpenCodeEvent(rawEvent) {
|
|
516
530
|
if (!rawEvent || typeof rawEvent !== "object") return void 0;
|
|
517
531
|
const raw = rawEvent;
|
|
@@ -817,6 +831,393 @@ function isRecord2(value) {
|
|
|
817
831
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
818
832
|
}
|
|
819
833
|
|
|
834
|
+
// src/bridge/create-emit-stream-event.ts
|
|
835
|
+
function createEmitStreamEvent({
|
|
836
|
+
state,
|
|
837
|
+
emit,
|
|
838
|
+
emitWarning,
|
|
839
|
+
emitError,
|
|
840
|
+
toWireToolName: toWireToolName2,
|
|
841
|
+
nativeNameField: nativeNameField2,
|
|
842
|
+
getHostToolName: getHostToolName2,
|
|
843
|
+
authorizeHostToolCall: authorizeHostToolCall2,
|
|
844
|
+
stripWorkDir: stripWorkDir2,
|
|
845
|
+
formatError: formatError2
|
|
846
|
+
}) {
|
|
847
|
+
return (event) => {
|
|
848
|
+
const type = event.type;
|
|
849
|
+
const props = event.properties ?? {};
|
|
850
|
+
if (type === "message.updated") {
|
|
851
|
+
const info = props.info;
|
|
852
|
+
if (isRecord3(info)) {
|
|
853
|
+
const id = stringValue2(info.id);
|
|
854
|
+
const role = stringValue2(info.role);
|
|
855
|
+
if (id && role) state.messageRoles.set(id, role);
|
|
856
|
+
}
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (type === "message.part.delta") {
|
|
860
|
+
emitLegacyPartDelta({ props, state, emit });
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (type === "message.part.updated") {
|
|
864
|
+
if (emitLegacyTextPartUpdate({ part: props.part, state, emit })) return;
|
|
865
|
+
if (emitLegacyStepFinishPart({ part: props.part, state, emit })) return;
|
|
866
|
+
emitLegacyToolPart({
|
|
867
|
+
part: props.part,
|
|
868
|
+
state,
|
|
869
|
+
emit,
|
|
870
|
+
toWireToolName: toWireToolName2,
|
|
871
|
+
nativeNameField: nativeNameField2,
|
|
872
|
+
getHostToolName: getHostToolName2,
|
|
873
|
+
authorizeHostToolCall: authorizeHostToolCall2
|
|
874
|
+
});
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (type === "session.next.text.started") {
|
|
878
|
+
emit({ type: "text-start", id: String(props.textID ?? event.id) });
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (type === "session.next.text.delta") {
|
|
882
|
+
const id = String(props.textID ?? event.id);
|
|
883
|
+
state.textDeltas.set(
|
|
884
|
+
id,
|
|
885
|
+
`${state.textDeltas.get(id) ?? ""}${String(props.delta ?? "")}`
|
|
886
|
+
);
|
|
887
|
+
emit({
|
|
888
|
+
type: "text-delta",
|
|
889
|
+
id,
|
|
890
|
+
delta: String(props.delta ?? "")
|
|
891
|
+
});
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (type === "session.next.text.ended") {
|
|
895
|
+
const id = String(props.textID ?? event.id);
|
|
896
|
+
emitMissingFinalDelta({
|
|
897
|
+
id,
|
|
898
|
+
fullText: typeof props.text === "string" ? props.text : void 0,
|
|
899
|
+
emittedText: state.textDeltas.get(id) ?? "",
|
|
900
|
+
emit,
|
|
901
|
+
type: "text-delta"
|
|
902
|
+
});
|
|
903
|
+
emit({ type: "text-end", id });
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (type === "session.next.reasoning.started") {
|
|
907
|
+
emit({
|
|
908
|
+
type: "reasoning-start",
|
|
909
|
+
id: String(props.reasoningID ?? event.id)
|
|
910
|
+
});
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
if (type === "session.next.reasoning.delta") {
|
|
914
|
+
const id = String(props.reasoningID ?? event.id);
|
|
915
|
+
state.reasoningDeltas.set(
|
|
916
|
+
id,
|
|
917
|
+
`${state.reasoningDeltas.get(id) ?? ""}${String(props.delta ?? "")}`
|
|
918
|
+
);
|
|
919
|
+
emit({
|
|
920
|
+
type: "reasoning-delta",
|
|
921
|
+
id,
|
|
922
|
+
delta: String(props.delta ?? "")
|
|
923
|
+
});
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (type === "session.next.reasoning.ended") {
|
|
927
|
+
const id = String(props.reasoningID ?? event.id);
|
|
928
|
+
emitMissingFinalDelta({
|
|
929
|
+
id,
|
|
930
|
+
fullText: typeof props.text === "string" ? props.text : void 0,
|
|
931
|
+
emittedText: state.reasoningDeltas.get(id) ?? "",
|
|
932
|
+
emit,
|
|
933
|
+
type: "reasoning-delta"
|
|
934
|
+
});
|
|
935
|
+
emit({ type: "reasoning-end", id });
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
if (type === "session.next.shell.started") {
|
|
939
|
+
const callID = String(props.callID ?? event.id);
|
|
940
|
+
const command = String(props.command ?? "");
|
|
941
|
+
state.shellCommands.set(callID, command);
|
|
942
|
+
emit({
|
|
943
|
+
type: "tool-call",
|
|
944
|
+
toolCallId: callID,
|
|
945
|
+
toolName: "bash",
|
|
946
|
+
nativeName: "bash",
|
|
947
|
+
input: JSON.stringify({ command }),
|
|
948
|
+
providerExecuted: true
|
|
949
|
+
});
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
if (type === "session.next.shell.ended") {
|
|
953
|
+
const callID = String(props.callID ?? event.id);
|
|
954
|
+
emit({
|
|
955
|
+
type: "tool-result",
|
|
956
|
+
toolCallId: callID,
|
|
957
|
+
toolName: "bash",
|
|
958
|
+
result: {
|
|
959
|
+
command: state.shellCommands.get(callID) ?? "",
|
|
960
|
+
output: String(props.output ?? "")
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
if (type === "session.next.tool.input.delta") {
|
|
966
|
+
const callID = String(props.callID ?? event.id);
|
|
967
|
+
state.toolInputs.set(
|
|
968
|
+
callID,
|
|
969
|
+
`${state.toolInputs.get(callID) ?? ""}${String(props.delta ?? "")}`
|
|
970
|
+
);
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
if (type === "session.next.tool.input.ended") {
|
|
974
|
+
state.toolInputs.set(
|
|
975
|
+
String(props.callID ?? event.id),
|
|
976
|
+
String(props.text ?? "")
|
|
977
|
+
);
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
if (type === "session.next.tool.called") {
|
|
981
|
+
const callID = String(props.callID ?? event.id);
|
|
982
|
+
const rawToolName = String(props.tool ?? "unknown");
|
|
983
|
+
const toolName = toWireToolName2(rawToolName);
|
|
984
|
+
state.toolNames.set(callID, { rawToolName, toolName });
|
|
985
|
+
const hostToolName = getHostToolName2(toolName, props.tool);
|
|
986
|
+
if (hostToolName) {
|
|
987
|
+
authorizeHostToolCall2({
|
|
988
|
+
callID,
|
|
989
|
+
toolName: hostToolName,
|
|
990
|
+
input: props.input ?? parseToolInput(state, props)
|
|
991
|
+
});
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
emit({
|
|
995
|
+
type: "tool-call",
|
|
996
|
+
toolCallId: callID,
|
|
997
|
+
toolName,
|
|
998
|
+
...nativeNameField2({ nativeName: rawToolName, toolName }),
|
|
999
|
+
input: JSON.stringify(props.input ?? parseToolInput(state, props)),
|
|
1000
|
+
providerExecuted: true,
|
|
1001
|
+
...props.provider?.metadata ? { providerMetadata: props.provider.metadata } : {}
|
|
1002
|
+
});
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
if (type === "session.next.tool.success" || type === "session.next.tool.failed") {
|
|
1006
|
+
const callID = String(props.callID ?? event.id);
|
|
1007
|
+
const cachedTool = state.toolNames.get(callID);
|
|
1008
|
+
const rawToolName = cachedTool?.rawToolName ?? String(props.tool ?? "");
|
|
1009
|
+
const toolName = cachedTool?.toolName ?? toWireToolName2(rawToolName || "unknown");
|
|
1010
|
+
if (getHostToolName2(toolName, rawToolName)) return;
|
|
1011
|
+
emit({
|
|
1012
|
+
type: "tool-result",
|
|
1013
|
+
toolCallId: callID,
|
|
1014
|
+
toolName,
|
|
1015
|
+
result: props.result ?? props.structured ?? ("content" in props ? props.content : null) ?? null,
|
|
1016
|
+
...type === "session.next.tool.failed" ? { isError: true } : {}
|
|
1017
|
+
});
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (type === "session.next.retried") {
|
|
1021
|
+
const error = props.error ?? event;
|
|
1022
|
+
if (isRecord3(error) && error.isRetryable === false) {
|
|
1023
|
+
emitError({
|
|
1024
|
+
error,
|
|
1025
|
+
message: "OpenCode session retry failed"
|
|
1026
|
+
});
|
|
1027
|
+
} else {
|
|
1028
|
+
emitWarning({ message: nextRetryEventMessage({ event, formatError: formatError2 }) });
|
|
1029
|
+
}
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
if (type === "session.next.step.ended") {
|
|
1033
|
+
closeLegacyOpenParts({ state, emit });
|
|
1034
|
+
state.turnUsage = mapUsage(props.tokens);
|
|
1035
|
+
emit({
|
|
1036
|
+
type: "finish-step",
|
|
1037
|
+
finishReason: {
|
|
1038
|
+
unified: mapOpenCodeFinishReason(String(props.finish ?? "stop")),
|
|
1039
|
+
raw: String(props.finish ?? "stop")
|
|
1040
|
+
},
|
|
1041
|
+
usage: state.turnUsage,
|
|
1042
|
+
...typeof props.cost === "number" ? { harnessMetadata: { opencode: { cost: props.cost } } } : {}
|
|
1043
|
+
});
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (type === "session.next.compaction.ended") {
|
|
1047
|
+
emit({
|
|
1048
|
+
type: "compaction",
|
|
1049
|
+
trigger: props.reason === "auto" ? "auto" : "manual",
|
|
1050
|
+
summary: String(props.text ?? ""),
|
|
1051
|
+
harnessMetadata: {
|
|
1052
|
+
opencode: {
|
|
1053
|
+
recent: String(props.recent ?? "")
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (type === "file.edited") {
|
|
1060
|
+
emit({
|
|
1061
|
+
type: "file-change",
|
|
1062
|
+
event: "modify",
|
|
1063
|
+
path: stripWorkDir2(String(props.file ?? ""))
|
|
1064
|
+
});
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (type === "session.error" || type === "session.next.step.failed") {
|
|
1068
|
+
const error = props.error ?? event;
|
|
1069
|
+
emitError({
|
|
1070
|
+
error,
|
|
1071
|
+
message: type === "session.error" ? "OpenCode session error" : "OpenCode step failed"
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
function closeLegacyOpenParts({
|
|
1077
|
+
state,
|
|
1078
|
+
emit
|
|
1079
|
+
}) {
|
|
1080
|
+
for (const id of state.legacyReasoningPartIds) {
|
|
1081
|
+
emit({ type: "reasoning-end", id });
|
|
1082
|
+
state.reasoningDeltas.delete(id);
|
|
1083
|
+
}
|
|
1084
|
+
state.legacyReasoningPartIds.clear();
|
|
1085
|
+
for (const id of state.legacyTextPartIds) {
|
|
1086
|
+
emit({ type: "text-end", id });
|
|
1087
|
+
state.textDeltas.delete(id);
|
|
1088
|
+
}
|
|
1089
|
+
state.legacyTextPartIds.clear();
|
|
1090
|
+
}
|
|
1091
|
+
function emitLegacyStepFinishPart({
|
|
1092
|
+
part,
|
|
1093
|
+
state,
|
|
1094
|
+
emit
|
|
1095
|
+
}) {
|
|
1096
|
+
const event = legacyStepFinishPartToFinishStep(part);
|
|
1097
|
+
if (!event) return false;
|
|
1098
|
+
const id = isRecord3(part) ? stringValue2(part.id) : void 0;
|
|
1099
|
+
if (id) {
|
|
1100
|
+
if (state.legacyStepFinishPartIds.has(id)) return true;
|
|
1101
|
+
state.legacyStepFinishPartIds.add(id);
|
|
1102
|
+
}
|
|
1103
|
+
closeLegacyOpenParts({ state, emit });
|
|
1104
|
+
state.turnUsage = event.usage;
|
|
1105
|
+
emit(event);
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1108
|
+
function emitLegacyToolPart({
|
|
1109
|
+
part,
|
|
1110
|
+
state,
|
|
1111
|
+
emit,
|
|
1112
|
+
toWireToolName: toWireToolName2,
|
|
1113
|
+
nativeNameField: nativeNameField2,
|
|
1114
|
+
getHostToolName: getHostToolName2,
|
|
1115
|
+
authorizeHostToolCall: authorizeHostToolCall2
|
|
1116
|
+
}) {
|
|
1117
|
+
if (!part || typeof part !== "object") return;
|
|
1118
|
+
const toolPart = part;
|
|
1119
|
+
if (toolPart.type !== "tool") return;
|
|
1120
|
+
const status = legacyToolPartStatus(toolPart);
|
|
1121
|
+
if (status !== "running" && status !== "completed" && status !== "error") {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (typeof toolPart.tool !== "string" || typeof toolPart.callID !== "string") {
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
const callID = toolPart.callID;
|
|
1128
|
+
const rawToolName = toolPart.tool;
|
|
1129
|
+
const toolName = toWireToolName2(rawToolName);
|
|
1130
|
+
state.toolNames.set(callID, { rawToolName, toolName });
|
|
1131
|
+
const hostToolName = getHostToolName2(toolName, rawToolName);
|
|
1132
|
+
if (hostToolName) {
|
|
1133
|
+
if (status === "running") {
|
|
1134
|
+
authorizeHostToolCall2({
|
|
1135
|
+
callID,
|
|
1136
|
+
toolName: hostToolName,
|
|
1137
|
+
input: legacyToolPartInput(toolPart)
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
if (!state.toolCallsEmitted.has(callID)) {
|
|
1143
|
+
state.toolCallsEmitted.add(callID);
|
|
1144
|
+
emit({
|
|
1145
|
+
type: "tool-call",
|
|
1146
|
+
toolCallId: callID,
|
|
1147
|
+
toolName,
|
|
1148
|
+
...nativeNameField2({ nativeName: rawToolName, toolName }),
|
|
1149
|
+
input: JSON.stringify(legacyToolPartInput(toolPart)),
|
|
1150
|
+
providerExecuted: true,
|
|
1151
|
+
...toolPart.provider?.metadata ? { providerMetadata: toolPart.provider.metadata } : {}
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
if ((status === "completed" || status === "error") && !state.toolResultsEmitted.has(callID)) {
|
|
1155
|
+
state.toolResultsEmitted.add(callID);
|
|
1156
|
+
emit({
|
|
1157
|
+
type: "tool-result",
|
|
1158
|
+
toolCallId: callID,
|
|
1159
|
+
toolName,
|
|
1160
|
+
result: legacyToolPartOutput(toolPart),
|
|
1161
|
+
...status === "error" ? { isError: true } : {}
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
function legacyToolPartStatus(part) {
|
|
1166
|
+
return typeof part.state === "string" ? part.state : typeof part.state === "object" && part.state !== null ? String(part.state.status ?? "") : void 0;
|
|
1167
|
+
}
|
|
1168
|
+
function legacyToolPartInput(part) {
|
|
1169
|
+
const state = typeof part.state === "object" && part.state !== null ? part.state : void 0;
|
|
1170
|
+
return {
|
|
1171
|
+
...isRecord3(part.metadata) ? part.metadata : {},
|
|
1172
|
+
...isRecord3(state?.metadata) ? state.metadata : {},
|
|
1173
|
+
...isRecord3(state?.input) ? state.input : {}
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
function legacyToolPartOutput(part) {
|
|
1177
|
+
const state = typeof part.state === "object" && part.state !== null ? part.state : void 0;
|
|
1178
|
+
if (state?.status === "error") {
|
|
1179
|
+
return state.error ?? part.error ?? state.result ?? "tool failed";
|
|
1180
|
+
}
|
|
1181
|
+
return state?.output ?? state?.result ?? state?.structured ?? state?.content ?? null;
|
|
1182
|
+
}
|
|
1183
|
+
function parseToolInput(state, props) {
|
|
1184
|
+
const text = state.toolInputs.get(String(props.callID ?? ""));
|
|
1185
|
+
if (!text) return {};
|
|
1186
|
+
try {
|
|
1187
|
+
return JSON.parse(text);
|
|
1188
|
+
} catch {
|
|
1189
|
+
return { input: text };
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
function nextRetryEventMessage({
|
|
1193
|
+
event,
|
|
1194
|
+
formatError: formatError2
|
|
1195
|
+
}) {
|
|
1196
|
+
const props = event.properties ?? {};
|
|
1197
|
+
const details = [];
|
|
1198
|
+
if (typeof props.attempt === "number") {
|
|
1199
|
+
details.push(`attempt ${props.attempt}`);
|
|
1200
|
+
}
|
|
1201
|
+
const error = props.error;
|
|
1202
|
+
if (isRecord3(error)) {
|
|
1203
|
+
const message = stringValue2(error.message) ?? (isRecord3(error.data) ? stringValue2(error.data.message) : void 0);
|
|
1204
|
+
const statusCode = error.statusCode;
|
|
1205
|
+
if (typeof statusCode === "number") {
|
|
1206
|
+
details.push(`HTTP ${statusCode}`);
|
|
1207
|
+
}
|
|
1208
|
+
if (message) details.push(message);
|
|
1209
|
+
} else if (error != null) {
|
|
1210
|
+
details.push(formatError2(error));
|
|
1211
|
+
}
|
|
1212
|
+
return details.length > 0 ? `OpenCode session retry: ${details.join("; ")}` : "OpenCode session retry";
|
|
1213
|
+
}
|
|
1214
|
+
function isRecord3(value) {
|
|
1215
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1216
|
+
}
|
|
1217
|
+
function stringValue2(value) {
|
|
1218
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
820
1221
|
// src/bridge/opencode-path.ts
|
|
821
1222
|
import path from "path";
|
|
822
1223
|
var fallbackPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
@@ -1342,25 +1743,6 @@ function legacyRetryStatusMessage(event) {
|
|
|
1342
1743
|
}
|
|
1343
1744
|
return details.length > 0 ? `OpenCode session retry: ${details.join("; ")}` : "OpenCode session retry";
|
|
1344
1745
|
}
|
|
1345
|
-
function nextRetryEventMessage(event) {
|
|
1346
|
-
const props = event.properties ?? {};
|
|
1347
|
-
const details = [];
|
|
1348
|
-
if (typeof props.attempt === "number") {
|
|
1349
|
-
details.push(`attempt ${props.attempt}`);
|
|
1350
|
-
}
|
|
1351
|
-
const error = props.error;
|
|
1352
|
-
if (isRecord3(error)) {
|
|
1353
|
-
const message = stringValue2(error.message) ?? (isRecord3(error.data) ? stringValue2(error.data.message) : void 0);
|
|
1354
|
-
const statusCode = error.statusCode;
|
|
1355
|
-
if (typeof statusCode === "number") {
|
|
1356
|
-
details.push(`HTTP ${statusCode}`);
|
|
1357
|
-
}
|
|
1358
|
-
if (message) details.push(message);
|
|
1359
|
-
} else if (error != null) {
|
|
1360
|
-
details.push(formatError(error));
|
|
1361
|
-
}
|
|
1362
|
-
return details.length > 0 ? `OpenCode session retry: ${details.join("; ")}` : "OpenCode session retry";
|
|
1363
|
-
}
|
|
1364
1746
|
async function ensureSession({
|
|
1365
1747
|
client,
|
|
1366
1748
|
start,
|
|
@@ -1403,6 +1785,7 @@ async function runPrompt({
|
|
|
1403
1785
|
let sawFinishStep = false;
|
|
1404
1786
|
let sawBusy = false;
|
|
1405
1787
|
let terminalError;
|
|
1788
|
+
const state = createTranslationState();
|
|
1406
1789
|
const initialSessionTokens = await readSessionTokens({
|
|
1407
1790
|
client,
|
|
1408
1791
|
sessionId
|
|
@@ -1416,6 +1799,7 @@ async function runPrompt({
|
|
|
1416
1799
|
permissionMode: start.permissionMode,
|
|
1417
1800
|
builtinToolFiltering: start.builtinToolFiltering,
|
|
1418
1801
|
turn,
|
|
1802
|
+
state,
|
|
1419
1803
|
emit: (msg) => {
|
|
1420
1804
|
if (msg.type === "text-delta" || msg.type === "reasoning-delta") {
|
|
1421
1805
|
sawContent = true;
|
|
@@ -1432,6 +1816,13 @@ async function runPrompt({
|
|
|
1432
1816
|
signal: eventsAbort.signal,
|
|
1433
1817
|
onSubscribed: () => eventsReady.resolve(void 0),
|
|
1434
1818
|
onEvent: (event) => {
|
|
1819
|
+
if (event.type === "message.updated") {
|
|
1820
|
+
emitOpenCodeStreamStart({
|
|
1821
|
+
info: event.properties?.info,
|
|
1822
|
+
state,
|
|
1823
|
+
emit
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1435
1826
|
if (event.type === "session.updated") {
|
|
1436
1827
|
latestSessionTokens = extractSessionTokens(event.properties) ?? latestSessionTokens;
|
|
1437
1828
|
}
|
|
@@ -1459,10 +1850,6 @@ async function runPrompt({
|
|
|
1459
1850
|
eventsReady.resolve(void 0);
|
|
1460
1851
|
turnSettled.resolve();
|
|
1461
1852
|
});
|
|
1462
|
-
emit({
|
|
1463
|
-
type: "stream-start",
|
|
1464
|
-
...start.model ? { modelId: start.model } : {}
|
|
1465
|
-
});
|
|
1466
1853
|
await eventsReady.promise;
|
|
1467
1854
|
const prompted = await legacySessionPrompt({
|
|
1468
1855
|
client,
|
|
@@ -1482,6 +1869,7 @@ async function runPrompt({
|
|
|
1482
1869
|
const emittedFallback = await emitContextFallback({
|
|
1483
1870
|
client,
|
|
1484
1871
|
sessionId,
|
|
1872
|
+
state,
|
|
1485
1873
|
emit,
|
|
1486
1874
|
emitContent: !sawContent
|
|
1487
1875
|
}).catch(() => false);
|
|
@@ -1533,6 +1921,7 @@ async function runCompaction({
|
|
|
1533
1921
|
permissionMode: start.permissionMode,
|
|
1534
1922
|
builtinToolFiltering: start.builtinToolFiltering,
|
|
1535
1923
|
turn,
|
|
1924
|
+
state: createTranslationState(),
|
|
1536
1925
|
emit: (msg) => {
|
|
1537
1926
|
if (msg.type === "compaction") sawCompaction = true;
|
|
1538
1927
|
emit(msg);
|
|
@@ -1593,6 +1982,7 @@ async function consumeEvents({
|
|
|
1593
1982
|
permissionMode,
|
|
1594
1983
|
builtinToolFiltering,
|
|
1595
1984
|
turn,
|
|
1985
|
+
state,
|
|
1596
1986
|
emit,
|
|
1597
1987
|
signal,
|
|
1598
1988
|
onSubscribed,
|
|
@@ -1601,393 +1991,47 @@ async function consumeEvents({
|
|
|
1601
1991
|
const stream = await subscribeLegacyEvents({ client, signal });
|
|
1602
1992
|
onSubscribed?.();
|
|
1603
1993
|
if (!stream) return;
|
|
1604
|
-
const
|
|
1994
|
+
const emitStreamEvent = createEmitStreamEvent({
|
|
1995
|
+
state,
|
|
1996
|
+
emit,
|
|
1997
|
+
emitWarning: turn.emitWarning,
|
|
1998
|
+
emitError: turn.emitError,
|
|
1999
|
+
toWireToolName,
|
|
2000
|
+
nativeNameField,
|
|
2001
|
+
getHostToolName,
|
|
2002
|
+
authorizeHostToolCall: (input) => authorizeHostToolCall({ ...input, state }),
|
|
2003
|
+
stripWorkDir,
|
|
2004
|
+
formatError
|
|
2005
|
+
});
|
|
1605
2006
|
for await (const rawEvent of stream) {
|
|
1606
2007
|
if (signal.aborted || turn.abortSignal.aborted) break;
|
|
1607
2008
|
const event = unwrapOpenCodeEvent(rawEvent);
|
|
1608
2009
|
const eventSessionId = event ? getOpenCodeEventSessionId(event) : void 0;
|
|
1609
2010
|
if (!event || eventSessionId && eventSessionId !== sessionId) continue;
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
});
|
|
1620
|
-
if (onEvent?.(event)) break;
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
async function translateAndEmit({
|
|
1624
|
-
event,
|
|
1625
|
-
state,
|
|
1626
|
-
sessionId,
|
|
1627
|
-
permissionMode,
|
|
1628
|
-
builtinToolFiltering,
|
|
1629
|
-
client,
|
|
1630
|
-
turn,
|
|
1631
|
-
emit
|
|
1632
|
-
}) {
|
|
1633
|
-
const type = event.type;
|
|
1634
|
-
const props = event.properties ?? {};
|
|
1635
|
-
if (type === "message.updated") {
|
|
1636
|
-
const info = props.info;
|
|
1637
|
-
if (isRecord3(info)) {
|
|
1638
|
-
const id = stringValue2(info.id);
|
|
1639
|
-
const role = stringValue2(info.role);
|
|
1640
|
-
if (id && role) state.messageRoles.set(id, role);
|
|
1641
|
-
}
|
|
1642
|
-
return;
|
|
1643
|
-
}
|
|
1644
|
-
if (type === "message.part.delta") {
|
|
1645
|
-
emitLegacyPartDelta({ props, state, emit });
|
|
1646
|
-
return;
|
|
1647
|
-
}
|
|
1648
|
-
if (type === "message.part.updated") {
|
|
1649
|
-
if (emitLegacyTextPartUpdate({ part: props.part, state, emit })) return;
|
|
1650
|
-
if (emitLegacyStepFinishPart({ part: props.part, state, emit })) return;
|
|
1651
|
-
emitLegacyToolPart({ part: props.part, state, emit });
|
|
1652
|
-
return;
|
|
1653
|
-
}
|
|
1654
|
-
if (type === "session.next.text.started") {
|
|
1655
|
-
emit({ type: "text-start", id: String(props.textID ?? event.id) });
|
|
1656
|
-
return;
|
|
1657
|
-
}
|
|
1658
|
-
if (type === "session.next.text.delta") {
|
|
1659
|
-
const id = String(props.textID ?? event.id);
|
|
1660
|
-
state.textDeltas.set(
|
|
1661
|
-
id,
|
|
1662
|
-
`${state.textDeltas.get(id) ?? ""}${String(props.delta ?? "")}`
|
|
1663
|
-
);
|
|
1664
|
-
emit({
|
|
1665
|
-
type: "text-delta",
|
|
1666
|
-
id,
|
|
1667
|
-
delta: String(props.delta ?? "")
|
|
1668
|
-
});
|
|
1669
|
-
return;
|
|
1670
|
-
}
|
|
1671
|
-
if (type === "session.next.text.ended") {
|
|
1672
|
-
const id = String(props.textID ?? event.id);
|
|
1673
|
-
emitMissingFinalDelta({
|
|
1674
|
-
id,
|
|
1675
|
-
fullText: typeof props.text === "string" ? props.text : void 0,
|
|
1676
|
-
emittedText: state.textDeltas.get(id) ?? "",
|
|
1677
|
-
emit,
|
|
1678
|
-
type: "text-delta"
|
|
1679
|
-
});
|
|
1680
|
-
emit({ type: "text-end", id });
|
|
1681
|
-
return;
|
|
1682
|
-
}
|
|
1683
|
-
if (type === "session.next.reasoning.started") {
|
|
1684
|
-
emit({
|
|
1685
|
-
type: "reasoning-start",
|
|
1686
|
-
id: String(props.reasoningID ?? event.id)
|
|
1687
|
-
});
|
|
1688
|
-
return;
|
|
1689
|
-
}
|
|
1690
|
-
if (type === "session.next.reasoning.delta") {
|
|
1691
|
-
const id = String(props.reasoningID ?? event.id);
|
|
1692
|
-
state.reasoningDeltas.set(
|
|
1693
|
-
id,
|
|
1694
|
-
`${state.reasoningDeltas.get(id) ?? ""}${String(props.delta ?? "")}`
|
|
1695
|
-
);
|
|
1696
|
-
emit({
|
|
1697
|
-
type: "reasoning-delta",
|
|
1698
|
-
id,
|
|
1699
|
-
delta: String(props.delta ?? "")
|
|
1700
|
-
});
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
if (type === "session.next.reasoning.ended") {
|
|
1704
|
-
const id = String(props.reasoningID ?? event.id);
|
|
1705
|
-
emitMissingFinalDelta({
|
|
1706
|
-
id,
|
|
1707
|
-
fullText: typeof props.text === "string" ? props.text : void 0,
|
|
1708
|
-
emittedText: state.reasoningDeltas.get(id) ?? "",
|
|
1709
|
-
emit,
|
|
1710
|
-
type: "reasoning-delta"
|
|
1711
|
-
});
|
|
1712
|
-
emit({ type: "reasoning-end", id });
|
|
1713
|
-
return;
|
|
1714
|
-
}
|
|
1715
|
-
if (type === "session.next.shell.started") {
|
|
1716
|
-
const callID = String(props.callID ?? event.id);
|
|
1717
|
-
const command = String(props.command ?? "");
|
|
1718
|
-
state.shellCommands.set(callID, command);
|
|
1719
|
-
emit({
|
|
1720
|
-
type: "tool-call",
|
|
1721
|
-
toolCallId: callID,
|
|
1722
|
-
toolName: "bash",
|
|
1723
|
-
nativeName: "bash",
|
|
1724
|
-
input: JSON.stringify({ command }),
|
|
1725
|
-
providerExecuted: true
|
|
1726
|
-
});
|
|
1727
|
-
return;
|
|
1728
|
-
}
|
|
1729
|
-
if (type === "session.next.shell.ended") {
|
|
1730
|
-
const callID = String(props.callID ?? event.id);
|
|
1731
|
-
emit({
|
|
1732
|
-
type: "tool-result",
|
|
1733
|
-
toolCallId: callID,
|
|
1734
|
-
toolName: "bash",
|
|
1735
|
-
result: {
|
|
1736
|
-
command: state.shellCommands.get(callID) ?? "",
|
|
1737
|
-
output: String(props.output ?? "")
|
|
1738
|
-
}
|
|
1739
|
-
});
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
if (type === "session.next.tool.input.delta") {
|
|
1743
|
-
const callID = String(props.callID ?? event.id);
|
|
1744
|
-
state.toolInputs.set(
|
|
1745
|
-
callID,
|
|
1746
|
-
`${state.toolInputs.get(callID) ?? ""}${String(props.delta ?? "")}`
|
|
1747
|
-
);
|
|
1748
|
-
return;
|
|
1749
|
-
}
|
|
1750
|
-
if (type === "session.next.tool.input.ended") {
|
|
1751
|
-
state.toolInputs.set(
|
|
1752
|
-
String(props.callID ?? event.id),
|
|
1753
|
-
String(props.text ?? "")
|
|
1754
|
-
);
|
|
1755
|
-
return;
|
|
1756
|
-
}
|
|
1757
|
-
if (type === "session.next.tool.called") {
|
|
1758
|
-
const callID = String(props.callID ?? event.id);
|
|
1759
|
-
const rawToolName = String(props.tool ?? "unknown");
|
|
1760
|
-
const toolName = toWireToolName(rawToolName);
|
|
1761
|
-
state.toolNames.set(callID, { rawToolName, toolName });
|
|
1762
|
-
const hostToolName = getHostToolName(toolName, props.tool);
|
|
1763
|
-
if (hostToolName) {
|
|
1764
|
-
authorizeHostToolCall({
|
|
1765
|
-
callID,
|
|
1766
|
-
toolName: hostToolName,
|
|
1767
|
-
input: props.input ?? parseToolInput(state, props),
|
|
1768
|
-
state
|
|
2011
|
+
if (event.type === "permission.v2.asked") {
|
|
2012
|
+
await handlePermissionV2({
|
|
2013
|
+
client,
|
|
2014
|
+
sessionId,
|
|
2015
|
+
permissionMode,
|
|
2016
|
+
builtinToolFiltering,
|
|
2017
|
+
turn,
|
|
2018
|
+
emit,
|
|
2019
|
+
event
|
|
1769
2020
|
});
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
...props.provider?.metadata ? { providerMetadata: props.provider.metadata } : {}
|
|
1780
|
-
});
|
|
1781
|
-
return;
|
|
1782
|
-
}
|
|
1783
|
-
if (type === "session.next.tool.success" || type === "session.next.tool.failed") {
|
|
1784
|
-
const callID = String(props.callID ?? event.id);
|
|
1785
|
-
const cachedTool = state.toolNames.get(callID);
|
|
1786
|
-
const rawToolName = cachedTool?.rawToolName ?? String(props.tool ?? "");
|
|
1787
|
-
const toolName = cachedTool?.toolName ?? toWireToolName(rawToolName || "unknown");
|
|
1788
|
-
if (getHostToolName(toolName, rawToolName)) return;
|
|
1789
|
-
emit({
|
|
1790
|
-
type: "tool-result",
|
|
1791
|
-
toolCallId: callID,
|
|
1792
|
-
toolName,
|
|
1793
|
-
result: props.result ?? props.structured ?? ("content" in props ? props.content : null) ?? null,
|
|
1794
|
-
...type === "session.next.tool.failed" ? { isError: true } : {}
|
|
1795
|
-
});
|
|
1796
|
-
return;
|
|
1797
|
-
}
|
|
1798
|
-
if (type === "session.next.retried") {
|
|
1799
|
-
const error = props.error ?? event;
|
|
1800
|
-
if (isRecord3(error) && error.isRetryable === false) {
|
|
1801
|
-
turn.emitError({
|
|
1802
|
-
error,
|
|
1803
|
-
message: "OpenCode session retry failed"
|
|
2021
|
+
} else if (event.type === "permission.asked") {
|
|
2022
|
+
await handlePermission({
|
|
2023
|
+
client,
|
|
2024
|
+
sessionId,
|
|
2025
|
+
permissionMode,
|
|
2026
|
+
builtinToolFiltering,
|
|
2027
|
+
turn,
|
|
2028
|
+
emit,
|
|
2029
|
+
event
|
|
1804
2030
|
});
|
|
1805
2031
|
} else {
|
|
1806
|
-
|
|
2032
|
+
emitStreamEvent(event);
|
|
1807
2033
|
}
|
|
1808
|
-
|
|
1809
|
-
}
|
|
1810
|
-
if (type === "session.next.step.ended") {
|
|
1811
|
-
closeLegacyOpenParts({ state, emit });
|
|
1812
|
-
state.turnUsage = mapUsage(props.tokens);
|
|
1813
|
-
emit({
|
|
1814
|
-
type: "finish-step",
|
|
1815
|
-
finishReason: {
|
|
1816
|
-
unified: mapOpenCodeFinishReason(String(props.finish ?? "stop")),
|
|
1817
|
-
raw: String(props.finish ?? "stop")
|
|
1818
|
-
},
|
|
1819
|
-
usage: state.turnUsage,
|
|
1820
|
-
...typeof props.cost === "number" ? { harnessMetadata: { opencode: { cost: props.cost } } } : {}
|
|
1821
|
-
});
|
|
1822
|
-
return;
|
|
1823
|
-
}
|
|
1824
|
-
if (type === "session.next.compaction.ended") {
|
|
1825
|
-
emit({
|
|
1826
|
-
type: "compaction",
|
|
1827
|
-
trigger: props.reason === "auto" ? "auto" : "manual",
|
|
1828
|
-
summary: String(props.text ?? ""),
|
|
1829
|
-
harnessMetadata: {
|
|
1830
|
-
opencode: {
|
|
1831
|
-
recent: String(props.recent ?? "")
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
});
|
|
1835
|
-
return;
|
|
1836
|
-
}
|
|
1837
|
-
if (type === "file.edited") {
|
|
1838
|
-
emit({
|
|
1839
|
-
type: "file-change",
|
|
1840
|
-
event: "modify",
|
|
1841
|
-
path: stripWorkDir(String(props.file ?? ""))
|
|
1842
|
-
});
|
|
1843
|
-
return;
|
|
1844
|
-
}
|
|
1845
|
-
if (type === "session.error" || type === "session.next.step.failed") {
|
|
1846
|
-
const error = props.error ?? event;
|
|
1847
|
-
turn.emitError({
|
|
1848
|
-
error,
|
|
1849
|
-
message: type === "session.error" ? "OpenCode session error" : "OpenCode step failed"
|
|
1850
|
-
});
|
|
1851
|
-
return;
|
|
1852
|
-
}
|
|
1853
|
-
if (type === "permission.v2.asked") {
|
|
1854
|
-
await handlePermissionV2({
|
|
1855
|
-
client,
|
|
1856
|
-
sessionId,
|
|
1857
|
-
permissionMode,
|
|
1858
|
-
builtinToolFiltering,
|
|
1859
|
-
turn,
|
|
1860
|
-
emit,
|
|
1861
|
-
event
|
|
1862
|
-
});
|
|
1863
|
-
return;
|
|
1864
|
-
}
|
|
1865
|
-
if (type === "permission.asked") {
|
|
1866
|
-
await handlePermission({
|
|
1867
|
-
client,
|
|
1868
|
-
sessionId,
|
|
1869
|
-
permissionMode,
|
|
1870
|
-
builtinToolFiltering,
|
|
1871
|
-
turn,
|
|
1872
|
-
emit,
|
|
1873
|
-
event
|
|
1874
|
-
});
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
function closeLegacyOpenParts({
|
|
1878
|
-
state,
|
|
1879
|
-
emit
|
|
1880
|
-
}) {
|
|
1881
|
-
for (const id of state.legacyReasoningPartIds) {
|
|
1882
|
-
emit({ type: "reasoning-end", id });
|
|
1883
|
-
state.reasoningDeltas.delete(id);
|
|
1884
|
-
}
|
|
1885
|
-
state.legacyReasoningPartIds.clear();
|
|
1886
|
-
for (const id of state.legacyTextPartIds) {
|
|
1887
|
-
emit({ type: "text-end", id });
|
|
1888
|
-
state.textDeltas.delete(id);
|
|
1889
|
-
}
|
|
1890
|
-
state.legacyTextPartIds.clear();
|
|
1891
|
-
}
|
|
1892
|
-
function emitLegacyStepFinishPart({
|
|
1893
|
-
part,
|
|
1894
|
-
state,
|
|
1895
|
-
emit
|
|
1896
|
-
}) {
|
|
1897
|
-
const event = legacyStepFinishPartToFinishStep(part);
|
|
1898
|
-
if (!event) return false;
|
|
1899
|
-
const id = isRecord3(part) ? stringValue2(part.id) : void 0;
|
|
1900
|
-
if (id) {
|
|
1901
|
-
if (state.legacyStepFinishPartIds.has(id)) return true;
|
|
1902
|
-
state.legacyStepFinishPartIds.add(id);
|
|
1903
|
-
}
|
|
1904
|
-
closeLegacyOpenParts({ state, emit });
|
|
1905
|
-
state.turnUsage = event.usage;
|
|
1906
|
-
emit(event);
|
|
1907
|
-
return true;
|
|
1908
|
-
}
|
|
1909
|
-
function emitLegacyToolPart({
|
|
1910
|
-
part,
|
|
1911
|
-
state,
|
|
1912
|
-
emit
|
|
1913
|
-
}) {
|
|
1914
|
-
if (!part || typeof part !== "object") return;
|
|
1915
|
-
const toolPart = part;
|
|
1916
|
-
if (toolPart.type !== "tool") return;
|
|
1917
|
-
const status = legacyToolPartStatus(toolPart);
|
|
1918
|
-
if (status !== "running" && status !== "completed" && status !== "error") {
|
|
1919
|
-
return;
|
|
1920
|
-
}
|
|
1921
|
-
if (typeof toolPart.tool !== "string" || typeof toolPart.callID !== "string") {
|
|
1922
|
-
return;
|
|
1923
|
-
}
|
|
1924
|
-
const callID = toolPart.callID;
|
|
1925
|
-
const rawToolName = toolPart.tool;
|
|
1926
|
-
const toolName = toWireToolName(rawToolName);
|
|
1927
|
-
state.toolNames.set(callID, { rawToolName, toolName });
|
|
1928
|
-
const hostToolName = getHostToolName(toolName, rawToolName);
|
|
1929
|
-
if (hostToolName) {
|
|
1930
|
-
if (status === "running") {
|
|
1931
|
-
authorizeHostToolCall({
|
|
1932
|
-
callID,
|
|
1933
|
-
toolName: hostToolName,
|
|
1934
|
-
input: legacyToolPartInput(toolPart),
|
|
1935
|
-
state
|
|
1936
|
-
});
|
|
1937
|
-
}
|
|
1938
|
-
return;
|
|
1939
|
-
}
|
|
1940
|
-
if (!state.toolCallsEmitted.has(callID)) {
|
|
1941
|
-
state.toolCallsEmitted.add(callID);
|
|
1942
|
-
emit({
|
|
1943
|
-
type: "tool-call",
|
|
1944
|
-
toolCallId: callID,
|
|
1945
|
-
toolName,
|
|
1946
|
-
...nativeNameField({ nativeName: rawToolName, toolName }),
|
|
1947
|
-
input: JSON.stringify(legacyToolPartInput(toolPart)),
|
|
1948
|
-
providerExecuted: true,
|
|
1949
|
-
...toolPart.provider?.metadata ? { providerMetadata: toolPart.provider.metadata } : {}
|
|
1950
|
-
});
|
|
1951
|
-
}
|
|
1952
|
-
if ((status === "completed" || status === "error") && !state.toolResultsEmitted.has(callID)) {
|
|
1953
|
-
state.toolResultsEmitted.add(callID);
|
|
1954
|
-
emit({
|
|
1955
|
-
type: "tool-result",
|
|
1956
|
-
toolCallId: callID,
|
|
1957
|
-
toolName,
|
|
1958
|
-
result: legacyToolPartOutput(toolPart),
|
|
1959
|
-
...status === "error" ? { isError: true } : {}
|
|
1960
|
-
});
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
function legacyToolPartStatus(part) {
|
|
1964
|
-
return typeof part.state === "string" ? part.state : typeof part.state === "object" && part.state !== null ? String(part.state.status ?? "") : void 0;
|
|
1965
|
-
}
|
|
1966
|
-
function legacyToolPartInput(part) {
|
|
1967
|
-
const state = typeof part.state === "object" && part.state !== null ? part.state : void 0;
|
|
1968
|
-
return {
|
|
1969
|
-
...isRecord3(part.metadata) ? part.metadata : {},
|
|
1970
|
-
...isRecord3(state?.metadata) ? state.metadata : {},
|
|
1971
|
-
...isRecord3(state?.input) ? state.input : {}
|
|
1972
|
-
};
|
|
1973
|
-
}
|
|
1974
|
-
function legacyToolPartOutput(part) {
|
|
1975
|
-
const state = typeof part.state === "object" && part.state !== null ? part.state : void 0;
|
|
1976
|
-
if (state?.status === "error") {
|
|
1977
|
-
return state.error ?? part.error ?? state.result ?? "tool failed";
|
|
1978
|
-
}
|
|
1979
|
-
return state?.output ?? state?.result ?? state?.structured ?? state?.content ?? null;
|
|
1980
|
-
}
|
|
1981
|
-
function isRecord3(value) {
|
|
1982
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1983
|
-
}
|
|
1984
|
-
function parseToolInput(state, props) {
|
|
1985
|
-
const text = state.toolInputs.get(String(props.callID ?? ""));
|
|
1986
|
-
if (!text) return {};
|
|
1987
|
-
try {
|
|
1988
|
-
return JSON.parse(text);
|
|
1989
|
-
} catch {
|
|
1990
|
-
return { input: text };
|
|
2034
|
+
if (onEvent?.(event)) break;
|
|
1991
2035
|
}
|
|
1992
2036
|
}
|
|
1993
2037
|
async function handlePermissionV2({
|
|
@@ -2160,11 +2204,13 @@ function authorizeHostToolCall({
|
|
|
2160
2204
|
async function emitContextFallback({
|
|
2161
2205
|
client,
|
|
2162
2206
|
sessionId,
|
|
2207
|
+
state,
|
|
2163
2208
|
emit,
|
|
2164
2209
|
emitContent
|
|
2165
2210
|
}) {
|
|
2166
2211
|
const assistant = await latestAssistantSnapshot({ client, sessionId });
|
|
2167
2212
|
if (!assistant) return false;
|
|
2213
|
+
emitOpenCodeStreamStart({ info: assistant, state, emit });
|
|
2168
2214
|
if (emitContent && Array.isArray(assistant.contentParts)) {
|
|
2169
2215
|
for (const part of assistant.contentParts) {
|
|
2170
2216
|
emitAssistantContentPart(part, emit);
|
|
@@ -2323,9 +2369,6 @@ function modelRefFromValue(value) {
|
|
|
2323
2369
|
if (!providerID || !modelID) return void 0;
|
|
2324
2370
|
return { providerID, modelID };
|
|
2325
2371
|
}
|
|
2326
|
-
function stringValue2(value) {
|
|
2327
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
2328
|
-
}
|
|
2329
2372
|
function stripWorkDir(file) {
|
|
2330
2373
|
if (!file) return file;
|
|
2331
2374
|
const normalized = path2.resolve(file);
|