[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
84
infrastructure/ai/harness/toolMessageIntegrity.ts
Normal file
84
infrastructure/ai/harness/toolMessageIntegrity.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { ModelMessage } from 'ai';
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
|
||||
export function repairToolMessageIntegrity(messages: ModelMessage[]): {
|
||||
messages: ModelMessage[];
|
||||
didAdjust: boolean;
|
||||
} {
|
||||
const pendingCalls = new Map<string, Array<Record<string, unknown>>>();
|
||||
const matchedCalls = new Set<Record<string, unknown>>();
|
||||
let didAdjust = false;
|
||||
const sanitized: ModelMessage[] = [];
|
||||
for (const message of messages) {
|
||||
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
||||
for (const part of message.content as unknown[]) {
|
||||
if (!isRecord(part) || part.type !== 'tool-call' || typeof part.toolCallId !== 'string') continue;
|
||||
const pending = pendingCalls.get(part.toolCallId) ?? [];
|
||||
pending.push(part);
|
||||
pendingCalls.set(part.toolCallId, pending);
|
||||
}
|
||||
sanitized.push(message);
|
||||
continue;
|
||||
}
|
||||
if (message.role !== 'tool' || !Array.isArray(message.content)) {
|
||||
sanitized.push(message);
|
||||
continue;
|
||||
}
|
||||
const content = (message.content as unknown[]).filter(part => {
|
||||
if (!isRecord(part) || part.type !== 'tool-result' || typeof part.toolCallId !== 'string') {
|
||||
return true;
|
||||
}
|
||||
const pending = pendingCalls.get(part.toolCallId);
|
||||
// Reused provider IDs are paired with the nearest preceding unresolved
|
||||
// call. This also handles an older interrupted call followed by a new
|
||||
// call that reused the same ID.
|
||||
const matchingCall = pending?.pop();
|
||||
if (!matchingCall) {
|
||||
didAdjust = true;
|
||||
return false;
|
||||
}
|
||||
matchedCalls.add(matchingCall);
|
||||
return true;
|
||||
});
|
||||
if (content.length === 0) {
|
||||
didAdjust = true;
|
||||
continue;
|
||||
}
|
||||
sanitized.push(content.length === message.content.length ? message : {
|
||||
...message,
|
||||
content,
|
||||
} as ModelMessage);
|
||||
}
|
||||
|
||||
const repaired: ModelMessage[] = [];
|
||||
for (const message of sanitized) {
|
||||
repaired.push(message);
|
||||
if (message.role !== 'assistant' || !Array.isArray(message.content)) continue;
|
||||
const missing = (message.content as unknown[]).filter(part => (
|
||||
isRecord(part)
|
||||
&& part.type === 'tool-call'
|
||||
&& typeof part.toolCallId === 'string'
|
||||
&& !matchedCalls.has(part)
|
||||
)) as Array<Record<string, unknown>>;
|
||||
if (missing.length === 0) continue;
|
||||
repaired.push({
|
||||
role: 'tool',
|
||||
content: missing.map(part => ({
|
||||
type: 'tool-result' as const,
|
||||
toolCallId: String(part.toolCallId),
|
||||
toolName: typeof part.toolName === 'string' ? part.toolName : 'unknown',
|
||||
output: {
|
||||
type: 'text' as const,
|
||||
value: '[Tool call interrupted before a result was recorded. Do not assume it succeeded or repeat a write automatically; verify current state first.]',
|
||||
},
|
||||
isError: true,
|
||||
})),
|
||||
});
|
||||
didAdjust = true;
|
||||
}
|
||||
|
||||
return { messages: didAdjust ? repaired : messages, didAdjust };
|
||||
}
|
||||
Reference in New Issue
Block a user