@assemblyline-agents/pi 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.
@@ -0,0 +1,162 @@
1
+ import { estimateTokens, generateSummary } from "@earendil-works/pi-agent-core";
2
+ /**
3
+ * Conversation transcript trimming for the pi harness, layered the way
4
+ * production agents converge on:
5
+ *
6
+ * 1. A lossless-ish cap pass replaces stale tool-result bodies outside the
7
+ * recent tail with a restorable marker (no model call; tool output is
8
+ * re-derivable from the sandbox or by re-running the tool).
9
+ * 2. When the transcript still exceeds `contextWindow - reserveTokens`, older
10
+ * full turns are summarized into a structured context checkpoint via
11
+ * pi-agent-core's `generateSummary`, keeping approximately
12
+ * `keepRecentTokens` of recent messages verbatim and never separating a
13
+ * tool result from its call. Re-compaction feeds the previous summary back
14
+ * in, updating it instead of stacking summaries.
15
+ */
16
+ export const TRANSCRIPT_SUMMARY_PREFIX = "[OpenEve conversation summary]";
17
+ export const DEFAULT_TRANSCRIPT_CONTEXT_WINDOW = 100_000;
18
+ const TOOL_RESULT_CAP_NOTICE = "…[truncated by OpenEve during context trimming. Re-run the tool or read the relevant sandbox file if you need the full output.]";
19
+ /** Cap tool-result text bodies older than the recent-token tail. */
20
+ export function capPiToolResults(messages, keepRecentTokens, toolResultCapChars) {
21
+ const tailStart = tailStartIndex(messages, keepRecentTokens);
22
+ let cappedToolResults = 0;
23
+ const next = messages.map((message, index) => {
24
+ if (index >= tailStart || message.role !== "toolResult" || !Array.isArray(message.content))
25
+ return message;
26
+ let changed = false;
27
+ const content = message.content.map((block) => {
28
+ if (!isRecord(block) || block.type !== "text" || typeof block.text !== "string")
29
+ return block;
30
+ if (block.text.length <= toolResultCapChars + TOOL_RESULT_CAP_NOTICE.length)
31
+ return block;
32
+ changed = true;
33
+ return { ...block, text: `${block.text.slice(0, toolResultCapChars)}${TOOL_RESULT_CAP_NOTICE}` };
34
+ });
35
+ if (!changed)
36
+ return message;
37
+ cappedToolResults += 1;
38
+ // Drop the structured duplicate of the payload alongside the capped text.
39
+ return { ...message, content, details: { truncated: true } };
40
+ });
41
+ return { messages: cappedToolResults > 0 ? next : messages, cappedToolResults };
42
+ }
43
+ export function estimateTranscriptTokens(messages) {
44
+ return messages.reduce((total, message) => total + estimateTokens(message), 0);
45
+ }
46
+ /** Resolve the model's context window for trim gating; unknown models use a conservative default. */
47
+ export function transcriptContextWindow(models, modelSpec) {
48
+ const model = tryResolveModel(models, modelSpec);
49
+ const window = model?.contextWindow;
50
+ return typeof window === "number" && Number.isFinite(window) && window > 0
51
+ ? window
52
+ : DEFAULT_TRANSCRIPT_CONTEXT_WINDOW;
53
+ }
54
+ /**
55
+ * Summarize older full turns into a checkpoint message, keeping the recent
56
+ * tail verbatim. Returns undefined when there is no compactable head (e.g. a
57
+ * single oversized turn). Throws when summarization itself fails so the
58
+ * caller can fall back instead of resuming a transcript that cannot fit.
59
+ */
60
+ export async function compactPiTranscript(input) {
61
+ const model = tryResolveModel(input.models, input.modelSpec);
62
+ if (!model)
63
+ return undefined;
64
+ let start = 0;
65
+ let previousSummary;
66
+ const first = input.messages[0];
67
+ if (first && isSummaryMessage(first)) {
68
+ previousSummary = summaryBody(first);
69
+ start = 1;
70
+ }
71
+ const cut = compactionCutIndex(input.messages, start, input.keepRecentTokens);
72
+ if (cut === undefined)
73
+ return undefined;
74
+ const head = input.messages.slice(start, cut);
75
+ if (head.length === 0)
76
+ return undefined;
77
+ const summary = await generateSummary(head, input.models, model, input.reserveTokens, input.signal, undefined, previousSummary);
78
+ if (!summary.ok) {
79
+ throw new Error(`Transcript compaction failed: ${summary.error.message}`);
80
+ }
81
+ return [summaryMessage(summary.value), ...input.messages.slice(cut)];
82
+ }
83
+ /**
84
+ * The tail-preserving cut: the latest user-message index at or before the
85
+ * recent-token boundary, so the kept tail starts on a turn boundary and every
86
+ * tool result stays with its call. Falls forward to the first user message
87
+ * after the boundary when the head contains none (better a shorter verbatim
88
+ * tail than an uncompactable over-budget transcript).
89
+ */
90
+ function compactionCutIndex(messages, start, keepRecentTokens) {
91
+ const tailStart = tailStartIndex(messages, keepRecentTokens);
92
+ for (let index = tailStart; index > start; index -= 1) {
93
+ if (messages[index]?.role === "user")
94
+ return index;
95
+ }
96
+ for (let index = tailStart + 1; index < messages.length; index += 1) {
97
+ if (messages[index]?.role === "user")
98
+ return index;
99
+ }
100
+ return undefined;
101
+ }
102
+ /**
103
+ * First index of the protected recent tail. The message crossing the token
104
+ * budget is excluded (the verbatim tail stays at or under budget), except the
105
+ * newest message, which is always protected.
106
+ */
107
+ function tailStartIndex(messages, keepRecentTokens) {
108
+ let tokens = 0;
109
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
110
+ const message = messages[index];
111
+ if (!message)
112
+ continue;
113
+ const estimated = estimateTokens(message);
114
+ if (tokens + estimated > keepRecentTokens)
115
+ return Math.min(index + 1, messages.length - 1);
116
+ tokens += estimated;
117
+ }
118
+ return 0;
119
+ }
120
+ function summaryMessage(summary) {
121
+ return {
122
+ role: "user",
123
+ content: [{
124
+ type: "text",
125
+ text: [
126
+ TRANSCRIPT_SUMMARY_PREFIX,
127
+ "Earlier turns of this conversation were compacted into the context checkpoint below. Continue the conversation seamlessly; do not mention this summary.",
128
+ "",
129
+ summary
130
+ ].join("\n")
131
+ }],
132
+ timestamp: Date.now()
133
+ };
134
+ }
135
+ export function isSummaryMessage(message) {
136
+ if (message.role !== "user")
137
+ return false;
138
+ const block = firstContentBlock(message);
139
+ return isRecord(block) && block.type === "text" && typeof block.text === "string" &&
140
+ block.text.startsWith(TRANSCRIPT_SUMMARY_PREFIX);
141
+ }
142
+ function summaryBody(message) {
143
+ const block = firstContentBlock(message);
144
+ if (!isRecord(block) || typeof block.text !== "string")
145
+ return undefined;
146
+ const body = block.text.split("\n").slice(3).join("\n").trim();
147
+ return body.length > 0 ? body : undefined;
148
+ }
149
+ function firstContentBlock(message) {
150
+ const content = message.content;
151
+ return Array.isArray(content) ? content[0] : undefined;
152
+ }
153
+ function tryResolveModel(models, modelSpec) {
154
+ const slash = modelSpec.indexOf("/");
155
+ if (slash <= 0 || slash === modelSpec.length - 1)
156
+ return undefined;
157
+ return models.getModel(modelSpec.slice(0, slash), modelSpec.slice(slash + 1)) ?? undefined;
158
+ }
159
+ function isRecord(value) {
160
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
161
+ }
162
+ //# sourceMappingURL=transcript-trim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript-trim.js","sourceRoot":"","sources":["../src/transcript-trim.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@assemblyline-agents/pi",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "default": "./dist/index.js"
9
+ }
10
+ },
11
+ "dependencies": {
12
+ "@earendil-works/pi-agent-core": "^0.80.2",
13
+ "@earendil-works/pi-ai": "^0.80.2",
14
+ "@assemblyline-agents/core": "0.1.0"
15
+ },
16
+ "description": "Canonical Pi agent loop for OpenEve behind the internal AgentHarness runtime contract.",
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "engines": {
21
+ "node": ">=22.19.0"
22
+ },
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/jasonbadeaux/openeve.git",
27
+ "directory": "packages/pi"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/jasonbadeaux/openeve/issues"
31
+ },
32
+ "homepage": "https://github.com/jasonbadeaux/openeve#readme",
33
+ "author": "OpenEve contributors",
34
+ "keywords": [
35
+ "openeve",
36
+ "ai",
37
+ "agents",
38
+ "pi",
39
+ "agent-loop",
40
+ "harness",
41
+ "runtime"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.json",
48
+ "check": "tsc -p tsconfig.json --noEmit"
49
+ }
50
+ }