@bryti/agent 0.0.1 → 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/Dockerfile +27 -0
- package/README.md +77 -50
- package/config.example.yml +265 -0
- package/dist/active-hours.d.ts +23 -0
- package/dist/active-hours.d.ts.map +1 -0
- package/dist/active-hours.js +68 -0
- package/dist/active-hours.js.map +1 -0
- package/dist/agent.d.ts +84 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +383 -0
- package/dist/agent.js.map +1 -0
- package/dist/channels/markdown/ir.d.ts +79 -0
- package/dist/channels/markdown/ir.d.ts.map +1 -0
- package/dist/channels/markdown/ir.js +824 -0
- package/dist/channels/markdown/ir.js.map +1 -0
- package/dist/channels/markdown/render.d.ts +35 -0
- package/dist/channels/markdown/render.d.ts.map +1 -0
- package/dist/channels/markdown/render.js +178 -0
- package/dist/channels/markdown/render.js.map +1 -0
- package/dist/channels/telegram-network-errors.d.ts +27 -0
- package/dist/channels/telegram-network-errors.d.ts.map +1 -0
- package/dist/channels/telegram-network-errors.js +156 -0
- package/dist/channels/telegram-network-errors.js.map +1 -0
- package/dist/channels/telegram.d.ts +76 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +814 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/types.d.ts +59 -0
- package/dist/channels/types.d.ts.map +1 -0
- package/dist/channels/types.js +9 -0
- package/dist/channels/types.js.map +1 -0
- package/dist/channels/whatsapp.d.ts +45 -0
- package/dist/channels/whatsapp.d.ts.map +1 -0
- package/dist/channels/whatsapp.js +310 -0
- package/dist/channels/whatsapp.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +635 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +35 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +113 -0
- package/dist/commands.js.map +1 -0
- package/dist/compaction/history.d.ts +17 -0
- package/dist/compaction/history.d.ts.map +1 -0
- package/dist/compaction/history.js +35 -0
- package/dist/compaction/history.js.map +1 -0
- package/dist/compaction/index.d.ts +3 -0
- package/dist/compaction/index.d.ts.map +1 -0
- package/dist/compaction/index.js +3 -0
- package/dist/compaction/index.js.map +1 -0
- package/dist/compaction/proactive.d.ts +25 -0
- package/dist/compaction/proactive.d.ts.map +1 -0
- package/dist/compaction/proactive.js +87 -0
- package/dist/compaction/proactive.js.map +1 -0
- package/dist/compaction/transcript-repair.d.ts +55 -0
- package/dist/compaction/transcript-repair.d.ts.map +1 -0
- package/dist/compaction/transcript-repair.js +215 -0
- package/dist/compaction/transcript-repair.js.map +1 -0
- package/dist/config.d.ts +128 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +317 -0
- package/dist/config.js.map +1 -0
- package/dist/crash-recovery.d.ts +23 -0
- package/dist/crash-recovery.d.ts.map +1 -0
- package/dist/crash-recovery.js +96 -0
- package/dist/crash-recovery.js.map +1 -0
- package/dist/defaults/extensions/EXTENSIONS.md +158 -0
- package/dist/defaults/extensions/documents-hedgedoc.ts +153 -0
- package/dist/history.d.ts +31 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +49 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +673 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +39 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +143 -0
- package/dist/logger.js.map +1 -0
- package/dist/memory/conversation-search.d.ts +15 -0
- package/dist/memory/conversation-search.d.ts.map +1 -0
- package/dist/memory/conversation-search.js +60 -0
- package/dist/memory/conversation-search.js.map +1 -0
- package/dist/memory/core-memory.d.ts +28 -0
- package/dist/memory/core-memory.d.ts.map +1 -0
- package/dist/memory/core-memory.js +102 -0
- package/dist/memory/core-memory.js.map +1 -0
- package/dist/memory/embeddings.d.ts +44 -0
- package/dist/memory/embeddings.d.ts.map +1 -0
- package/dist/memory/embeddings.js +139 -0
- package/dist/memory/embeddings.js.map +1 -0
- package/dist/memory/search.d.ts +49 -0
- package/dist/memory/search.d.ts.map +1 -0
- package/dist/memory/search.js +97 -0
- package/dist/memory/search.js.map +1 -0
- package/dist/memory/store.d.ts +32 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +205 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/message-queue.d.ts +73 -0
- package/dist/message-queue.d.ts.map +1 -0
- package/dist/message-queue.js +188 -0
- package/dist/message-queue.js.map +1 -0
- package/dist/model-infra.d.ts +64 -0
- package/dist/model-infra.d.ts.map +1 -0
- package/dist/model-infra.js +202 -0
- package/dist/model-infra.js.map +1 -0
- package/dist/projection/format.d.ts +10 -0
- package/dist/projection/format.d.ts.map +1 -0
- package/dist/projection/format.js +30 -0
- package/dist/projection/format.js.map +1 -0
- package/dist/projection/index.d.ts +11 -0
- package/dist/projection/index.d.ts.map +1 -0
- package/dist/projection/index.js +9 -0
- package/dist/projection/index.js.map +1 -0
- package/dist/projection/reflection.d.ts +94 -0
- package/dist/projection/reflection.d.ts.map +1 -0
- package/dist/projection/reflection.js +334 -0
- package/dist/projection/reflection.js.map +1 -0
- package/dist/projection/store.d.ts +144 -0
- package/dist/projection/store.d.ts.map +1 -0
- package/dist/projection/store.js +519 -0
- package/dist/projection/store.js.map +1 -0
- package/dist/projection/tools.d.ts +11 -0
- package/dist/projection/tools.d.ts.map +1 -0
- package/dist/projection/tools.js +237 -0
- package/dist/projection/tools.js.map +1 -0
- package/dist/scheduler.d.ts +36 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +286 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/system-prompt.d.ts +41 -0
- package/dist/system-prompt.d.ts.map +1 -0
- package/dist/system-prompt.js +162 -0
- package/dist/system-prompt.js.map +1 -0
- package/dist/time.d.ts +52 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +138 -0
- package/dist/time.js.map +1 -0
- package/dist/tools/archival-memory-tool.d.ts +8 -0
- package/dist/tools/archival-memory-tool.d.ts.map +1 -0
- package/dist/tools/archival-memory-tool.js +68 -0
- package/dist/tools/archival-memory-tool.js.map +1 -0
- package/dist/tools/conversation-search-tool.d.ts +6 -0
- package/dist/tools/conversation-search-tool.d.ts.map +1 -0
- package/dist/tools/conversation-search-tool.js +28 -0
- package/dist/tools/conversation-search-tool.js.map +1 -0
- package/dist/tools/core-memory-tool.d.ts +7 -0
- package/dist/tools/core-memory-tool.d.ts.map +1 -0
- package/dist/tools/core-memory-tool.js +59 -0
- package/dist/tools/core-memory-tool.js.map +1 -0
- package/dist/tools/fetch-url.d.ts +15 -0
- package/dist/tools/fetch-url.d.ts.map +1 -0
- package/dist/tools/fetch-url.js +76 -0
- package/dist/tools/fetch-url.js.map +1 -0
- package/dist/tools/files.d.ts +10 -0
- package/dist/tools/files.d.ts.map +1 -0
- package/dist/tools/files.js +127 -0
- package/dist/tools/files.js.map +1 -0
- package/dist/tools/index.d.ts +17 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +118 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/result.d.ts +21 -0
- package/dist/tools/result.d.ts.map +1 -0
- package/dist/tools/result.js +36 -0
- package/dist/tools/result.js.map +1 -0
- package/dist/tools/skill-install.d.ts +17 -0
- package/dist/tools/skill-install.d.ts.map +1 -0
- package/dist/tools/skill-install.js +148 -0
- package/dist/tools/skill-install.js.map +1 -0
- package/dist/tools/web-search.d.ts +42 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +237 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/trust/guardrail.d.ts +60 -0
- package/dist/trust/guardrail.d.ts.map +1 -0
- package/dist/trust/guardrail.js +171 -0
- package/dist/trust/guardrail.js.map +1 -0
- package/dist/trust/index.d.ts +12 -0
- package/dist/trust/index.d.ts.map +1 -0
- package/dist/trust/index.js +12 -0
- package/dist/trust/index.js.map +1 -0
- package/dist/trust/store.d.ts +118 -0
- package/dist/trust/store.d.ts.map +1 -0
- package/dist/trust/store.js +209 -0
- package/dist/trust/store.js.map +1 -0
- package/dist/trust/wrapper.d.ts +36 -0
- package/dist/trust/wrapper.d.ts.map +1 -0
- package/dist/trust/wrapper.js +142 -0
- package/dist/trust/wrapper.js.map +1 -0
- package/dist/usage.d.ts +53 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +124 -0
- package/dist/usage.js.map +1 -0
- package/dist/util/math.d.ts +9 -0
- package/dist/util/math.d.ts.map +1 -0
- package/dist/util/math.js +22 -0
- package/dist/util/math.js.map +1 -0
- package/dist/util/ssrf.d.ts +21 -0
- package/dist/util/ssrf.d.ts.map +1 -0
- package/dist/util/ssrf.js +77 -0
- package/dist/util/ssrf.js.map +1 -0
- package/dist/workers/index.d.ts +8 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +7 -0
- package/dist/workers/index.js.map +1 -0
- package/dist/workers/registry.d.ts +53 -0
- package/dist/workers/registry.d.ts.map +1 -0
- package/dist/workers/registry.js +38 -0
- package/dist/workers/registry.js.map +1 -0
- package/dist/workers/scoped-tools.d.ts +21 -0
- package/dist/workers/scoped-tools.d.ts.map +1 -0
- package/dist/workers/scoped-tools.js +111 -0
- package/dist/workers/scoped-tools.js.map +1 -0
- package/dist/workers/spawn.d.ts +62 -0
- package/dist/workers/spawn.d.ts.map +1 -0
- package/dist/workers/spawn.js +314 -0
- package/dist/workers/spawn.js.map +1 -0
- package/dist/workers/tools.d.ts +26 -0
- package/dist/workers/tools.d.ts.map +1 -0
- package/dist/workers/tools.js +380 -0
- package/dist/workers/tools.js.map +1 -0
- package/docker-compose.yml +72 -0
- package/package.json +16 -1
- package/run.sh +27 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash command handling and activity logging.
|
|
3
|
+
*
|
|
4
|
+
* Handles /clear, /memory, /log, and /restart commands.
|
|
5
|
+
* Builds the human-readable activity log from tool-calls.jsonl.
|
|
6
|
+
*/
|
|
7
|
+
import type { IncomingMessage } from "./channels/types.js";
|
|
8
|
+
import type { Config } from "./config.js";
|
|
9
|
+
import type { CoreMemory } from "./memory/core-memory.js";
|
|
10
|
+
import type { HistoryManager } from "./history.js";
|
|
11
|
+
/**
|
|
12
|
+
* Human-readable labels for the /log output. Tool names never leak to the user.
|
|
13
|
+
*/
|
|
14
|
+
export declare const TOOL_DESCRIPTIONS: Record<string, string>;
|
|
15
|
+
/**
|
|
16
|
+
* Build a human-readable activity summary from the tool call log.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildActivityLog(dataDir: string, userId: string, timezone: string): string;
|
|
19
|
+
export interface SlashCommandContext {
|
|
20
|
+
config: Config;
|
|
21
|
+
coreMemory: CoreMemory;
|
|
22
|
+
historyManager: HistoryManager;
|
|
23
|
+
/** Callback to dispose and delete a user session. */
|
|
24
|
+
disposeSession: (userId: string) => void;
|
|
25
|
+
/** Send a message to the user. */
|
|
26
|
+
sendMessage: (channelId: string, text: string) => Promise<string>;
|
|
27
|
+
/** Trigger a restart. */
|
|
28
|
+
triggerRestart: (msg: IncomingMessage, reason: string) => Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if the incoming message is a slash command and handle it.
|
|
32
|
+
* Returns true if the message was a command (and was handled), false otherwise.
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleSlashCommand(msg: IncomingMessage, context: SlashCommandContext): Promise<boolean>;
|
|
35
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiBpD,CAAC;AASF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiD1F;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,cAAc,CAAC;IAC/B,qDAAqD;IACrD,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,kCAAkC;IAClC,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE,yBAAyB;IACzB,cAAc,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAuClB"}
|
package/dist/commands.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash command handling and activity logging.
|
|
3
|
+
*
|
|
4
|
+
* Handles /clear, /memory, /log, and /restart commands.
|
|
5
|
+
* Builds the human-readable activity log from tool-calls.jsonl.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { getUserTimezone } from "./time.js";
|
|
10
|
+
/**
|
|
11
|
+
* Human-readable labels for the /log output. Tool names never leak to the user.
|
|
12
|
+
*/
|
|
13
|
+
export const TOOL_DESCRIPTIONS = {
|
|
14
|
+
memory_archival_search: "Searched memory",
|
|
15
|
+
memory_archival_insert: "Saved a memory",
|
|
16
|
+
memory_core_append: "Updated core memory",
|
|
17
|
+
memory_core_replace: "Updated core memory",
|
|
18
|
+
memory_conversation_search: "Searched conversation history",
|
|
19
|
+
projection_create: "Set a reminder",
|
|
20
|
+
projection_resolve: "Resolved a reminder",
|
|
21
|
+
projection_list: "Checked upcoming reminders",
|
|
22
|
+
projection_link: "Linked memory to a reminder",
|
|
23
|
+
worker_dispatch: "Started background research",
|
|
24
|
+
worker_check: "Checked background task",
|
|
25
|
+
worker_interrupt: "Cancelled a background task",
|
|
26
|
+
worker_steer: "Adjusted a background task",
|
|
27
|
+
file_read: "Read a file",
|
|
28
|
+
file_write: "Wrote a file",
|
|
29
|
+
file_list: "Listed files",
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Build a human-readable activity summary from the tool call log.
|
|
33
|
+
*/
|
|
34
|
+
export function buildActivityLog(dataDir, userId, timezone) {
|
|
35
|
+
const logPath = path.join(dataDir, "logs", "tool-calls.jsonl");
|
|
36
|
+
if (!fs.existsSync(logPath)) {
|
|
37
|
+
return "No recent activity on record yet.";
|
|
38
|
+
}
|
|
39
|
+
let entries;
|
|
40
|
+
try {
|
|
41
|
+
const raw = fs.readFileSync(logPath, "utf-8");
|
|
42
|
+
entries = raw
|
|
43
|
+
.split("\n")
|
|
44
|
+
.filter((line) => line.trim())
|
|
45
|
+
.map((line) => {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(line);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
.filter((entry) => entry !== null);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return "Could not read the activity log.";
|
|
57
|
+
}
|
|
58
|
+
// Filter to this user, take the last 20 entries
|
|
59
|
+
const userEntries = entries
|
|
60
|
+
.filter((e) => e.userId === userId)
|
|
61
|
+
.slice(-20);
|
|
62
|
+
if (userEntries.length === 0) {
|
|
63
|
+
return "No recent activity on record yet.";
|
|
64
|
+
}
|
|
65
|
+
const lines = userEntries.map((entry) => {
|
|
66
|
+
const ts = new Date(entry.timestamp);
|
|
67
|
+
const time = ts.toLocaleString("sv-SE", {
|
|
68
|
+
timeZone: timezone,
|
|
69
|
+
hour: "2-digit",
|
|
70
|
+
minute: "2-digit",
|
|
71
|
+
hour12: false,
|
|
72
|
+
});
|
|
73
|
+
const description = TOOL_DESCRIPTIONS[entry.toolName] ?? "Ran a task";
|
|
74
|
+
const detail = entry.args_summary ? `: ${entry.args_summary}` : "";
|
|
75
|
+
return `- ${time} ${description}${detail}`;
|
|
76
|
+
});
|
|
77
|
+
return `Recent activity:\n${lines.join("\n")}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if the incoming message is a slash command and handle it.
|
|
81
|
+
* Returns true if the message was a command (and was handled), false otherwise.
|
|
82
|
+
*/
|
|
83
|
+
export async function handleSlashCommand(msg, context) {
|
|
84
|
+
if (msg.text === "/clear") {
|
|
85
|
+
// Clear the JSONL audit log and dispose the in-memory session so the next
|
|
86
|
+
// message starts fresh (a new session file will be created).
|
|
87
|
+
await context.historyManager.clear();
|
|
88
|
+
context.disposeSession(msg.userId);
|
|
89
|
+
await context.sendMessage(msg.channelId, "Conversation history cleared.");
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (msg.text === "/memory") {
|
|
93
|
+
const memory = context.coreMemory.read();
|
|
94
|
+
if (memory) {
|
|
95
|
+
await context.sendMessage(msg.channelId, `Your core memory:\n\n${memory}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
await context.sendMessage(msg.channelId, "Your core memory is empty. I haven't saved anything yet.");
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (msg.text === "/log") {
|
|
103
|
+
const logText = buildActivityLog(context.config.data_dir, msg.userId, getUserTimezone(context.config));
|
|
104
|
+
await context.sendMessage(msg.channelId, logText);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (msg.text === "/restart") {
|
|
108
|
+
await context.triggerRestart(msg, "user command");
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History turn limiting.
|
|
3
|
+
*
|
|
4
|
+
* Safety valve that limits the session to the last N user turns and their
|
|
5
|
+
* responses. Unlikely to trigger with 200K+ context models, but prevents
|
|
6
|
+
* edge cases where a very long session survives compaction intact.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
9
|
+
/**
|
|
10
|
+
* Return the last `limit` user turns (and their responses) from a message list.
|
|
11
|
+
*
|
|
12
|
+
* A "turn" is a user message plus everything that follows it up to the next
|
|
13
|
+
* user message. If `limit` is undefined, zero, or negative, the original list
|
|
14
|
+
* is returned unchanged.
|
|
15
|
+
*/
|
|
16
|
+
export declare function limitHistoryTurns(messages: AgentMessage[], limit: number | undefined): AgentMessage[];
|
|
17
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/compaction/history.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,EAAE,EACxB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,YAAY,EAAE,CAsBhB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History turn limiting.
|
|
3
|
+
*
|
|
4
|
+
* Safety valve that limits the session to the last N user turns and their
|
|
5
|
+
* responses. Unlikely to trigger with 200K+ context models, but prevents
|
|
6
|
+
* edge cases where a very long session survives compaction intact.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Return the last `limit` user turns (and their responses) from a message list.
|
|
10
|
+
*
|
|
11
|
+
* A "turn" is a user message plus everything that follows it up to the next
|
|
12
|
+
* user message. If `limit` is undefined, zero, or negative, the original list
|
|
13
|
+
* is returned unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export function limitHistoryTurns(messages, limit) {
|
|
16
|
+
if (!limit || limit <= 0 || messages.length === 0) {
|
|
17
|
+
return messages;
|
|
18
|
+
}
|
|
19
|
+
let userCount = 0;
|
|
20
|
+
let cutIndex = messages.length;
|
|
21
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
22
|
+
if (messages[i].role === "user") {
|
|
23
|
+
userCount++;
|
|
24
|
+
if (userCount > limit) {
|
|
25
|
+
// cutIndex is the start of the (limit+1)th user turn from the end,
|
|
26
|
+
// which we want to discard. The next iteration will set it correctly.
|
|
27
|
+
return messages.slice(cutIndex);
|
|
28
|
+
}
|
|
29
|
+
cutIndex = i;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Fewer than limit user turns in the list: return unchanged.
|
|
33
|
+
return messages;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/compaction/history.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAwB,EACxB,KAAyB;IAEzB,IAAI,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC;IAE/B,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAChC,SAAS,EAAE,CAAC;YACZ,IAAI,SAAS,GAAG,KAAK,EAAE,CAAC;gBACtB,mEAAmE;gBACnE,sEAAsE;gBACtE,OAAO,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;YACD,QAAQ,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAA4B,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proactive compaction: idle and nightly.
|
|
3
|
+
*
|
|
4
|
+
* Pi SDK auto-compacts when the context fills, but that's mid-conversation
|
|
5
|
+
* and adds latency. Proactive compaction runs during quiet periods instead:
|
|
6
|
+
*
|
|
7
|
+
* - **Idle compaction**: After 30 minutes of inactivity, compact sessions
|
|
8
|
+
* that are using more than 30% of their context window.
|
|
9
|
+
* - **Nightly compaction**: At 03:00 user timezone, compact all sessions
|
|
10
|
+
* to start the day with a clean slate.
|
|
11
|
+
*/
|
|
12
|
+
import { Cron } from "croner";
|
|
13
|
+
import type { UserSession } from "../agent.js";
|
|
14
|
+
import type { Config } from "../config.js";
|
|
15
|
+
/**
|
|
16
|
+
* Try to compact a session. Skips if already in progress or if the session
|
|
17
|
+
* is too small to bother with.
|
|
18
|
+
*/
|
|
19
|
+
export declare function tryCompact(userSession: UserSession, reason: string): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Start proactive compaction cron jobs.
|
|
22
|
+
* Returns the cron jobs so the caller can stop them on shutdown.
|
|
23
|
+
*/
|
|
24
|
+
export declare function startProactiveCompaction(config: Config, getSessions: () => Map<string, UserSession>): Cron[];
|
|
25
|
+
//# sourceMappingURL=proactive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proactive.d.ts","sourceRoot":"","sources":["../../src/compaction/proactive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAM3C;;;GAGG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyCxF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAC1C,IAAI,EAAE,CA2BR"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proactive compaction: idle and nightly.
|
|
3
|
+
*
|
|
4
|
+
* Pi SDK auto-compacts when the context fills, but that's mid-conversation
|
|
5
|
+
* and adds latency. Proactive compaction runs during quiet periods instead:
|
|
6
|
+
*
|
|
7
|
+
* - **Idle compaction**: After 30 minutes of inactivity, compact sessions
|
|
8
|
+
* that are using more than 30% of their context window.
|
|
9
|
+
* - **Nightly compaction**: At 03:00 user timezone, compact all sessions
|
|
10
|
+
* to start the day with a clean slate.
|
|
11
|
+
*/
|
|
12
|
+
import { Cron } from "croner";
|
|
13
|
+
import { getUserTimezone } from "../time.js";
|
|
14
|
+
const IDLE_THRESHOLD_MS = 30 * 60 * 1000; // 30 minutes
|
|
15
|
+
const IDLE_CONTEXT_THRESHOLD = 30; // percent of context window
|
|
16
|
+
/**
|
|
17
|
+
* Try to compact a session. Skips if already in progress or if the session
|
|
18
|
+
* is too small to bother with.
|
|
19
|
+
*/
|
|
20
|
+
export async function tryCompact(userSession, reason) {
|
|
21
|
+
const { session, userId } = userSession;
|
|
22
|
+
if (session.isCompacting)
|
|
23
|
+
return;
|
|
24
|
+
// Don't compact tiny sessions (system + a couple messages)
|
|
25
|
+
const messageCount = session.messages.length;
|
|
26
|
+
if (messageCount < 6)
|
|
27
|
+
return;
|
|
28
|
+
// Idle compaction: only when context is above threshold. No point compacting
|
|
29
|
+
// a mostly-empty context just because the user stepped away.
|
|
30
|
+
// Nightly compaction always runs (fresh start for the morning).
|
|
31
|
+
if (reason !== "nightly") {
|
|
32
|
+
const usage = session.getContextUsage();
|
|
33
|
+
const percent = usage?.percent ?? 0;
|
|
34
|
+
if (percent < IDLE_CONTEXT_THRESHOLD) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
console.log(`[compaction] proactive ${reason} for user ${userId} (${messageCount} messages)`);
|
|
39
|
+
try {
|
|
40
|
+
const reasonHint = reason === "nightly"
|
|
41
|
+
? "This is a nightly compaction. The user is asleep. " +
|
|
42
|
+
"Summarize the entire day's conversation into a concise recap. " +
|
|
43
|
+
"Tomorrow's session should start clean with full context of what happened today."
|
|
44
|
+
: "The user has been inactive for a while and may return to continue. " +
|
|
45
|
+
"Summarize completed topics but preserve the thread of any ongoing discussion.";
|
|
46
|
+
await session.compact(`${reasonHint} ` +
|
|
47
|
+
"This is a personal assistant conversation. " +
|
|
48
|
+
"Preserve: user preferences, commitments and promises made, ongoing tasks, " +
|
|
49
|
+
"facts learned about the user, decisions made, and any context the user would " +
|
|
50
|
+
"expect the assistant to remember. " +
|
|
51
|
+
"Discard: verbose tool outputs, raw search results, intermediate reasoning, " +
|
|
52
|
+
"and conversational filler.");
|
|
53
|
+
console.log(`[compaction] proactive ${reason} done for user ${userId}`);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.error(`[compaction] proactive ${reason} failed for user ${userId}:`, err.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Start proactive compaction cron jobs.
|
|
61
|
+
* Returns the cron jobs so the caller can stop them on shutdown.
|
|
62
|
+
*/
|
|
63
|
+
export function startProactiveCompaction(config, getSessions) {
|
|
64
|
+
const jobs = [];
|
|
65
|
+
// Check every 10 minutes: compact sessions idle for 30+ minutes
|
|
66
|
+
const idleCheck = new Cron("*/10 * * * *", { timezone: "UTC" }, () => {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
for (const [_userId, userSession] of getSessions()) {
|
|
69
|
+
const idleMs = now - userSession.lastUserMessageAt;
|
|
70
|
+
if (idleMs >= IDLE_THRESHOLD_MS) {
|
|
71
|
+
tryCompact(userSession, "idle").catch(() => { });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
jobs.push(idleCheck);
|
|
76
|
+
// Nightly compaction at 03:00 user timezone (all sessions)
|
|
77
|
+
const tz = getUserTimezone(config);
|
|
78
|
+
const nightlyCompact = new Cron("0 3 * * *", { timezone: tz }, () => {
|
|
79
|
+
for (const [_userId, userSession] of getSessions()) {
|
|
80
|
+
tryCompact(userSession, "nightly").catch(() => { });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
jobs.push(nightlyCompact);
|
|
84
|
+
console.log(`Proactive compaction: idle check every 10 min (threshold: 30 min), nightly at 03:00 ${tz}`);
|
|
85
|
+
return jobs;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=proactive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proactive.js","sourceRoot":"","sources":["../../src/compaction/proactive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript repair for pi session files.
|
|
3
|
+
*
|
|
4
|
+
* Anthropic-compatible providers reject (HTTP 400) requests where assistant
|
|
5
|
+
* tool calls are not immediately followed by matching tool results. Session
|
|
6
|
+
* files can end up malformed in two concrete ways:
|
|
7
|
+
*
|
|
8
|
+
* 1. A partial write during a crash leaves a tool_use block in the
|
|
9
|
+
* assistant message with no corresponding tool_result in the next
|
|
10
|
+
* user turn. The provider sees an unclosed tool call and rejects.
|
|
11
|
+
*
|
|
12
|
+
* 2. A race during session flush writes tool_result messages out of
|
|
13
|
+
* order or duplicated relative to their tool_use. The provider
|
|
14
|
+
* expects results to appear in the same order as their matching
|
|
15
|
+
* calls, so any mismatch causes a 400.
|
|
16
|
+
*
|
|
17
|
+
* Repairs by: reordering results to follow their matching tool call, inserting
|
|
18
|
+
* synthetic error results for missing ones, and dropping duplicates and orphans.
|
|
19
|
+
*/
|
|
20
|
+
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
21
|
+
export type ToolUseRepairReport = {
|
|
22
|
+
messages: AgentMessage[];
|
|
23
|
+
/**
|
|
24
|
+
* Synthetic error results that were inserted for missing tool results.
|
|
25
|
+
* Logged on startup; frequent non-zero values indicate that tool calls are
|
|
26
|
+
* being lost before their results are persisted — a systemic session
|
|
27
|
+
* persistence issue worth investigating.
|
|
28
|
+
*/
|
|
29
|
+
added: Array<Extract<AgentMessage, {
|
|
30
|
+
role: "toolResult";
|
|
31
|
+
}>>;
|
|
32
|
+
/**
|
|
33
|
+
* Number of tool_result messages dropped because a result with the same ID
|
|
34
|
+
* had already been seen. Logged on startup; frequent non-zero values
|
|
35
|
+
* indicate that the session flush is writing results more than once, which
|
|
36
|
+
* is a systemic session persistence issue.
|
|
37
|
+
*/
|
|
38
|
+
droppedDuplicateCount: number;
|
|
39
|
+
/**
|
|
40
|
+
* Number of tool_result messages dropped because no matching tool_use call
|
|
41
|
+
* existed anywhere in the transcript. Logged on startup; frequent non-zero
|
|
42
|
+
* values indicate that tool_use messages are being lost while their results
|
|
43
|
+
* survive, again a systemic session persistence issue.
|
|
44
|
+
*/
|
|
45
|
+
droppedOrphanCount: number;
|
|
46
|
+
/** Whether any messages were reordered or changed. */
|
|
47
|
+
changed: boolean;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Repair tool-call/tool-result pairing in a message list. Returns the
|
|
51
|
+
* repaired list and a report. If nothing needed fixing, returns the
|
|
52
|
+
* original array unchanged (same reference).
|
|
53
|
+
*/
|
|
54
|
+
export declare function repairToolUseResultPairing(messages: AgentMessage[]): ToolUseRepairReport;
|
|
55
|
+
//# sourceMappingURL=transcript-repair.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-repair.d.ts","sourceRoot":"","sources":["../../src/compaction/transcript-repair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAoFhE,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB;;;;;OAKG;IACH,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE;QAAE,IAAI,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5D;;;;;OAKG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,mBAAmB,CAuIxF"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript repair for pi session files.
|
|
3
|
+
*
|
|
4
|
+
* Anthropic-compatible providers reject (HTTP 400) requests where assistant
|
|
5
|
+
* tool calls are not immediately followed by matching tool results. Session
|
|
6
|
+
* files can end up malformed in two concrete ways:
|
|
7
|
+
*
|
|
8
|
+
* 1. A partial write during a crash leaves a tool_use block in the
|
|
9
|
+
* assistant message with no corresponding tool_result in the next
|
|
10
|
+
* user turn. The provider sees an unclosed tool call and rejects.
|
|
11
|
+
*
|
|
12
|
+
* 2. A race during session flush writes tool_result messages out of
|
|
13
|
+
* order or duplicated relative to their tool_use. The provider
|
|
14
|
+
* expects results to appear in the same order as their matching
|
|
15
|
+
* calls, so any mismatch causes a 400.
|
|
16
|
+
*
|
|
17
|
+
* Repairs by: reordering results to follow their matching tool call, inserting
|
|
18
|
+
* synthetic error results for missing ones, and dropping duplicates and orphans.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Extract all tool-call blocks from an assistant message.
|
|
22
|
+
*
|
|
23
|
+
* Checks for three type strings ("toolCall", "toolUse", "functionCall")
|
|
24
|
+
* because different SDK versions and provider adapters use different names
|
|
25
|
+
* for the same concept. Normalising here lets the rest of the repair logic
|
|
26
|
+
* work against a single representation regardless of which adapter produced
|
|
27
|
+
* the session file.
|
|
28
|
+
*/
|
|
29
|
+
function extractToolCallsFromAssistant(msg) {
|
|
30
|
+
const content = msg.content;
|
|
31
|
+
if (!Array.isArray(content)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const toolCalls = [];
|
|
35
|
+
for (const block of content) {
|
|
36
|
+
if (!block || typeof block !== "object") {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const rec = block;
|
|
40
|
+
if (typeof rec.id !== "string" || !rec.id) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (rec.type === "toolCall" || rec.type === "toolUse" || rec.type === "functionCall") {
|
|
44
|
+
toolCalls.push({
|
|
45
|
+
id: rec.id,
|
|
46
|
+
name: typeof rec.name === "string" ? rec.name : undefined,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return toolCalls;
|
|
51
|
+
}
|
|
52
|
+
function extractToolResultId(msg) {
|
|
53
|
+
const toolCallId = msg.toolCallId;
|
|
54
|
+
if (typeof toolCallId === "string" && toolCallId) {
|
|
55
|
+
return toolCallId;
|
|
56
|
+
}
|
|
57
|
+
const toolUseId = msg.toolUseId;
|
|
58
|
+
if (typeof toolUseId === "string" && toolUseId) {
|
|
59
|
+
return toolUseId;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Construct a synthetic tool_result for a tool call that has no matching
|
|
65
|
+
* result in the session file.
|
|
66
|
+
*
|
|
67
|
+
* The content string "[bryti] missing tool result" is intentionally prefixed
|
|
68
|
+
* with the agent name so the model can identify this as a repair artifact
|
|
69
|
+
* rather than a real tool failure. That distinction matters: the model should
|
|
70
|
+
* reason "this call was lost during a crash" rather than "the tool returned
|
|
71
|
+
* an error", which would lead to incorrect retry or error-handling behaviour.
|
|
72
|
+
*/
|
|
73
|
+
function makeMissingToolResult(params) {
|
|
74
|
+
return {
|
|
75
|
+
role: "toolResult",
|
|
76
|
+
toolCallId: params.toolCallId,
|
|
77
|
+
toolName: params.toolName ?? "unknown",
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: "[bryti] missing tool result in session history; inserted synthetic error result for transcript repair.",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
isError: true,
|
|
85
|
+
timestamp: Date.now(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Repair tool-call/tool-result pairing in a message list. Returns the
|
|
90
|
+
* repaired list and a report. If nothing needed fixing, returns the
|
|
91
|
+
* original array unchanged (same reference).
|
|
92
|
+
*/
|
|
93
|
+
export function repairToolUseResultPairing(messages) {
|
|
94
|
+
const out = [];
|
|
95
|
+
const added = [];
|
|
96
|
+
const seenToolResultIds = new Set();
|
|
97
|
+
let droppedDuplicateCount = 0;
|
|
98
|
+
let droppedOrphanCount = 0;
|
|
99
|
+
let changed = false;
|
|
100
|
+
const pushToolResult = (msg) => {
|
|
101
|
+
const id = extractToolResultId(msg);
|
|
102
|
+
if (id && seenToolResultIds.has(id)) {
|
|
103
|
+
droppedDuplicateCount += 1;
|
|
104
|
+
changed = true;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (id) {
|
|
108
|
+
seenToolResultIds.add(id);
|
|
109
|
+
}
|
|
110
|
+
out.push(msg);
|
|
111
|
+
};
|
|
112
|
+
for (let i = 0; i < messages.length; i++) {
|
|
113
|
+
const msg = messages[i];
|
|
114
|
+
if (!msg || typeof msg !== "object") {
|
|
115
|
+
out.push(msg);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const role = msg.role;
|
|
119
|
+
if (role !== "assistant") {
|
|
120
|
+
// Free-floating toolResult entries must be dropped: they cause strict
|
|
121
|
+
// providers to reject the request.
|
|
122
|
+
if (role !== "toolResult") {
|
|
123
|
+
out.push(msg);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
droppedOrphanCount += 1;
|
|
127
|
+
changed = true;
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const assistant = msg;
|
|
132
|
+
// Phase 1: build the expected tool-call sequence from this assistant message.
|
|
133
|
+
const toolCalls = extractToolCallsFromAssistant(assistant);
|
|
134
|
+
if (toolCalls.length === 0) {
|
|
135
|
+
out.push(msg);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const toolCallIds = new Set(toolCalls.map((t) => t.id));
|
|
139
|
+
// Phase 2: collect all tool results (and any non-result messages) that
|
|
140
|
+
// appear between this assistant turn and the next one.
|
|
141
|
+
const spanResultsById = new Map();
|
|
142
|
+
const remainder = [];
|
|
143
|
+
let j = i + 1;
|
|
144
|
+
for (; j < messages.length; j++) {
|
|
145
|
+
const next = messages[j];
|
|
146
|
+
if (!next || typeof next !== "object") {
|
|
147
|
+
remainder.push(next);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const nextRole = next.role;
|
|
151
|
+
if (nextRole === "assistant") {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
if (nextRole === "toolResult") {
|
|
155
|
+
const toolResult = next;
|
|
156
|
+
const id = extractToolResultId(toolResult);
|
|
157
|
+
if (id && toolCallIds.has(id)) {
|
|
158
|
+
if (seenToolResultIds.has(id)) {
|
|
159
|
+
droppedDuplicateCount += 1;
|
|
160
|
+
changed = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (spanResultsById.has(id)) {
|
|
164
|
+
// Duplicate within this span
|
|
165
|
+
droppedDuplicateCount += 1;
|
|
166
|
+
changed = true;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
spanResultsById.set(id, toolResult);
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (next.role !== "toolResult") {
|
|
175
|
+
remainder.push(next);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// Phase 5 (inline): drop orphan results that have no matching call.
|
|
179
|
+
droppedOrphanCount += 1;
|
|
180
|
+
changed = true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
out.push(msg);
|
|
184
|
+
if (spanResultsById.size > 0 && remainder.length > 0) {
|
|
185
|
+
changed = true;
|
|
186
|
+
}
|
|
187
|
+
// Phase 3: emit results in call order (reordering any that were out of
|
|
188
|
+
// sequence). Phase 4: for any call with no matching result, insert a
|
|
189
|
+
// synthetic error result so the transcript is structurally valid.
|
|
190
|
+
for (const call of toolCalls) {
|
|
191
|
+
const existing = spanResultsById.get(call.id);
|
|
192
|
+
if (existing) {
|
|
193
|
+
pushToolResult(existing);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const missing = makeMissingToolResult({ toolCallId: call.id, toolName: call.name });
|
|
197
|
+
added.push(missing);
|
|
198
|
+
changed = true;
|
|
199
|
+
pushToolResult(missing);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
for (const rem of remainder) {
|
|
203
|
+
out.push(rem);
|
|
204
|
+
}
|
|
205
|
+
i = j - 1;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
messages: changed ? out : messages,
|
|
209
|
+
added,
|
|
210
|
+
droppedDuplicateCount,
|
|
211
|
+
droppedOrphanCount,
|
|
212
|
+
changed,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=transcript-repair.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-repair.js","sourceRoot":"","sources":["../../src/compaction/transcript-repair.ts"],"names":[],"mappings":"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"}
|