longer-agent 0.1.0
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/LICENSE +21 -0
- package/README.md +227 -0
- package/README.zh-CN.md +227 -0
- package/agent_templates/executor/agent.yaml +22 -0
- package/agent_templates/executor/system_prompt.md +17 -0
- package/agent_templates/explorer/agent.yaml +13 -0
- package/agent_templates/explorer/system_prompt.md +19 -0
- package/agent_templates/main/agent.yaml +7 -0
- package/agent_templates/main/system_prompt.md +45 -0
- package/configExample.yaml +83 -0
- package/dist/agents/agent.d.ts +79 -0
- package/dist/agents/agent.d.ts.map +1 -0
- package/dist/agents/agent.js +156 -0
- package/dist/agents/agent.js.map +1 -0
- package/dist/agents/tool-loop.d.ts +140 -0
- package/dist/agents/tool-loop.d.ts.map +1 -0
- package/dist/agents/tool-loop.js +465 -0
- package/dist/agents/tool-loop.js.map +1 -0
- package/dist/ask.d.ts +81 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +34 -0
- package/dist/ask.js.map +1 -0
- package/dist/auth/openai-oauth.d.ts +66 -0
- package/dist/auth/openai-oauth.d.ts.map +1 -0
- package/dist/auth/openai-oauth.js +640 -0
- package/dist/auth/openai-oauth.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +254 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +118 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +862 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +130 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +648 -0
- package/dist/config.js.map +1 -0
- package/dist/context-rendering.d.ts +69 -0
- package/dist/context-rendering.d.ts.map +1 -0
- package/dist/context-rendering.js +250 -0
- package/dist/context-rendering.js.map +1 -0
- package/dist/document-projection.d.ts +12 -0
- package/dist/document-projection.d.ts.map +1 -0
- package/dist/document-projection.js +75 -0
- package/dist/document-projection.js.map +1 -0
- package/dist/ephemeral-log.d.ts +15 -0
- package/dist/ephemeral-log.d.ts.map +1 -0
- package/dist/ephemeral-log.js +173 -0
- package/dist/ephemeral-log.js.map +1 -0
- package/dist/file-attach.d.ts +89 -0
- package/dist/file-attach.d.ts.map +1 -0
- package/dist/file-attach.js +571 -0
- package/dist/file-attach.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/init-wizard.d.ts +13 -0
- package/dist/init-wizard.d.ts.map +1 -0
- package/dist/init-wizard.js +328 -0
- package/dist/init-wizard.js.map +1 -0
- package/dist/log-entry.d.ts +104 -0
- package/dist/log-entry.d.ts.map +1 -0
- package/dist/log-entry.js +292 -0
- package/dist/log-entry.js.map +1 -0
- package/dist/log-projection.d.ts +73 -0
- package/dist/log-projection.d.ts.map +1 -0
- package/dist/log-projection.js +651 -0
- package/dist/log-projection.js.map +1 -0
- package/dist/mcp-client.d.ts +55 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +402 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/model-selection.d.ts +16 -0
- package/dist/model-selection.d.ts.map +1 -0
- package/dist/model-selection.js +181 -0
- package/dist/model-selection.js.map +1 -0
- package/dist/network-retry.d.ts +38 -0
- package/dist/network-retry.d.ts.map +1 -0
- package/dist/network-retry.js +140 -0
- package/dist/network-retry.js.map +1 -0
- package/dist/persistence.d.ts +104 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +644 -0
- package/dist/persistence.js.map +1 -0
- package/dist/primitives/context.d.ts +29 -0
- package/dist/primitives/context.d.ts.map +1 -0
- package/dist/primitives/context.js +85 -0
- package/dist/primitives/context.js.map +1 -0
- package/dist/progress.d.ts +51 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +229 -0
- package/dist/progress.js.map +1 -0
- package/dist/provider-presets.d.ts +34 -0
- package/dist/provider-presets.d.ts.map +1 -0
- package/dist/provider-presets.js +181 -0
- package/dist/provider-presets.js.map +1 -0
- package/dist/providers/anthropic.d.ts +32 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +450 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +135 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +104 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/glm.d.ts +18 -0
- package/dist/providers/glm.d.ts.map +1 -0
- package/dist/providers/glm.js +59 -0
- package/dist/providers/glm.js.map +1 -0
- package/dist/providers/kimi.d.ts +23 -0
- package/dist/providers/kimi.d.ts.map +1 -0
- package/dist/providers/kimi.js +89 -0
- package/dist/providers/kimi.js.map +1 -0
- package/dist/providers/minimax.d.ts +20 -0
- package/dist/providers/minimax.d.ts.map +1 -0
- package/dist/providers/minimax.js +192 -0
- package/dist/providers/minimax.js.map +1 -0
- package/dist/providers/openai-chat.d.ts +33 -0
- package/dist/providers/openai-chat.d.ts.map +1 -0
- package/dist/providers/openai-chat.js +543 -0
- package/dist/providers/openai-chat.js.map +1 -0
- package/dist/providers/openai-responses.d.ts +26 -0
- package/dist/providers/openai-responses.d.ts.map +1 -0
- package/dist/providers/openai-responses.js +443 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/openrouter.d.ts +24 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +177 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/registry.d.ts +7 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +38 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/security/path.d.ts +51 -0
- package/dist/security/path.d.ts.map +1 -0
- package/dist/security/path.js +187 -0
- package/dist/security/path.js.map +1 -0
- package/dist/security/sensitive-files.d.ts +3 -0
- package/dist/security/sensitive-files.d.ts.map +1 -0
- package/dist/security/sensitive-files.js +41 -0
- package/dist/security/sensitive-files.js.map +1 -0
- package/dist/session.d.ts +446 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +4595 -0
- package/dist/session.js.map +1 -0
- package/dist/settings.d.ts +46 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +134 -0
- package/dist/settings.js.map +1 -0
- package/dist/show-context.d.ts +35 -0
- package/dist/show-context.d.ts.map +1 -0
- package/dist/show-context.js +320 -0
- package/dist/show-context.js.map +1 -0
- package/dist/skills/loader.d.ts +49 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +166 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/summarize-context.d.ts +29 -0
- package/dist/summarize-context.d.ts.map +1 -0
- package/dist/summarize-context.js +247 -0
- package/dist/summarize-context.js.map +1 -0
- package/dist/templates/loader.d.ts +104 -0
- package/dist/templates/loader.d.ts.map +1 -0
- package/dist/templates/loader.js +514 -0
- package/dist/templates/loader.js.map +1 -0
- package/dist/tools/basic.d.ts +29 -0
- package/dist/tools/basic.d.ts.map +1 -0
- package/dist/tools/basic.js +2079 -0
- package/dist/tools/basic.js.map +1 -0
- package/dist/tools/comm.d.ts +17 -0
- package/dist/tools/comm.d.ts.map +1 -0
- package/dist/tools/comm.js +192 -0
- package/dist/tools/comm.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +11 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +237 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +24 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +51 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tui/app.d.ts +35 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +1042 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/checkbox-picker.d.ts +35 -0
- package/dist/tui/checkbox-picker.d.ts.map +1 -0
- package/dist/tui/checkbox-picker.js +85 -0
- package/dist/tui/checkbox-picker.js.map +1 -0
- package/dist/tui/command-picker.d.ts +31 -0
- package/dist/tui/command-picker.d.ts.map +1 -0
- package/dist/tui/command-picker.js +113 -0
- package/dist/tui/command-picker.js.map +1 -0
- package/dist/tui/components/ask-panel.d.ts +21 -0
- package/dist/tui/components/ask-panel.d.ts.map +1 -0
- package/dist/tui/components/ask-panel.js +81 -0
- package/dist/tui/components/ask-panel.js.map +1 -0
- package/dist/tui/components/conversation-panel.d.ts +68 -0
- package/dist/tui/components/conversation-panel.d.ts.map +1 -0
- package/dist/tui/components/conversation-panel.js +611 -0
- package/dist/tui/components/conversation-panel.js.map +1 -0
- package/dist/tui/components/input-panel.d.ts +27 -0
- package/dist/tui/components/input-panel.d.ts.map +1 -0
- package/dist/tui/components/input-panel.js +725 -0
- package/dist/tui/components/input-panel.js.map +1 -0
- package/dist/tui/components/logo-panel.d.ts +14 -0
- package/dist/tui/components/logo-panel.d.ts.map +1 -0
- package/dist/tui/components/logo-panel.js +37 -0
- package/dist/tui/components/logo-panel.js.map +1 -0
- package/dist/tui/components/plan-panel.d.ts +10 -0
- package/dist/tui/components/plan-panel.d.ts.map +1 -0
- package/dist/tui/components/plan-panel.js +8 -0
- package/dist/tui/components/plan-panel.js.map +1 -0
- package/dist/tui/components/status-bar.d.ts +24 -0
- package/dist/tui/components/status-bar.d.ts.map +1 -0
- package/dist/tui/components/status-bar.js +80 -0
- package/dist/tui/components/status-bar.js.map +1 -0
- package/dist/tui/input/editor-state.d.ts +22 -0
- package/dist/tui/input/editor-state.d.ts.map +1 -0
- package/dist/tui/input/editor-state.js +157 -0
- package/dist/tui/input/editor-state.js.map +1 -0
- package/dist/tui/input/keymap.d.ts +3 -0
- package/dist/tui/input/keymap.d.ts.map +1 -0
- package/dist/tui/input/keymap.js +72 -0
- package/dist/tui/input/keymap.js.map +1 -0
- package/dist/tui/input/paste-slots.d.ts +17 -0
- package/dist/tui/input/paste-slots.d.ts.map +1 -0
- package/dist/tui/input/paste-slots.js +46 -0
- package/dist/tui/input/paste-slots.js.map +1 -0
- package/dist/tui/input/paste.d.ts +15 -0
- package/dist/tui/input/paste.d.ts.map +1 -0
- package/dist/tui/input/paste.js +35 -0
- package/dist/tui/input/paste.js.map +1 -0
- package/dist/tui/input/protocol.d.ts +9 -0
- package/dist/tui/input/protocol.d.ts.map +1 -0
- package/dist/tui/input/protocol.js +387 -0
- package/dist/tui/input/protocol.js.map +1 -0
- package/dist/tui/input/sanitize.d.ts +6 -0
- package/dist/tui/input/sanitize.d.ts.map +1 -0
- package/dist/tui/input/sanitize.js +20 -0
- package/dist/tui/input/sanitize.js.map +1 -0
- package/dist/tui/input/types.d.ts +18 -0
- package/dist/tui/input/types.d.ts.map +1 -0
- package/dist/tui/input/types.js +2 -0
- package/dist/tui/input/types.js.map +1 -0
- package/dist/tui/launch.d.ts +23 -0
- package/dist/tui/launch.d.ts.map +1 -0
- package/dist/tui/launch.js +104 -0
- package/dist/tui/launch.js.map +1 -0
- package/dist/tui/theme.d.ts +20 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +29 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/types.d.ts +136 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +9 -0
- package/dist/tui/types.js.map +1 -0
- package/package.json +76 -0
- package/prompts/sections/agents_md.md +23 -0
- package/prompts/sections/important_log.md +16 -0
- package/prompts/sections/system_mechanisms.md +18 -0
- package/prompts/tools/apply_patch.md +31 -0
- package/prompts/tools/ask.md +18 -0
- package/prompts/tools/bash.md +13 -0
- package/prompts/tools/bash_background.md +9 -0
- package/prompts/tools/bash_output.md +9 -0
- package/prompts/tools/check_status.md +3 -0
- package/prompts/tools/diff.md +5 -0
- package/prompts/tools/edit_file.md +11 -0
- package/prompts/tools/glob.md +7 -0
- package/prompts/tools/grep.md +20 -0
- package/prompts/tools/kill_agent.md +3 -0
- package/prompts/tools/kill_shell.md +5 -0
- package/prompts/tools/list_dir.md +5 -0
- package/prompts/tools/plan.md +252 -0
- package/prompts/tools/read_file.md +9 -0
- package/prompts/tools/show_context.md +12 -0
- package/prompts/tools/skill.md +7 -0
- package/prompts/tools/spawn_agent.md +195 -0
- package/prompts/tools/summarize_context.md +122 -0
- package/prompts/tools/test.md +5 -0
- package/prompts/tools/wait.md +17 -0
- package/prompts/tools/web_fetch.md +9 -0
- package/prompts/tools/web_search.md +5 -0
- package/prompts/tools/write_file.md +11 -0
- package/skills/.staging/.gitkeep +0 -0
- package/skills/explain-code/SKILL.md +15 -0
- package/skills/skill-manager/SKILL.md +83 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log projection functions — derive TUI entries and API messages from the log.
|
|
3
|
+
*
|
|
4
|
+
* Both real-time conversation and resume use the same projection logic,
|
|
5
|
+
* guaranteeing 100% consistency.
|
|
6
|
+
*/
|
|
7
|
+
import { mergeConsecutiveSameRole } from "./context-rendering.js";
|
|
8
|
+
import { truncateSummaryText } from "./summarize-context.js";
|
|
9
|
+
// ------------------------------------------------------------------
|
|
10
|
+
// TuiDisplayKind → ConversationEntryKind mapping
|
|
11
|
+
// ------------------------------------------------------------------
|
|
12
|
+
const DISPLAY_KIND_TO_ENTRY_KIND = {
|
|
13
|
+
user: "user",
|
|
14
|
+
assistant: "assistant",
|
|
15
|
+
reasoning: "reasoning",
|
|
16
|
+
progress: "progress",
|
|
17
|
+
tool_call: "tool_call",
|
|
18
|
+
status: "status",
|
|
19
|
+
error: "error",
|
|
20
|
+
compact_mark: "compact_mark",
|
|
21
|
+
tool_result: "tool_result",
|
|
22
|
+
};
|
|
23
|
+
const INTERRUPTED_MARKER_TEXT = "[Interrupted here.]";
|
|
24
|
+
const INTERRUPTED_MARKER_SUFFIX = ` ${INTERRUPTED_MARKER_TEXT}`;
|
|
25
|
+
const PRIMARY_ROUND_ENTRY_TYPES = new Set([
|
|
26
|
+
"assistant_text",
|
|
27
|
+
"reasoning",
|
|
28
|
+
"tool_call",
|
|
29
|
+
"tool_result",
|
|
30
|
+
]);
|
|
31
|
+
function isHiddenSubAgentLifecycle(entry) {
|
|
32
|
+
return entry.type === "sub_agent_start";
|
|
33
|
+
}
|
|
34
|
+
function isProjectableTuiEntry(entry) {
|
|
35
|
+
if (entry.discarded)
|
|
36
|
+
return false;
|
|
37
|
+
if (!entry.tuiVisible)
|
|
38
|
+
return false;
|
|
39
|
+
if (entry.type === "summary")
|
|
40
|
+
return false;
|
|
41
|
+
if (isHiddenSubAgentLifecycle(entry))
|
|
42
|
+
return false;
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
function toConversationEntry(entry, toolElapsedMap) {
|
|
46
|
+
if (entry.type === "sub_agent_end") {
|
|
47
|
+
const subAgentId = entry.meta["subAgentId"];
|
|
48
|
+
const subAgentName = entry.meta["subAgentName"];
|
|
49
|
+
const elapsed = entry.meta["elapsed"];
|
|
50
|
+
const label = [
|
|
51
|
+
typeof subAgentId === "number" ? `#${subAgentId}` : "#?",
|
|
52
|
+
typeof subAgentName === "string" ? subAgentName : "sub-agent",
|
|
53
|
+
].join(" ");
|
|
54
|
+
const elapsedStr = typeof elapsed === "number" ? elapsed.toFixed(1) : "?";
|
|
55
|
+
return {
|
|
56
|
+
kind: "sub_agent_done",
|
|
57
|
+
text: `[${label}] [done] (${elapsedStr}s)`,
|
|
58
|
+
id: entry.id,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const kind = entry.displayKind
|
|
62
|
+
? DISPLAY_KIND_TO_ENTRY_KIND[entry.displayKind]
|
|
63
|
+
: "status";
|
|
64
|
+
const ce = {
|
|
65
|
+
kind,
|
|
66
|
+
text: entry.display,
|
|
67
|
+
id: entry.id,
|
|
68
|
+
};
|
|
69
|
+
if (entry.meta["tuiDim"])
|
|
70
|
+
ce.dim = true;
|
|
71
|
+
// Attach timing info for tool_call entries
|
|
72
|
+
if (entry.type === "tool_call") {
|
|
73
|
+
ce.startedAt = entry.timestamp;
|
|
74
|
+
const toolCallId = entry.meta["toolCallId"];
|
|
75
|
+
if (typeof toolCallId === "string" && toolElapsedMap?.has(toolCallId)) {
|
|
76
|
+
ce.elapsedMs = toolElapsedMap.get(toolCallId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return ce;
|
|
80
|
+
}
|
|
81
|
+
function toConversationEntries(entry, toolElapsedMap) {
|
|
82
|
+
const ce = toConversationEntry(entry, toolElapsedMap);
|
|
83
|
+
if (ce.kind !== "assistant") {
|
|
84
|
+
return [ce];
|
|
85
|
+
}
|
|
86
|
+
if (ce.text === INTERRUPTED_MARKER_TEXT) {
|
|
87
|
+
return [
|
|
88
|
+
{
|
|
89
|
+
kind: "interrupted_marker",
|
|
90
|
+
text: INTERRUPTED_MARKER_TEXT,
|
|
91
|
+
id: ce.id,
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
if (!ce.text.endsWith(INTERRUPTED_MARKER_SUFFIX)) {
|
|
96
|
+
return [ce];
|
|
97
|
+
}
|
|
98
|
+
const assistantText = ce.text.slice(0, -INTERRUPTED_MARKER_SUFFIX.length);
|
|
99
|
+
const entries = [];
|
|
100
|
+
if (assistantText.trim().length > 0) {
|
|
101
|
+
entries.push({
|
|
102
|
+
...ce,
|
|
103
|
+
text: assistantText,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
entries.push({
|
|
107
|
+
kind: "interrupted_marker",
|
|
108
|
+
text: INTERRUPTED_MARKER_TEXT,
|
|
109
|
+
id: ce.id ? `${ce.id}:interrupt` : undefined,
|
|
110
|
+
});
|
|
111
|
+
return entries;
|
|
112
|
+
}
|
|
113
|
+
function isPrimaryRoundEntry(entry) {
|
|
114
|
+
return (isProjectableTuiEntry(entry) &&
|
|
115
|
+
entry.roundIndex !== undefined &&
|
|
116
|
+
PRIMARY_ROUND_ENTRY_TYPES.has(entry.type));
|
|
117
|
+
}
|
|
118
|
+
function buildSubAgentRollup(entries) {
|
|
119
|
+
if (entries.length === 0)
|
|
120
|
+
return null;
|
|
121
|
+
const lastFive = entries.slice(-5);
|
|
122
|
+
const omitted = entries.length - lastFive.length;
|
|
123
|
+
const noun = lastFive.length === 1 ? "tool call" : "tool calls";
|
|
124
|
+
const header = omitted > 0
|
|
125
|
+
? `${omitted} earlier ${noun} omitted, last ${lastFive.length}:`
|
|
126
|
+
: `Last ${lastFive.length} sub-agent ${noun}:`;
|
|
127
|
+
return {
|
|
128
|
+
kind: "sub_agent_rollup",
|
|
129
|
+
id: `subrollup-${entries[0].id}`,
|
|
130
|
+
text: [header, ...lastFive.map((entry) => entry.display)].join("\n"),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build a map of toolCallId → elapsed time (ms) by pairing
|
|
135
|
+
* tool_call and tool_result entries.
|
|
136
|
+
*/
|
|
137
|
+
function buildToolElapsedMap(entries) {
|
|
138
|
+
const callTimestamps = new Map();
|
|
139
|
+
const elapsed = new Map();
|
|
140
|
+
for (const entry of entries) {
|
|
141
|
+
if (entry.type === "tool_call") {
|
|
142
|
+
const id = entry.meta["toolCallId"];
|
|
143
|
+
if (typeof id === "string") {
|
|
144
|
+
callTimestamps.set(id, entry.timestamp);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (entry.type === "tool_result") {
|
|
148
|
+
const id = entry.meta["toolCallId"];
|
|
149
|
+
if (typeof id === "string" && callTimestamps.has(id)) {
|
|
150
|
+
elapsed.set(id, entry.timestamp - callTimestamps.get(id));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return elapsed;
|
|
155
|
+
}
|
|
156
|
+
function projectTuiWindow(entries) {
|
|
157
|
+
const result = [];
|
|
158
|
+
const pendingSubAgentCalls = [];
|
|
159
|
+
const toolElapsedMap = buildToolElapsedMap(entries);
|
|
160
|
+
const flushPendingSubAgentCalls = () => {
|
|
161
|
+
const rollup = buildSubAgentRollup(pendingSubAgentCalls);
|
|
162
|
+
pendingSubAgentCalls.length = 0;
|
|
163
|
+
if (rollup)
|
|
164
|
+
result.push(rollup);
|
|
165
|
+
};
|
|
166
|
+
let i = 0;
|
|
167
|
+
while (i < entries.length) {
|
|
168
|
+
const entry = entries[i];
|
|
169
|
+
if (!isProjectableTuiEntry(entry)) {
|
|
170
|
+
i++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (entry.type === "sub_agent_tool_call") {
|
|
174
|
+
pendingSubAgentCalls.push(entry);
|
|
175
|
+
i++;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (isPrimaryRoundEntry(entry)) {
|
|
179
|
+
if (pendingSubAgentCalls.length > 0) {
|
|
180
|
+
flushPendingSubAgentCalls();
|
|
181
|
+
}
|
|
182
|
+
const turnIndex = entry.turnIndex;
|
|
183
|
+
const roundIndex = entry.roundIndex;
|
|
184
|
+
while (i < entries.length) {
|
|
185
|
+
const candidate = entries[i];
|
|
186
|
+
if (!isProjectableTuiEntry(candidate)) {
|
|
187
|
+
i++;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (candidate.type === "sub_agent_tool_call") {
|
|
191
|
+
pendingSubAgentCalls.push(candidate);
|
|
192
|
+
i++;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (candidate.turnIndex === turnIndex &&
|
|
196
|
+
candidate.roundIndex === roundIndex &&
|
|
197
|
+
PRIMARY_ROUND_ENTRY_TYPES.has(candidate.type)) {
|
|
198
|
+
result.push(...toConversationEntries(candidate, toolElapsedMap));
|
|
199
|
+
i++;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
if (pendingSubAgentCalls.length > 0) {
|
|
205
|
+
flushPendingSubAgentCalls();
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (pendingSubAgentCalls.length > 0) {
|
|
210
|
+
flushPendingSubAgentCalls();
|
|
211
|
+
}
|
|
212
|
+
result.push(...toConversationEntries(entry, toolElapsedMap));
|
|
213
|
+
i++;
|
|
214
|
+
}
|
|
215
|
+
if (pendingSubAgentCalls.length > 0) {
|
|
216
|
+
flushPendingSubAgentCalls();
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Project log entries into ConversationEntry[] for TUI rendering.
|
|
222
|
+
*
|
|
223
|
+
* Rules:
|
|
224
|
+
* 1. Determine fold boundary based on compact markers
|
|
225
|
+
* 2. Skip: folded entries, tuiVisible===false, discarded, summary entries
|
|
226
|
+
* 3. Map (displayKind, display) → ConversationEntry
|
|
227
|
+
*/
|
|
228
|
+
export function projectToTuiEntries(entries, options) {
|
|
229
|
+
const threshold = options?.compactFoldThreshold ?? 3;
|
|
230
|
+
// Find all compact_marker indices
|
|
231
|
+
const compactMarkerIndices = [];
|
|
232
|
+
for (let i = 0; i < entries.length; i++) {
|
|
233
|
+
if (entries[i].type === "compact_marker" && !entries[i].discarded) {
|
|
234
|
+
compactMarkerIndices.push(i);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Determine fold boundary: if N >= threshold, fold entries before the (N - threshold + 1)th marker
|
|
238
|
+
let foldEndIdx = -1; // entries at index <= foldEndIdx are folded
|
|
239
|
+
let foldedCount = 0;
|
|
240
|
+
let foldedCompactCount = 0;
|
|
241
|
+
if (compactMarkerIndices.length >= threshold) {
|
|
242
|
+
const foldUpToMarker = compactMarkerIndices[compactMarkerIndices.length - threshold];
|
|
243
|
+
foldEndIdx = foldUpToMarker;
|
|
244
|
+
foldedCount = projectTuiWindow(entries.slice(0, foldEndIdx + 1)).length;
|
|
245
|
+
foldedCompactCount = compactMarkerIndices.length - threshold + 1;
|
|
246
|
+
}
|
|
247
|
+
const result = [];
|
|
248
|
+
// Add fold placeholder if needed
|
|
249
|
+
if (foldEndIdx >= 0 && foldedCount > 0) {
|
|
250
|
+
result.push({
|
|
251
|
+
kind: "status",
|
|
252
|
+
text: `\u25b8 ${foldedCount} earlier entries (${foldedCompactCount} compacts)`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
result.push(...projectTuiWindow(entries.slice(foldEndIdx + 1)));
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Project log entries into InternalMessage[] for provider consumption.
|
|
260
|
+
*
|
|
261
|
+
* Algorithm:
|
|
262
|
+
* 1. Re-render system prompt (or use log's)
|
|
263
|
+
* 2. Find last compact_marker → API window start
|
|
264
|
+
* 3. Insert compact_context if present
|
|
265
|
+
* 4. Iterate entries, skip: apiRole===null, summarized, discarded, archived with null content
|
|
266
|
+
* 5. Group by roundIndex to build assistant messages
|
|
267
|
+
*/
|
|
268
|
+
export function projectToApiMessages(entries, options) {
|
|
269
|
+
// Step 1: Find system prompt
|
|
270
|
+
let systemPromptContent = "";
|
|
271
|
+
for (const e of entries) {
|
|
272
|
+
if (e.type === "system_prompt" && !e.discarded) {
|
|
273
|
+
systemPromptContent = options?.systemPrompt ?? e.content;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Step 2: Find last compact_marker → window start
|
|
278
|
+
let windowStartIdx = 0;
|
|
279
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
280
|
+
if (entries[i].type === "compact_marker" && !entries[i].discarded) {
|
|
281
|
+
windowStartIdx = i + 1;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Step 3: Find compact_context for the current window
|
|
286
|
+
let compactContextContent = null;
|
|
287
|
+
let compactContextId;
|
|
288
|
+
for (let i = windowStartIdx; i < entries.length; i++) {
|
|
289
|
+
const e = entries[i];
|
|
290
|
+
if (e.type === "compact_context" && !e.discarded && !e.summarized) {
|
|
291
|
+
compactContextContent = e.content;
|
|
292
|
+
const ctxId = e.meta["contextId"];
|
|
293
|
+
compactContextId = ctxId !== undefined && ctxId !== null ? String(ctxId) : undefined;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Also check just before the window start (compact_context may be right after compact_marker)
|
|
298
|
+
if (!compactContextContent && windowStartIdx > 0) {
|
|
299
|
+
for (let i = windowStartIdx; i < entries.length && i < windowStartIdx + 5; i++) {
|
|
300
|
+
const e = entries[i];
|
|
301
|
+
if (e.type === "compact_context" && !e.discarded && !e.summarized) {
|
|
302
|
+
compactContextContent = e.content;
|
|
303
|
+
const ctxId = e.meta["contextId"];
|
|
304
|
+
compactContextId = ctxId !== undefined && ctxId !== null ? String(ctxId) : undefined;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Copy annotations map so we can delete entries after first injection per group
|
|
310
|
+
const annotations = options?.showContextAnnotations
|
|
311
|
+
? new Map(options.showContextAnnotations)
|
|
312
|
+
: null;
|
|
313
|
+
// Build messages
|
|
314
|
+
const messages = [];
|
|
315
|
+
// System prompt
|
|
316
|
+
if (systemPromptContent) {
|
|
317
|
+
messages.push({ role: "system", content: systemPromptContent });
|
|
318
|
+
}
|
|
319
|
+
// Compact context (as user message)
|
|
320
|
+
if (compactContextContent) {
|
|
321
|
+
let content = compactContextContent;
|
|
322
|
+
if (compactContextId !== undefined && annotations?.has(compactContextId)) {
|
|
323
|
+
content = prependAnnotation(content, annotations.get(compactContextId));
|
|
324
|
+
annotations.delete(compactContextId);
|
|
325
|
+
}
|
|
326
|
+
const compactMsg = { role: "user", content };
|
|
327
|
+
if (compactContextId !== undefined)
|
|
328
|
+
compactMsg["_context_id"] = compactContextId;
|
|
329
|
+
messages.push(compactMsg);
|
|
330
|
+
}
|
|
331
|
+
// Step 4-5: Collect window entries and group by round
|
|
332
|
+
const windowEntries = entries.slice(windowStartIdx).filter((e) => {
|
|
333
|
+
if (e.summarized)
|
|
334
|
+
return false;
|
|
335
|
+
if (e.discarded)
|
|
336
|
+
return false;
|
|
337
|
+
if (e.archived && e.content === null)
|
|
338
|
+
return false;
|
|
339
|
+
if (e.type === "system_prompt")
|
|
340
|
+
return false; // already handled
|
|
341
|
+
if (e.type === "compact_context")
|
|
342
|
+
return false; // already handled
|
|
343
|
+
// reasoning has apiRole=null but is grouped with assistant entries
|
|
344
|
+
if (e.type === "reasoning")
|
|
345
|
+
return true;
|
|
346
|
+
if (e.apiRole === null)
|
|
347
|
+
return false;
|
|
348
|
+
return true;
|
|
349
|
+
});
|
|
350
|
+
// Group assistant-related entries (assistant_text, reasoning, tool_call, no_reply)
|
|
351
|
+
// by (turnIndex, roundIndex), then emit them as single assistant messages.
|
|
352
|
+
// Non-assistant entries are emitted directly.
|
|
353
|
+
let i = 0;
|
|
354
|
+
while (i < windowEntries.length) {
|
|
355
|
+
const entry = windowEntries[i];
|
|
356
|
+
if ((entry.apiRole === "assistant" || entry.type === "reasoning") &&
|
|
357
|
+
entry.roundIndex !== undefined) {
|
|
358
|
+
// Collect all entries in this round
|
|
359
|
+
const roundIdx = entry.roundIndex;
|
|
360
|
+
const turnIdx = entry.turnIndex;
|
|
361
|
+
const roundEntries = [];
|
|
362
|
+
while (i < windowEntries.length &&
|
|
363
|
+
windowEntries[i].turnIndex === turnIdx &&
|
|
364
|
+
windowEntries[i].roundIndex === roundIdx &&
|
|
365
|
+
(windowEntries[i].apiRole === "assistant" ||
|
|
366
|
+
windowEntries[i].type === "reasoning")) {
|
|
367
|
+
roundEntries.push(windowEntries[i]);
|
|
368
|
+
i++;
|
|
369
|
+
}
|
|
370
|
+
messages.push(buildAssistantMessage(roundEntries, entries));
|
|
371
|
+
}
|
|
372
|
+
else if (entry.apiRole === "user") {
|
|
373
|
+
let content = resolveImageRefs(entry.content, options?.resolveImageRef);
|
|
374
|
+
// Preserve _context_id for spatial index (summarize_context)
|
|
375
|
+
const ctxId = entry.meta["contextId"];
|
|
376
|
+
// Inject show_context annotation if active
|
|
377
|
+
if (ctxId !== undefined && annotations?.has(String(ctxId))) {
|
|
378
|
+
content = prependAnnotation(content, annotations.get(String(ctxId)));
|
|
379
|
+
}
|
|
380
|
+
const userMsg = { role: "user", content };
|
|
381
|
+
if (ctxId !== undefined)
|
|
382
|
+
userMsg["_context_id"] = ctxId;
|
|
383
|
+
// Preserve summary metadata
|
|
384
|
+
if (entry.type === "summary") {
|
|
385
|
+
userMsg["_is_summary"] = true;
|
|
386
|
+
userMsg["_summary_depth"] = entry.meta["summaryDepth"] ?? 1;
|
|
387
|
+
userMsg["_summarized_ids"] = entry.meta["summarizedEntryIds"] ?? [];
|
|
388
|
+
}
|
|
389
|
+
messages.push(userMsg);
|
|
390
|
+
i++;
|
|
391
|
+
}
|
|
392
|
+
else if (entry.apiRole === "tool_result") {
|
|
393
|
+
const resultContent = entry.content;
|
|
394
|
+
let trContent = resultContent.content;
|
|
395
|
+
// Preserve _context_id for spatial index
|
|
396
|
+
const trCtxId = entry.meta["contextId"];
|
|
397
|
+
// Inject show_context annotation into first tool_result per context group
|
|
398
|
+
if (trCtxId !== undefined && annotations?.has(String(trCtxId))) {
|
|
399
|
+
trContent = `${annotations.get(String(trCtxId))}\n\n${trContent}`;
|
|
400
|
+
// Remove from annotations so only the first tool_result per group gets it
|
|
401
|
+
annotations.delete(String(trCtxId));
|
|
402
|
+
}
|
|
403
|
+
// Check for multimodal content blocks in metadata
|
|
404
|
+
const toolMeta = entry.meta["toolMetadata"];
|
|
405
|
+
const contentBlocks = toolMeta?.["_contentBlocks"];
|
|
406
|
+
const trMsg = {
|
|
407
|
+
role: "tool_result",
|
|
408
|
+
tool_call_id: entry.meta.toolCallId,
|
|
409
|
+
tool_name: entry.meta.toolName,
|
|
410
|
+
content: contentBlocks ?? trContent,
|
|
411
|
+
tool_summary: resultContent.toolSummary,
|
|
412
|
+
};
|
|
413
|
+
if (trCtxId !== undefined)
|
|
414
|
+
trMsg["_context_id"] = trCtxId;
|
|
415
|
+
messages.push(trMsg);
|
|
416
|
+
i++;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
// Fallback
|
|
420
|
+
messages.push({ role: entry.apiRole, content: entry.content });
|
|
421
|
+
i++;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// Inject important log if provided
|
|
425
|
+
if (options?.importantLog?.trim()) {
|
|
426
|
+
injectImportantLog(messages, options.importantLog);
|
|
427
|
+
}
|
|
428
|
+
// Inject AGENTS.md content if provided
|
|
429
|
+
if (options?.agentsMd?.trim()) {
|
|
430
|
+
injectAgentsMd(messages, options.agentsMd);
|
|
431
|
+
}
|
|
432
|
+
let projected = options?.truncateSummarizeToolArgs === false
|
|
433
|
+
? messages
|
|
434
|
+
: truncateSummarizeToolArgs(messages);
|
|
435
|
+
if (options?.requiresAlternatingRoles) {
|
|
436
|
+
projected = mergeConsecutiveSameRole(projected);
|
|
437
|
+
}
|
|
438
|
+
return projected;
|
|
439
|
+
}
|
|
440
|
+
// ------------------------------------------------------------------
|
|
441
|
+
// show_context annotation injection
|
|
442
|
+
// ------------------------------------------------------------------
|
|
443
|
+
/**
|
|
444
|
+
* Prepend a show_context annotation to message content.
|
|
445
|
+
* Handles both string content and array content blocks.
|
|
446
|
+
*/
|
|
447
|
+
function prependAnnotation(content, annotation) {
|
|
448
|
+
if (typeof content === "string") {
|
|
449
|
+
return `${annotation}\n\n${content}`;
|
|
450
|
+
}
|
|
451
|
+
if (Array.isArray(content)) {
|
|
452
|
+
const copy = content.map((b) => ({ ...b }));
|
|
453
|
+
// Prepend annotation as a text block
|
|
454
|
+
copy.unshift({ type: "text", text: `${annotation}\n\n` });
|
|
455
|
+
return copy;
|
|
456
|
+
}
|
|
457
|
+
return content;
|
|
458
|
+
}
|
|
459
|
+
// ------------------------------------------------------------------
|
|
460
|
+
// Image ref resolution
|
|
461
|
+
// ------------------------------------------------------------------
|
|
462
|
+
/**
|
|
463
|
+
* Resolve image_ref blocks in content to inline base64 for API consumption.
|
|
464
|
+
* If content is a string or resolver is not provided, returns as-is.
|
|
465
|
+
*/
|
|
466
|
+
function resolveImageRefs(content, resolver) {
|
|
467
|
+
if (!resolver || !Array.isArray(content))
|
|
468
|
+
return content;
|
|
469
|
+
let hasRef = false;
|
|
470
|
+
for (const block of content) {
|
|
471
|
+
if (block &&
|
|
472
|
+
typeof block === "object" &&
|
|
473
|
+
block["type"] === "image_ref") {
|
|
474
|
+
hasRef = true;
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (!hasRef)
|
|
479
|
+
return content;
|
|
480
|
+
return content.map((block) => {
|
|
481
|
+
if (block["type"] !== "image_ref")
|
|
482
|
+
return block;
|
|
483
|
+
const resolved = resolver(block["path"]);
|
|
484
|
+
if (!resolved)
|
|
485
|
+
return block; // fallback: pass through
|
|
486
|
+
return {
|
|
487
|
+
type: "image",
|
|
488
|
+
data: resolved.data,
|
|
489
|
+
media_type: resolved.media_type,
|
|
490
|
+
};
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
// ------------------------------------------------------------------
|
|
494
|
+
// Helpers
|
|
495
|
+
// ------------------------------------------------------------------
|
|
496
|
+
/**
|
|
497
|
+
* Build a single assistant API message from grouped round entries.
|
|
498
|
+
*/
|
|
499
|
+
function buildAssistantMessage(roundEntries, _allEntries) {
|
|
500
|
+
const msg = { role: "assistant" };
|
|
501
|
+
// Extract reasoning
|
|
502
|
+
const reasoning = roundEntries.find((e) => e.type === "reasoning");
|
|
503
|
+
if (reasoning) {
|
|
504
|
+
msg.reasoning_content = reasoning.content;
|
|
505
|
+
if (reasoning.meta.reasoningState !== undefined) {
|
|
506
|
+
msg._reasoning_state = reasoning.meta.reasoningState;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Extract assistant_text
|
|
510
|
+
const text = roundEntries.find((e) => e.type === "assistant_text");
|
|
511
|
+
// Extract tool_calls
|
|
512
|
+
const toolCalls = roundEntries
|
|
513
|
+
.filter((e) => e.type === "tool_call")
|
|
514
|
+
.map((e) => e.content);
|
|
515
|
+
// Extract no_reply
|
|
516
|
+
const noReply = roundEntries.find((e) => e.type === "no_reply");
|
|
517
|
+
if (toolCalls.length > 0) {
|
|
518
|
+
msg.tool_calls = toolCalls;
|
|
519
|
+
if (text) {
|
|
520
|
+
msg.text = text.content;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else if (noReply) {
|
|
524
|
+
msg.content = noReply.content;
|
|
525
|
+
}
|
|
526
|
+
else if (text) {
|
|
527
|
+
msg.content = text.content;
|
|
528
|
+
}
|
|
529
|
+
// Preserve _context_id from the first entry with one
|
|
530
|
+
for (const e of roundEntries) {
|
|
531
|
+
const ctxId = e.meta["contextId"];
|
|
532
|
+
if (ctxId !== undefined) {
|
|
533
|
+
msg["_context_id"] = ctxId;
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return msg;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Inject important log content after the system prompt.
|
|
541
|
+
* Merges into the first user message if possible.
|
|
542
|
+
*/
|
|
543
|
+
function injectImportantLog(messages, logContent) {
|
|
544
|
+
const header = "[IMPORTANT LOG]\n" +
|
|
545
|
+
"The following is your persistent engineering notebook " +
|
|
546
|
+
"(\"(empty file)\" means the notebook is blank — it is not real content):\n\n";
|
|
547
|
+
const fullContent = header + logContent;
|
|
548
|
+
// Find position after system prompt(s)
|
|
549
|
+
let insertIdx = 0;
|
|
550
|
+
while (insertIdx < messages.length && messages[insertIdx].role === "system") {
|
|
551
|
+
insertIdx++;
|
|
552
|
+
}
|
|
553
|
+
if (insertIdx < messages.length && messages[insertIdx].role === "user") {
|
|
554
|
+
// Merge into first user message
|
|
555
|
+
const first = messages[insertIdx];
|
|
556
|
+
messages[insertIdx] = {
|
|
557
|
+
...first,
|
|
558
|
+
content: mergeMessageContent(fullContent, first.content),
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
// Insert standalone user message
|
|
563
|
+
messages.splice(insertIdx, 0, { role: "user", content: fullContent });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Inject AGENTS.md content after the system prompt (and after important log).
|
|
568
|
+
* Merges into the first user message if possible.
|
|
569
|
+
*/
|
|
570
|
+
function injectAgentsMd(messages, agentsMdContent) {
|
|
571
|
+
const header = "[AGENTS.MD — PERSISTENT MEMORY]\n" +
|
|
572
|
+
"The following is your persistent memory across sessions:\n\n";
|
|
573
|
+
const fullContent = header + agentsMdContent;
|
|
574
|
+
// Find position after system prompt(s)
|
|
575
|
+
let insertIdx = 0;
|
|
576
|
+
while (insertIdx < messages.length && messages[insertIdx].role === "system") {
|
|
577
|
+
insertIdx++;
|
|
578
|
+
}
|
|
579
|
+
if (insertIdx < messages.length && messages[insertIdx].role === "user") {
|
|
580
|
+
// Merge into first user message (important log may already be merged there)
|
|
581
|
+
const first = messages[insertIdx];
|
|
582
|
+
messages[insertIdx] = {
|
|
583
|
+
...first,
|
|
584
|
+
content: mergeMessageContent(fullContent, first.content),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
// Insert standalone user message
|
|
589
|
+
messages.splice(insertIdx, 0, { role: "user", content: fullContent });
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function truncateSummarizeToolArgs(messages) {
|
|
593
|
+
return messages.map((msg) => {
|
|
594
|
+
const toolCalls = msg["tool_calls"];
|
|
595
|
+
if (!toolCalls?.length)
|
|
596
|
+
return msg;
|
|
597
|
+
let modified = false;
|
|
598
|
+
const nextToolCalls = toolCalls.map((tc) => {
|
|
599
|
+
if (tc["name"] !== "summarize_context")
|
|
600
|
+
return tc;
|
|
601
|
+
const args = tc["arguments"];
|
|
602
|
+
const operations = args?.["operations"];
|
|
603
|
+
if (!args || !operations?.length)
|
|
604
|
+
return tc;
|
|
605
|
+
let opsModified = false;
|
|
606
|
+
const nextOperations = operations.map((op) => {
|
|
607
|
+
const summary = op["summary"];
|
|
608
|
+
const resultCtxId = op["_result_context_id"];
|
|
609
|
+
if (!summary || summary.length <= 100) {
|
|
610
|
+
if (resultCtxId === undefined)
|
|
611
|
+
return op;
|
|
612
|
+
opsModified = true;
|
|
613
|
+
const { _result_context_id: _removed, ...rest } = op;
|
|
614
|
+
return rest;
|
|
615
|
+
}
|
|
616
|
+
opsModified = true;
|
|
617
|
+
const { _result_context_id: _removed, ...rest } = op;
|
|
618
|
+
return {
|
|
619
|
+
...rest,
|
|
620
|
+
summary: truncateSummaryText(summary, resultCtxId),
|
|
621
|
+
};
|
|
622
|
+
});
|
|
623
|
+
if (!opsModified)
|
|
624
|
+
return tc;
|
|
625
|
+
modified = true;
|
|
626
|
+
return {
|
|
627
|
+
...tc,
|
|
628
|
+
arguments: {
|
|
629
|
+
...args,
|
|
630
|
+
operations: nextOperations,
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
});
|
|
634
|
+
if (!modified)
|
|
635
|
+
return msg;
|
|
636
|
+
return { ...msg, tool_calls: nextToolCalls };
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
function mergeMessageContent(prefix, existing) {
|
|
640
|
+
if (typeof existing === "string") {
|
|
641
|
+
return `${prefix}\n\n${existing}`;
|
|
642
|
+
}
|
|
643
|
+
if (Array.isArray(existing)) {
|
|
644
|
+
return [
|
|
645
|
+
{ type: "text", text: prefix },
|
|
646
|
+
...existing,
|
|
647
|
+
];
|
|
648
|
+
}
|
|
649
|
+
return `${prefix}\n\n${String(existing ?? "")}`;
|
|
650
|
+
}
|
|
651
|
+
//# sourceMappingURL=log-projection.js.map
|