[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

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
import type { ModelMessage } from 'ai';
import { estimateModelMessagesTokensWithKind } from './tokenEstimator';
import { COMPACTION_PROMPT_RESERVE } from './contextBudget';
function endsWithToolCall(message: ModelMessage | undefined): boolean {
if (!message || message.role !== 'assistant' || !Array.isArray(message.content)) return false;
return message.content.some((part) => {
return part && typeof part === 'object' && (part as { type?: string }).type === 'tool-call';
});
}
function startsWithToolResult(message: ModelMessage | undefined): boolean {
if (!message || message.role !== 'tool') return false;
if (!Array.isArray(message.content)) return true;
return message.content.some((part) => {
return part && typeof part === 'object' && (part as { type?: string }).type === 'tool-result';
});
}
function skipToolResultsForward(messages: ModelMessage[], startIndex: number): number {
let index = startIndex;
while (index < messages.length && startsWithToolResult(messages[index])) index += 1;
return index;
}
function findToolResultsStart(messages: ModelMessage[]): number {
let index = messages.length;
while (index > 0 && startsWithToolResult(messages[index - 1])) index -= 1;
return index;
}
/** Prune from the tail while preserving valid tool-call/tool-result pairing. */
export function pruneLastModelMessage(messages: ModelMessage[]): ModelMessage[] {
if (messages.length === 0) return messages;
if (messages.length === 1) return [];
const trailingToolStart = findToolResultsStart(messages);
if (trailingToolStart < messages.length) {
const preceding = messages[trailingToolStart - 1];
return preceding?.role === 'assistant' && endsWithToolCall(preceding)
? messages.slice(0, trailingToolStart - 1)
: messages.slice(0, trailingToolStart);
}
const secondToLastIndex = messages.length - 2;
const secondToLast = messages[secondToLastIndex];
if (secondToLast.role === 'assistant' && endsWithToolCall(secondToLast)) {
return messages.slice(0, -2);
}
if (secondToLast.role === 'user') {
return messages.slice(0, -2);
}
return messages.slice(0, -1);
}
/** Prune from the head while preserving valid tool-call/tool-result pairing. */
export function pruneFirstModelMessage(messages: ModelMessage[]): ModelMessage[] {
if (messages.length === 0) return messages;
if (messages.length === 1) return [];
const first = messages[0];
const second = messages[1];
if (first.role === 'assistant' && endsWithToolCall(first)) {
return messages.slice(skipToolResultsForward(messages, 1));
}
if (first.role === 'user' && second?.role === 'assistant' && endsWithToolCall(second)) {
return messages.slice(skipToolResultsForward(messages, 2));
}
if (first.role === 'user' && second?.role === 'assistant') {
return messages.slice(2);
}
if (startsWithToolResult(first)) {
return messages.slice(skipToolResultsForward(messages, 0));
}
return messages.slice(1);
}
export function countMessagesTokens(messages: ModelMessage[], providerId?: string | null): number {
return estimateModelMessagesTokensWithKind({ messages, providerId }).tokens;
}
export interface PruneUntilFitsCompactionInput {
messages: ModelMessage[];
availableForInput: number;
providerId?: string | null;
compactionPromptTokens?: number;
}
export function pruneUntilFitsCompaction(input: PruneUntilFitsCompactionInput): ModelMessage[] {
const reserve = input.compactionPromptTokens ?? COMPACTION_PROMPT_RESERVE;
let working = input.messages;
while (working.length > 0) {
const tokens = countMessagesTokens(working, input.providerId) + reserve;
if (tokens <= input.availableForInput) {
return working;
}
const pruned = pruneFirstModelMessage(working);
if (pruned.length === working.length) break;
working = pruned;
}
return working;
}