@tangle-network/agent-app 0.43.53 → 0.43.55
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/README.md +41 -35
- package/dist/assistant/index.d.ts +2 -2
- package/dist/assistant/index.js +3 -3
- package/dist/{attachment-validation-DX2KIzMC.d.ts → attachment-validation-D0csjCvz.d.ts} +1 -1
- package/dist/chat-routes/index.d.ts +12 -9
- package/dist/chat-routes/index.js +328 -146
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +2 -2
- package/dist/chat-store/index.js +1 -1
- package/dist/{chunk-3EKOSBYL.js → chunk-K63OWUDJ.js} +2 -2
- package/dist/{chunk-EIG7ZQW2.js → chunk-RVUISYUE.js} +15 -3
- package/dist/chunk-RVUISYUE.js.map +1 -0
- package/dist/{chunk-JGYOYY5D.js → chunk-XKBIYSSM.js} +1 -1
- package/dist/chunk-XKBIYSSM.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{parts-IB-Kbb7z.d.ts → parts-1sRYSPjF.d.ts} +58 -1
- package/dist/web-react/index.d.ts +16 -4
- package/dist/web-react/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-EIG7ZQW2.js.map +0 -1
- package/dist/chunk-JGYOYY5D.js.map +0 -1
- /package/dist/{chunk-3EKOSBYL.js.map → chunk-K63OWUDJ.js.map} +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createSandboxFileIndexRoute,
|
|
12
12
|
sanitizeAttachmentFileName,
|
|
13
13
|
sniffBinary
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-K63OWUDJ.js";
|
|
15
15
|
import {
|
|
16
16
|
DEFAULT_STALE_TURN_LOCK_GRACE_MS,
|
|
17
17
|
DEFAULT_TERMINAL_TURN_LOCK_GRACE_MS,
|
|
@@ -54,21 +54,25 @@ import {
|
|
|
54
54
|
promptPartsByteSize,
|
|
55
55
|
toChatMessageParts,
|
|
56
56
|
validateSandboxMentionPath
|
|
57
|
-
} from "../chunk-
|
|
57
|
+
} from "../chunk-XKBIYSSM.js";
|
|
58
58
|
import {
|
|
59
59
|
asRecord,
|
|
60
60
|
asString,
|
|
61
61
|
finalizeAssistantParts,
|
|
62
|
+
finalizePendingInteractionParts,
|
|
62
63
|
getPartKey,
|
|
63
64
|
mergePersistedPart,
|
|
64
65
|
normalizePersistedPart,
|
|
65
|
-
normalizeToolEvent
|
|
66
|
+
normalizeToolEvent,
|
|
67
|
+
terminalizeDanglingAssistantToolUpdates
|
|
66
68
|
} from "../chunk-5MG74GVQ.js";
|
|
67
69
|
import {
|
|
68
70
|
cancelStatusFor,
|
|
69
71
|
interactionPartKey,
|
|
70
72
|
interactionToPersistedPart,
|
|
71
73
|
isRenderableInteractionKind,
|
|
74
|
+
noticePart,
|
|
75
|
+
noticePartKey,
|
|
72
76
|
parseInteractionCancel,
|
|
73
77
|
parseInteractionRequest
|
|
74
78
|
} from "../chunk-XAWFPMAR.js";
|
|
@@ -573,6 +577,72 @@ function usageFromStepFinish(part, usage) {
|
|
|
573
577
|
const cost = Number(part.cost);
|
|
574
578
|
if (Number.isFinite(cost)) usage.costUsd = (usage.costUsd ?? 0) + cost;
|
|
575
579
|
}
|
|
580
|
+
function toFiniteNumber(value) {
|
|
581
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
582
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
583
|
+
const parsed = Number(value);
|
|
584
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
585
|
+
}
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
function extractReportedTurnUsage(data) {
|
|
589
|
+
const tokenUsage = asRecord(data?.tokenUsage);
|
|
590
|
+
if (!tokenUsage) return null;
|
|
591
|
+
const inputTokens = toFiniteNumber(tokenUsage.inputTokens);
|
|
592
|
+
const outputTokens = toFiniteNumber(tokenUsage.outputTokens);
|
|
593
|
+
if (inputTokens === null || outputTokens === null) return null;
|
|
594
|
+
const reported = { inputTokens, outputTokens };
|
|
595
|
+
const reasoningTokens = toFiniteNumber(tokenUsage.reasoningTokens);
|
|
596
|
+
if (reasoningTokens !== null) reported.reasoningTokens = reasoningTokens;
|
|
597
|
+
const cacheReadTokens = toFiniteNumber(tokenUsage.cacheReadInputTokens);
|
|
598
|
+
if (cacheReadTokens !== null) reported.cacheReadTokens = cacheReadTokens;
|
|
599
|
+
const cacheWriteTokens = toFiniteNumber(tokenUsage.cacheCreationInputTokens);
|
|
600
|
+
if (cacheWriteTokens !== null) reported.cacheWriteTokens = cacheWriteTokens;
|
|
601
|
+
const costUsd = toFiniteNumber(data?.totalCostUsd) ?? toFiniteNumber(tokenUsage.cost);
|
|
602
|
+
if (costUsd !== null) reported.costUsd = costUsd;
|
|
603
|
+
return reported;
|
|
604
|
+
}
|
|
605
|
+
function applyTerminalUsage(reported, usage) {
|
|
606
|
+
usage.inputTokens = reported.inputTokens;
|
|
607
|
+
usage.outputTokens = reported.outputTokens;
|
|
608
|
+
if (reported.reasoningTokens !== void 0) usage.reasoningTokens = reported.reasoningTokens;
|
|
609
|
+
if (reported.cacheReadTokens !== void 0) usage.cacheReadTokens = reported.cacheReadTokens;
|
|
610
|
+
if (reported.cacheWriteTokens !== void 0) usage.cacheWriteTokens = reported.cacheWriteTokens;
|
|
611
|
+
if (reported.costUsd !== void 0) usage.costUsd = reported.costUsd;
|
|
612
|
+
}
|
|
613
|
+
function sandboxStreamErrorMessage(data) {
|
|
614
|
+
const record = asRecord(data);
|
|
615
|
+
const message = asString(record?.message) ?? asString(record?.error);
|
|
616
|
+
if (message) return message;
|
|
617
|
+
try {
|
|
618
|
+
return JSON.stringify(data) || "Sandbox stream returned an error event without details.";
|
|
619
|
+
} catch {
|
|
620
|
+
return "Sandbox stream returned an error event without details.";
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function toProducerWireEvent(event) {
|
|
624
|
+
return event;
|
|
625
|
+
}
|
|
626
|
+
function sandboxStreamFailureDiagnostic(error) {
|
|
627
|
+
const streamMessage = error?.streamMessage;
|
|
628
|
+
const message = typeof streamMessage === "string" ? streamMessage : error instanceof Error ? error.message : "Sandbox stream failed";
|
|
629
|
+
const diagnostics = asRecord(error?.diagnostics);
|
|
630
|
+
let diagnosticText;
|
|
631
|
+
if (diagnostics) {
|
|
632
|
+
try {
|
|
633
|
+
diagnosticText = JSON.stringify(diagnostics);
|
|
634
|
+
} catch {
|
|
635
|
+
diagnosticText = "[unserializable diagnostics]";
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const userMessage = [
|
|
639
|
+
"The sandbox model stream stopped before a clean completion.",
|
|
640
|
+
`Error: ${message}`,
|
|
641
|
+
"Please retry. If it repeats, send these support details to the team."
|
|
642
|
+
].join("\n\n");
|
|
643
|
+
const failureNote = diagnosticText ? `sandbox-stream: ${message}; ${diagnosticText}` : `sandbox-stream: ${message}`;
|
|
644
|
+
return { userMessage, failureNote };
|
|
645
|
+
}
|
|
576
646
|
function createSandboxChatProducer(options) {
|
|
577
647
|
const log = options.log ?? ((message, meta) => console.error(message, meta ?? ""));
|
|
578
648
|
const renderable = options.isRenderableInteraction ?? isRenderableInteractionKind;
|
|
@@ -584,6 +654,9 @@ function createSandboxChatProducer(options) {
|
|
|
584
654
|
const announcedTools = /* @__PURE__ */ new Set();
|
|
585
655
|
const settledTools = /* @__PURE__ */ new Set();
|
|
586
656
|
let stepCounter = 0;
|
|
657
|
+
let danglingToolsTerminalized = false;
|
|
658
|
+
let interactionOutcome = "answered";
|
|
659
|
+
let warningCount = 0;
|
|
587
660
|
const promotedFileParts = /* @__PURE__ */ new Map();
|
|
588
661
|
function recordPersistedPart(part, delta, keyOverride) {
|
|
589
662
|
const persisted = normalizePersistedPart(part);
|
|
@@ -592,179 +665,288 @@ function createSandboxChatProducer(options) {
|
|
|
592
665
|
if (!partMap.has(key)) partOrder.push(key);
|
|
593
666
|
partMap.set(key, mergePersistedPart(partMap.get(key), persisted, delta));
|
|
594
667
|
}
|
|
668
|
+
function* emitTerminalizedTools() {
|
|
669
|
+
if (danglingToolsTerminalized) return;
|
|
670
|
+
danglingToolsTerminalized = true;
|
|
671
|
+
const updates = terminalizeDanglingAssistantToolUpdates(partOrder, partMap, fullText);
|
|
672
|
+
for (const part of updates) {
|
|
673
|
+
const toolId = asString(part.id);
|
|
674
|
+
if (!toolId || settledTools.has(toolId)) continue;
|
|
675
|
+
settledTools.add(toolId);
|
|
676
|
+
const state = asRecord(part.state);
|
|
677
|
+
yield {
|
|
678
|
+
type: "tool_result",
|
|
679
|
+
toolCallId: toolId,
|
|
680
|
+
toolName: asString(part.tool) ?? "tool",
|
|
681
|
+
outcome: {
|
|
682
|
+
ok: false,
|
|
683
|
+
...asString(state?.error) ? { message: asString(state?.error) } : {}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
function* emitFinalUsage() {
|
|
689
|
+
const promptTokens = usage.inputTokens ?? 0;
|
|
690
|
+
const completionTokens = usage.outputTokens ?? 0;
|
|
691
|
+
if (promptTokens || completionTokens) {
|
|
692
|
+
yield { type: "usage", usage: { promptTokens, completionTokens } };
|
|
693
|
+
}
|
|
694
|
+
}
|
|
595
695
|
async function* stream() {
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (
|
|
612
|
-
|
|
696
|
+
try {
|
|
697
|
+
for await (const raw of options.events) {
|
|
698
|
+
const record = asRecord(raw);
|
|
699
|
+
if (!record || typeof record.type !== "string") continue;
|
|
700
|
+
const normalized = normalizeToolEvent({ type: record.type, data: asRecord(record.data) });
|
|
701
|
+
const event = normalized.type === "message.part.updated" ? normalized : { type: record.type, data: asRecord(record.data) };
|
|
702
|
+
if (event.type === "message.part.updated") {
|
|
703
|
+
const part = asRecord(event.data?.part);
|
|
704
|
+
if (!part) continue;
|
|
705
|
+
const rawDelta = event.data?.delta;
|
|
706
|
+
const partType = String(part.type ?? "");
|
|
707
|
+
if (partType === "text" || partType === "reasoning") {
|
|
708
|
+
const key = getPartKey(part);
|
|
709
|
+
const delta = textDelta(tracker, key, part, rawDelta);
|
|
710
|
+
recordPersistedPart(part, delta || void 0);
|
|
711
|
+
if (delta) {
|
|
712
|
+
if (partType === "text") fullText += delta;
|
|
713
|
+
yield { type: partType, text: delta };
|
|
714
|
+
}
|
|
715
|
+
continue;
|
|
613
716
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
717
|
+
if (partType === "tool") {
|
|
718
|
+
recordPersistedPart(part, void 0);
|
|
719
|
+
const persisted = partMap.get(getPartKey(part));
|
|
720
|
+
const state = asRecord(persisted?.state);
|
|
721
|
+
const toolId = String(persisted?.id ?? "");
|
|
722
|
+
const toolName = String(persisted?.tool ?? "tool");
|
|
723
|
+
if (toolId && !announcedTools.has(toolId)) {
|
|
724
|
+
announcedTools.add(toolId);
|
|
725
|
+
yield {
|
|
726
|
+
type: "tool_call",
|
|
727
|
+
call: { toolCallId: toolId, toolName, args: asRecord(state?.input) ?? {} }
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
const status = String(state?.status ?? "");
|
|
731
|
+
if (toolId && (status === "completed" || status === "error") && !settledTools.has(toolId)) {
|
|
732
|
+
settledTools.add(toolId);
|
|
733
|
+
yield {
|
|
734
|
+
type: "tool_result",
|
|
735
|
+
toolCallId: toolId,
|
|
736
|
+
toolName,
|
|
737
|
+
outcome: {
|
|
738
|
+
ok: status === "completed",
|
|
739
|
+
...state?.output !== void 0 ? { result: state.output } : {},
|
|
740
|
+
...asString(state?.error) ? { message: asString(state?.error) } : {}
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
continue;
|
|
628
745
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
...state?.output !== void 0 ? { result: state.output } : {},
|
|
639
|
-
...asString(state?.error) ? { message: asString(state?.error) } : {}
|
|
640
|
-
}
|
|
641
|
-
};
|
|
746
|
+
if (partType === "step-finish") {
|
|
747
|
+
usageFromStepFinish(part, usage);
|
|
748
|
+
recordPersistedPart(part, void 0, `step-finish:#${stepCounter++}`);
|
|
749
|
+
const promptTokens = usage.inputTokens ?? 0;
|
|
750
|
+
const completionTokens = usage.outputTokens ?? 0;
|
|
751
|
+
if (promptTokens || completionTokens) {
|
|
752
|
+
yield { type: "usage", usage: { promptTokens, completionTokens } };
|
|
753
|
+
}
|
|
754
|
+
continue;
|
|
642
755
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
usageFromStepFinish(part, usage);
|
|
647
|
-
recordPersistedPart(part, void 0, `step-finish:#${stepCounter++}`);
|
|
648
|
-
const promptTokens = usage.inputTokens ?? 0;
|
|
649
|
-
const completionTokens = usage.outputTokens ?? 0;
|
|
650
|
-
if (promptTokens || completionTokens) {
|
|
651
|
-
yield { type: "usage", usage: { promptTokens, completionTokens } };
|
|
756
|
+
if (partType === "step-start") {
|
|
757
|
+
recordPersistedPart(part, void 0, `step-start:#${stepCounter}`);
|
|
758
|
+
continue;
|
|
652
759
|
}
|
|
760
|
+
if (partType === "file" && options.promoteFilePart) {
|
|
761
|
+
const promote = options.promoteFilePart;
|
|
762
|
+
const rawId = asString(part.id);
|
|
763
|
+
const rawUrl = asString(part.url);
|
|
764
|
+
const memoKey = rawId ? `id:${rawId}` : rawUrl ? `url:${rawUrl}` : void 0;
|
|
765
|
+
const attempt = () => Promise.resolve().then(() => promote(part)).catch((err) => {
|
|
766
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
767
|
+
log("[chat-routes] file part promotion threw", { key: memoKey ?? "(keyless)", error: reason });
|
|
768
|
+
return { succeeded: false, reason };
|
|
769
|
+
});
|
|
770
|
+
let pending;
|
|
771
|
+
if (memoKey) {
|
|
772
|
+
pending = promotedFileParts.get(memoKey) ?? attempt();
|
|
773
|
+
promotedFileParts.set(memoKey, pending);
|
|
774
|
+
} else {
|
|
775
|
+
pending = attempt();
|
|
776
|
+
}
|
|
777
|
+
const outcome = await pending;
|
|
778
|
+
if (outcome.succeeded) {
|
|
779
|
+
recordPersistedPart(outcome.part, void 0, outcome.key);
|
|
780
|
+
} else if (outcome.part) {
|
|
781
|
+
recordPersistedPart(outcome.part, void 0, outcome.key);
|
|
782
|
+
} else {
|
|
783
|
+
recordPersistedPart(part, void 0);
|
|
784
|
+
}
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
recordPersistedPart(part, void 0);
|
|
653
788
|
continue;
|
|
654
789
|
}
|
|
655
|
-
if (
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const promote = options.promoteFilePart;
|
|
661
|
-
const rawId = asString(part.id);
|
|
662
|
-
const rawUrl = asString(part.url);
|
|
663
|
-
const memoKey = rawId ? `id:${rawId}` : rawUrl ? `url:${rawUrl}` : void 0;
|
|
664
|
-
const attempt = () => Promise.resolve().then(() => promote(part)).catch((err) => {
|
|
665
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
666
|
-
log("[chat-routes] file part promotion threw", { key: memoKey ?? "(keyless)", error: reason });
|
|
667
|
-
return { succeeded: false, reason };
|
|
668
|
-
});
|
|
669
|
-
let pending;
|
|
670
|
-
if (memoKey) {
|
|
671
|
-
pending = promotedFileParts.get(memoKey) ?? attempt();
|
|
672
|
-
promotedFileParts.set(memoKey, pending);
|
|
673
|
-
} else {
|
|
674
|
-
pending = attempt();
|
|
790
|
+
if (event.type === "interaction") {
|
|
791
|
+
const parsed = parseInteractionRequest(asRecord(record.data));
|
|
792
|
+
if (!parsed.succeeded) {
|
|
793
|
+
log("[chat-routes] dropping malformed interaction event", { error: parsed.error });
|
|
794
|
+
continue;
|
|
675
795
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
796
|
+
if (renderable(parsed.value.kind)) {
|
|
797
|
+
recordPersistedPart(
|
|
798
|
+
interactionToPersistedPart(parsed.value, "pending"),
|
|
799
|
+
void 0,
|
|
800
|
+
interactionPartKey(parsed.value.id)
|
|
801
|
+
);
|
|
802
|
+
yield toProducerWireEvent(record);
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
let declineFailed = false;
|
|
806
|
+
if (options.declineInteraction) {
|
|
807
|
+
try {
|
|
808
|
+
await options.declineInteraction(parsed.value.id);
|
|
809
|
+
} catch (err) {
|
|
810
|
+
declineFailed = true;
|
|
811
|
+
log("[chat-routes] failed to auto-decline interaction", {
|
|
812
|
+
id: parsed.value.id,
|
|
813
|
+
error: err instanceof Error ? err.message : String(err)
|
|
814
|
+
});
|
|
815
|
+
}
|
|
681
816
|
} else {
|
|
682
|
-
|
|
817
|
+
declineFailed = true;
|
|
818
|
+
log("[chat-routes] non-renderable interaction with no declineInteraction wired", {
|
|
819
|
+
id: parsed.value.id,
|
|
820
|
+
kind: parsed.value.kind
|
|
821
|
+
});
|
|
683
822
|
}
|
|
823
|
+
const text = declineFailed ? `The agent requested ${parsed.value.kind} approval; declining it failed \u2014 it will expire on its own.` : `The agent requested ${parsed.value.kind} approval \u2014 auto-declined by policy.`;
|
|
824
|
+
const notice = noticePart("auto-declined", `auto-declined-${parsed.value.id}`, text);
|
|
825
|
+
recordPersistedPart(notice, void 0, noticePartKey(notice.id));
|
|
826
|
+
yield { type: "notice", id: notice.id, noticeKind: "auto-declined", text };
|
|
684
827
|
continue;
|
|
685
828
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
829
|
+
if (event.type === "interaction.cancel") {
|
|
830
|
+
const parsed = parseInteractionCancel(asRecord(record.data));
|
|
831
|
+
if (!parsed.succeeded) {
|
|
832
|
+
log("[chat-routes] dropping malformed interaction.cancel event", { error: parsed.error });
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
const key = interactionPartKey(parsed.value.id);
|
|
836
|
+
const existing = partMap.get(key);
|
|
837
|
+
if (existing?.type === "interaction" && existing.status === "pending") {
|
|
838
|
+
recordPersistedPart({
|
|
839
|
+
...existing,
|
|
840
|
+
status: cancelStatusFor(parsed.value.reason),
|
|
841
|
+
...parsed.value.reason ? { cancelReason: parsed.value.reason } : {}
|
|
842
|
+
}, void 0, key);
|
|
843
|
+
}
|
|
844
|
+
yield toProducerWireEvent(record);
|
|
693
845
|
continue;
|
|
694
846
|
}
|
|
695
|
-
if (
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
847
|
+
if (event.type === "plan.submitted") {
|
|
848
|
+
const parsed = parsePlanSubmittedEvent(record);
|
|
849
|
+
if (!parsed.succeeded) {
|
|
850
|
+
log("[chat-routes] dropping malformed plan.submitted event", { error: parsed.error });
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
recordPersistedPart(planToPersistedPart(parsed.value), void 0);
|
|
854
|
+
yield toProducerWireEvent(record);
|
|
702
855
|
continue;
|
|
703
856
|
}
|
|
704
|
-
if (
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
857
|
+
if (event.type === "warning") {
|
|
858
|
+
const message = asString(event.data?.message);
|
|
859
|
+
if (message) {
|
|
860
|
+
warningCount += 1;
|
|
861
|
+
const code = asString(event.data?.code);
|
|
862
|
+
const text = code ? `${code}: ${message}` : message;
|
|
863
|
+
const notice = noticePart("warning", `warning-${warningCount}`, text);
|
|
864
|
+
recordPersistedPart(notice, void 0, noticePartKey(notice.id));
|
|
865
|
+
yield { type: "notice", id: notice.id, noticeKind: "warning", text };
|
|
712
866
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
id: parsed.value.id,
|
|
716
|
-
kind: parsed.value.kind
|
|
717
|
-
});
|
|
867
|
+
yield toProducerWireEvent(record);
|
|
868
|
+
continue;
|
|
718
869
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
870
|
+
if (event.type === "result") {
|
|
871
|
+
const finalText = asString(event.data?.finalText);
|
|
872
|
+
if (finalText) fullText = finalText;
|
|
873
|
+
const reported = extractReportedTurnUsage(asRecord(event.data));
|
|
874
|
+
if (reported) {
|
|
875
|
+
applyTerminalUsage(reported, usage);
|
|
876
|
+
yield* emitFinalUsage();
|
|
877
|
+
} else {
|
|
878
|
+
const resultUsage = asRecord(event.data?.usage);
|
|
879
|
+
if (resultUsage) {
|
|
880
|
+
const input = Number(resultUsage.inputTokens);
|
|
881
|
+
const output = Number(resultUsage.outputTokens);
|
|
882
|
+
if (Number.isFinite(input)) usage.inputTokens = input;
|
|
883
|
+
if (Number.isFinite(output)) usage.outputTokens = output;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
yield* emitTerminalizedTools();
|
|
725
887
|
continue;
|
|
726
888
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
889
|
+
if (event.type === "done") {
|
|
890
|
+
const reported = extractReportedTurnUsage(asRecord(event.data));
|
|
891
|
+
if (reported) {
|
|
892
|
+
applyTerminalUsage(reported, usage);
|
|
893
|
+
yield* emitFinalUsage();
|
|
894
|
+
}
|
|
895
|
+
yield* emitTerminalizedTools();
|
|
896
|
+
yield toProducerWireEvent(record);
|
|
897
|
+
continue;
|
|
735
898
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
899
|
+
if (event.type === "error") {
|
|
900
|
+
const message = sandboxStreamErrorMessage(event.data);
|
|
901
|
+
const errorContent = fullText.trim() ? `The sandbox model stream stopped before a clean completion.
|
|
902
|
+
|
|
903
|
+
Error: ${message}` : `The sandbox agent returned an error before producing a visible answer.
|
|
904
|
+
|
|
905
|
+
Error: ${message}`;
|
|
906
|
+
const errorDelta = fullText ? `
|
|
907
|
+
|
|
908
|
+
---
|
|
909
|
+
${errorContent}` : errorContent;
|
|
910
|
+
fullText += errorDelta;
|
|
911
|
+
interactionOutcome = "expired";
|
|
912
|
+
yield { type: "text", text: errorDelta };
|
|
913
|
+
yield* emitTerminalizedTools();
|
|
914
|
+
yield toProducerWireEvent(record);
|
|
743
915
|
continue;
|
|
744
916
|
}
|
|
745
|
-
|
|
746
|
-
yield event;
|
|
747
|
-
continue;
|
|
917
|
+
yield toProducerWireEvent(record);
|
|
748
918
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
919
|
+
} catch (streamErr) {
|
|
920
|
+
const diagnostic = sandboxStreamFailureDiagnostic(streamErr);
|
|
921
|
+
log("[chat-routes] sandbox stream failed", {
|
|
922
|
+
failureNote: diagnostic.failureNote,
|
|
923
|
+
error: streamErr instanceof Error ? streamErr.message : String(streamErr)
|
|
924
|
+
});
|
|
925
|
+
const errorDelta = fullText ? `
|
|
926
|
+
|
|
927
|
+
---
|
|
928
|
+
${diagnostic.userMessage}` : diagnostic.userMessage;
|
|
929
|
+
fullText += errorDelta;
|
|
930
|
+
interactionOutcome = "expired";
|
|
931
|
+
yield { type: "text", text: errorDelta };
|
|
932
|
+
yield* emitTerminalizedTools();
|
|
933
|
+
yield {
|
|
934
|
+
type: "error",
|
|
935
|
+
data: {
|
|
936
|
+
message: diagnostic.userMessage,
|
|
937
|
+
code: "sandbox.stream_failed",
|
|
938
|
+
details: { failureNote: diagnostic.failureNote }
|
|
758
939
|
}
|
|
759
|
-
|
|
760
|
-
}
|
|
761
|
-
yield event;
|
|
940
|
+
};
|
|
762
941
|
}
|
|
763
942
|
}
|
|
764
943
|
return {
|
|
765
944
|
stream: stream(),
|
|
766
945
|
finalText: () => fullText,
|
|
767
|
-
assistantParts: () =>
|
|
946
|
+
assistantParts: () => finalizePendingInteractionParts(
|
|
947
|
+
finalizeAssistantParts(partOrder, partMap, fullText),
|
|
948
|
+
interactionOutcome
|
|
949
|
+
),
|
|
768
950
|
usage: () => usage,
|
|
769
951
|
...options.model ? { model: options.model } : {}
|
|
770
952
|
};
|