@aexhq/agentloop-pi 3.0.0 → 4.0.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/README.md +9 -0
- package/dist/loop.component.wasm +0 -0
- package/package.json +3 -3
- package/src/component.mjs +2 -2
- package/src/logic.mjs +16 -4
package/README.md
CHANGED
|
@@ -43,3 +43,12 @@ explicit `placements: { toolName: "environmentName" }` option. Set
|
|
|
43
43
|
`environmentSelection: "model"` to expose each Tool's authorized Environment names in its input
|
|
44
44
|
schema. The loop unwraps that choice before dispatch; Brain validates the selected pair and the
|
|
45
45
|
canonical input. Both modes use the same session Tool placements.
|
|
46
|
+
|
|
47
|
+
Transcript and kv changes are saved inline through Brain's state services. A later model or
|
|
48
|
+
Tool failure preserves acknowledged writes; turn output contains only the result. Retained
|
|
49
|
+
native model blocks pass through unchanged, and user images enter model context as media.
|
|
50
|
+
Compaction explicitly resets the response format and installs a summary only after `end_turn`;
|
|
51
|
+
a truncated, refused, or unknown summary leaves the original saved context intact.
|
|
52
|
+
|
|
53
|
+
Rebuild Components against the matching Brain WIT when upgrading. Existing sessions keep their
|
|
54
|
+
immutable loop implementation and must be recreated for this interface change.
|
package/dist/loop.component.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aexhq/agentloop-pi",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Pi-style parallel-Tool Brain extension",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public",
|
|
18
|
-
"provenance":
|
|
18
|
+
"provenance": true,
|
|
19
19
|
"tag": "next"
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"prepack": "npm run build"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@aexhq/brain": "0.
|
|
37
|
+
"@aexhq/brain": "0.19.0",
|
|
38
38
|
"zod": "4.4.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {}
|
package/src/component.mjs
CHANGED
|
@@ -25,6 +25,8 @@ export async function turn(input) {
|
|
|
25
25
|
system: input.system,
|
|
26
26
|
tools: JSON.parse(input.toolsJson),
|
|
27
27
|
}, {
|
|
28
|
+
setTranscript: (messages) => hosted(() => host.setTranscript(JSON.stringify(messages))),
|
|
29
|
+
setKv: (key, value) => hosted(() => host.setKv(key, JSON.stringify(value))),
|
|
28
30
|
events: (after) => JSON.parse(hosted(() => host.events(BigInt(after)))),
|
|
29
31
|
model: (request) => JSON.parse(hosted(() => host.model(JSON.stringify(request)))),
|
|
30
32
|
dispatch: (calls) => JSON.parse(hosted(() => host.dispatch(JSON.stringify(calls)))),
|
|
@@ -32,8 +34,6 @@ export async function turn(input) {
|
|
|
32
34
|
telemetry: (record) => host.telemetry(JSON.stringify(record ?? null)),
|
|
33
35
|
});
|
|
34
36
|
return {
|
|
35
|
-
transcriptJson: JSON.stringify(output.transcript),
|
|
36
|
-
kvJson: JSON.stringify(output.kv),
|
|
37
37
|
resultJson: output.result === undefined ? undefined : JSON.stringify(output.result),
|
|
38
38
|
};
|
|
39
39
|
} catch (error) {
|
package/src/logic.mjs
CHANGED
|
@@ -45,6 +45,8 @@ const text = (message) => message.content.filter((block) => block.type === "text
|
|
|
45
45
|
const blockText = (block) => {
|
|
46
46
|
if (block.type === "text") return block.text;
|
|
47
47
|
if (block.type === "tool_use") return `[tool_use ${block.name}] ${JSON.stringify(block.input)}`;
|
|
48
|
+
if (block.type === "native") return `[provider continuation state: ${block.format}]`;
|
|
49
|
+
if (block.type === "image") return block.url.startsWith("https://") ? `[image ${block.url}]` : "[embedded image]";
|
|
48
50
|
const output = typeof block.content === "string" ? block.content : JSON.stringify(block.content);
|
|
49
51
|
return `[tool_result${block.is_error ? " (error)" : ""}] ${output.length > 2000 ? `${output.slice(0, 2000)}…` : output}`;
|
|
50
52
|
};
|
|
@@ -64,6 +66,7 @@ export async function runPi(input, context) {
|
|
|
64
66
|
const transcript = cloneJson(input.transcript);
|
|
65
67
|
const observed_sequence = await observeEvents(context, transcript, input.kv.observed_sequence ?? 0);
|
|
66
68
|
const saved = input.kv.checkpoint;
|
|
69
|
+
await context.setKv("observed_sequence", observed_sequence);
|
|
67
70
|
const checkpoint = saved === undefined ? { summary: null } : cloneJson(saved);
|
|
68
71
|
const body = () => checkpoint.summary === null ? transcript : transcript.slice(1);
|
|
69
72
|
const shouldCompact = () =>
|
|
@@ -87,27 +90,36 @@ export async function runPi(input, context) {
|
|
|
87
90
|
if (cut === 0) return;
|
|
88
91
|
const previous = checkpoint.summary === null ? "" : `Previous summary:\n\n${checkpoint.summary}\n\n`;
|
|
89
92
|
const prompt = checkpoint.summary === null ? SUMMARIZATION_PROMPT : `${UPDATE_RULES}${SUMMARIZATION_PROMPT}`;
|
|
90
|
-
const { message } = await context.model({
|
|
93
|
+
const { message, stop_reason } = await context.model({
|
|
94
|
+
response_format: null,
|
|
91
95
|
tools: [],
|
|
92
96
|
messages: [{ role: "user", content: [{ type: "text", text: `${previous}${serializeConversation(messages.slice(0, cut))}\n\n${prompt}` }] }],
|
|
93
97
|
});
|
|
98
|
+
if (stop_reason !== "end_turn") throw new Error(`Compaction did not complete: ${stop_reason}`);
|
|
94
99
|
checkpoint.summary = text(message);
|
|
95
100
|
transcript.splice(0, transcript.length,
|
|
96
101
|
{ role: "user", content: [{ type: "text", text: `${CHECKPOINT_PREFIX}${checkpoint.summary}` }] },
|
|
97
102
|
...messages.slice(cut));
|
|
98
103
|
};
|
|
99
104
|
|
|
100
|
-
transcript.push({ role: "user", content: [{ type: "text", text: input.input.message }] });
|
|
105
|
+
transcript.push({ role: "user", content: [{ type: "text", text: input.input.message }, ...(input.input.media ?? [])] });
|
|
101
106
|
for (;;) {
|
|
102
|
-
|
|
107
|
+
await context.setTranscript(transcript);
|
|
108
|
+
if (shouldCompact()) {
|
|
109
|
+
await compact();
|
|
110
|
+
await context.setTranscript(transcript);
|
|
111
|
+
await context.setKv("checkpoint", checkpoint);
|
|
112
|
+
}
|
|
103
113
|
const { message, stop_reason } = await context.model({ messages: transcript, tools: placement.definitions });
|
|
104
114
|
transcript.push(message);
|
|
115
|
+
await context.setTranscript(transcript);
|
|
116
|
+
await context.setKv("checkpoint", checkpoint);
|
|
105
117
|
const calls = message.content
|
|
106
118
|
.filter((block) => block.type === "tool_use")
|
|
107
119
|
.map((block) => ({ call_id: block.id, name: block.name, input: block.input }));
|
|
108
120
|
if (calls.length === 0) {
|
|
109
121
|
await context.emit("output_emitted", { type: "assistant_message", message: text(message) });
|
|
110
|
-
return {
|
|
122
|
+
return {};
|
|
111
123
|
}
|
|
112
124
|
if (stop_reason === "max_tokens") {
|
|
113
125
|
transcript.push({
|