@mobrienv/autoloop-harness 0.7.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/dist/artifacts.d.ts +50 -0
- package/dist/artifacts.js +333 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/config-helpers.d.ts +36 -0
- package/dist/config-helpers.js +427 -0
- package/dist/config-helpers.js.map +1 -0
- package/dist/coordination.d.ts +1 -0
- package/dist/coordination.js +126 -0
- package/dist/coordination.js.map +1 -0
- package/dist/display.d.ts +21 -0
- package/dist/display.js +177 -0
- package/dist/display.js.map +1 -0
- package/dist/emit.d.ts +21 -0
- package/dist/emit.js +243 -0
- package/dist/emit.js.map +1 -0
- package/dist/events.d.ts +56 -0
- package/dist/events.js +9 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/iteration.d.ts +16 -0
- package/dist/iteration.js +153 -0
- package/dist/iteration.js.map +1 -0
- package/dist/metareview.d.ts +6 -0
- package/dist/metareview.js +104 -0
- package/dist/metareview.js.map +1 -0
- package/dist/metrics.d.ts +12 -0
- package/dist/metrics.js +177 -0
- package/dist/metrics.js.map +1 -0
- package/dist/parallel.d.ts +37 -0
- package/dist/parallel.js +242 -0
- package/dist/parallel.js.map +1 -0
- package/dist/pi-adapter.d.ts +1 -0
- package/dist/pi-adapter.js +220 -0
- package/dist/pi-adapter.js.map +1 -0
- package/dist/prompt.d.ts +46 -0
- package/dist/prompt.js +446 -0
- package/dist/prompt.js.map +1 -0
- package/dist/registry-bridge.d.ts +7 -0
- package/dist/registry-bridge.js +63 -0
- package/dist/registry-bridge.js.map +1 -0
- package/dist/scratchpad.d.ts +2 -0
- package/dist/scratchpad.js +65 -0
- package/dist/scratchpad.js.map +1 -0
- package/dist/stop.d.ts +5 -0
- package/dist/stop.js +81 -0
- package/dist/stop.js.map +1 -0
- package/dist/tools.d.ts +3 -0
- package/dist/tools.js +65 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +137 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wave/finalize-wave.d.ts +9 -0
- package/dist/wave/finalize-wave.js +86 -0
- package/dist/wave/finalize-wave.js.map +1 -0
- package/dist/wave/launch-branches.d.ts +6 -0
- package/dist/wave/launch-branches.js +313 -0
- package/dist/wave/launch-branches.js.map +1 -0
- package/dist/wave/parse-objectives.d.ts +3 -0
- package/dist/wave/parse-objectives.js +32 -0
- package/dist/wave/parse-objectives.js.map +1 -0
- package/dist/wave/types.d.ts +43 -0
- package/dist/wave/types.js +2 -0
- package/dist/wave/types.js.map +1 -0
- package/dist/wave.d.ts +6 -0
- package/dist/wave.js +158 -0
- package/dist/wave.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { shellWords } from "@mobrienv/autoloop-core";
|
|
4
|
+
const BRIDGE_SCRIPT = `
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import pathlib
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def extract_text_from_message(message):
|
|
13
|
+
text_parts = []
|
|
14
|
+
for item in message.get("content") or []:
|
|
15
|
+
if item.get("type") == "text":
|
|
16
|
+
text_parts.append(item.get("text", ""))
|
|
17
|
+
return "".join(text_parts)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def extract_text_from_messages(messages):
|
|
21
|
+
if not messages:
|
|
22
|
+
return ""
|
|
23
|
+
return extract_text_from_message(messages[-1] or {})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def extract_tool_error(response):
|
|
27
|
+
for item in response.get("output") or []:
|
|
28
|
+
if item.get("type") == "text":
|
|
29
|
+
text = item.get("text", "")
|
|
30
|
+
if text:
|
|
31
|
+
return text
|
|
32
|
+
return ""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def stream_log_path():
|
|
36
|
+
state_dir = os.environ.get("AUTOLOOP_STATE_DIR", "")
|
|
37
|
+
if not state_dir:
|
|
38
|
+
return None
|
|
39
|
+
prefix = "pi-review" if os.environ.get("AUTOLOOP_REVIEW_MODE", "") == "hyperagent" else "pi-stream"
|
|
40
|
+
iteration = os.environ.get("AUTOLOOP_ITERATION", "")
|
|
41
|
+
name = prefix + (("." + iteration) if iteration else "") + ".jsonl"
|
|
42
|
+
return pathlib.Path(state_dir) / name
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
cmd = sys.argv[1:-1]
|
|
46
|
+
prompt_path = sys.argv[-1]
|
|
47
|
+
raw_output = ""
|
|
48
|
+
exit_code = 1
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
with open(prompt_path, "r", encoding="utf-8") as prompt_file:
|
|
52
|
+
completed = subprocess.run(cmd, stdin=prompt_file, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding="utf-8", errors="replace")
|
|
53
|
+
raw_output = completed.stdout or ""
|
|
54
|
+
exit_code = completed.returncode
|
|
55
|
+
except FileNotFoundError as exc:
|
|
56
|
+
raw_output = str(exc)
|
|
57
|
+
exit_code = 127
|
|
58
|
+
|
|
59
|
+
log_path = stream_log_path()
|
|
60
|
+
if log_path is not None:
|
|
61
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
log_path.write_text(raw_output)
|
|
63
|
+
|
|
64
|
+
text_parts = []
|
|
65
|
+
fallback_text = ""
|
|
66
|
+
saw_turn_end = False
|
|
67
|
+
saw_agent_end = False
|
|
68
|
+
error = ""
|
|
69
|
+
|
|
70
|
+
for raw_line in raw_output.splitlines():
|
|
71
|
+
line = raw_line.strip()
|
|
72
|
+
if not line:
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
event = json.loads(line)
|
|
77
|
+
except Exception:
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
event_type = event.get("type")
|
|
81
|
+
|
|
82
|
+
if event_type == "message_update":
|
|
83
|
+
assistant_event = event.get("assistantMessageEvent") or {}
|
|
84
|
+
assistant_type = assistant_event.get("type")
|
|
85
|
+
if assistant_type == "text_delta":
|
|
86
|
+
text_parts.append(assistant_event.get("delta", ""))
|
|
87
|
+
elif assistant_type == "error" and assistant_event.get("reason"):
|
|
88
|
+
error = assistant_event.get("reason")
|
|
89
|
+
elif event_type == "tool_execution_end":
|
|
90
|
+
response = event.get("toolExecutionResponseEvent") or {}
|
|
91
|
+
if response.get("isError"):
|
|
92
|
+
detail = extract_tool_error(response)
|
|
93
|
+
if detail:
|
|
94
|
+
error = detail
|
|
95
|
+
elif event_type == "turn_end":
|
|
96
|
+
saw_turn_end = True
|
|
97
|
+
if not fallback_text:
|
|
98
|
+
fallback_text = extract_text_from_message(event.get("message") or {})
|
|
99
|
+
elif event_type == "agent_end":
|
|
100
|
+
saw_agent_end = True
|
|
101
|
+
if not fallback_text:
|
|
102
|
+
fallback_text = extract_text_from_messages(event.get("messages") or [])
|
|
103
|
+
|
|
104
|
+
text = "".join(text_parts)
|
|
105
|
+
if not text:
|
|
106
|
+
text = fallback_text
|
|
107
|
+
|
|
108
|
+
failed = exit_code != 0 or (not saw_turn_end and not saw_agent_end) or bool(error)
|
|
109
|
+
verbose = os.environ.get("AUTOLOOP_LOG_LEVEL", "") == "debug"
|
|
110
|
+
output = ""
|
|
111
|
+
|
|
112
|
+
if failed:
|
|
113
|
+
if verbose:
|
|
114
|
+
if text and error:
|
|
115
|
+
output = text + "\\n\\npi error: " + error
|
|
116
|
+
elif text:
|
|
117
|
+
output = text
|
|
118
|
+
else:
|
|
119
|
+
output = raw_output or error
|
|
120
|
+
else:
|
|
121
|
+
if text:
|
|
122
|
+
output = text
|
|
123
|
+
elif error:
|
|
124
|
+
output = "pi failed (run with -v for details)"
|
|
125
|
+
else:
|
|
126
|
+
output = raw_output or "pi failed"
|
|
127
|
+
else:
|
|
128
|
+
output = text
|
|
129
|
+
|
|
130
|
+
if output:
|
|
131
|
+
sys.stdout.write(output)
|
|
132
|
+
|
|
133
|
+
sys.exit(1 if failed else 0)
|
|
134
|
+
`;
|
|
135
|
+
export function run(args) {
|
|
136
|
+
const prompt = resolvePrompt();
|
|
137
|
+
if (!prompt) {
|
|
138
|
+
finishFailure("missing projected prompt");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const promptPath = materializePromptPath(prompt);
|
|
142
|
+
const piCommand = args[0] || "pi";
|
|
143
|
+
const piArgs = args.slice(1);
|
|
144
|
+
const command = buildPiBridgeCommand(piCommand, piArgs, promptPath);
|
|
145
|
+
try {
|
|
146
|
+
const output = execSync(command, {
|
|
147
|
+
encoding: "utf-8",
|
|
148
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
149
|
+
shell: "/bin/sh",
|
|
150
|
+
maxBuffer: 100 * 1024 * 1024,
|
|
151
|
+
});
|
|
152
|
+
finishSuccess(output || "");
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
const e = err;
|
|
156
|
+
finishFailure(e.stdout || e.stderr || "");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function resolvePrompt() {
|
|
160
|
+
const envPrompt = process.env.AUTOLOOP_PROMPT;
|
|
161
|
+
if (envPrompt)
|
|
162
|
+
return envPrompt;
|
|
163
|
+
const pathPrompt = promptFromPath();
|
|
164
|
+
if (pathPrompt)
|
|
165
|
+
return pathPrompt;
|
|
166
|
+
return projectedPrompt();
|
|
167
|
+
}
|
|
168
|
+
function projectedPrompt() {
|
|
169
|
+
const bin = process.env.AUTOLOOP_BIN || "";
|
|
170
|
+
const iteration = process.env.AUTOLOOP_ITERATION || "";
|
|
171
|
+
if (!bin || !iteration)
|
|
172
|
+
return "";
|
|
173
|
+
try {
|
|
174
|
+
const output = execSync(shellWords([bin, "inspect", "prompt", iteration, "--format", "md"]), { encoding: "utf-8", shell: "/bin/sh", timeout: 30000 });
|
|
175
|
+
return output || "";
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return "";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function promptFromPath() {
|
|
182
|
+
const path = process.env.AUTOLOOP_PROMPT_PATH || "";
|
|
183
|
+
if (!path || !existsSync(path))
|
|
184
|
+
return "";
|
|
185
|
+
return readFileSync(path, "utf-8");
|
|
186
|
+
}
|
|
187
|
+
function buildPiBridgeCommand(command, extraArgs, promptPath) {
|
|
188
|
+
return shellWords([
|
|
189
|
+
"python3",
|
|
190
|
+
"-c",
|
|
191
|
+
BRIDGE_SCRIPT,
|
|
192
|
+
command,
|
|
193
|
+
...defaultPiArgs(),
|
|
194
|
+
...extraArgs,
|
|
195
|
+
promptPath,
|
|
196
|
+
]);
|
|
197
|
+
}
|
|
198
|
+
function materializePromptPath(prompt) {
|
|
199
|
+
const path = promptStoragePath();
|
|
200
|
+
writeFileSync(path, prompt, "utf-8");
|
|
201
|
+
return path;
|
|
202
|
+
}
|
|
203
|
+
function promptStoragePath() {
|
|
204
|
+
const configured = process.env.AUTOLOOP_PROMPT_PATH || "";
|
|
205
|
+
return configured || "/tmp/autoloop-pi-adapter-prompt.md";
|
|
206
|
+
}
|
|
207
|
+
function defaultPiArgs() {
|
|
208
|
+
return ["-p", "--mode", "json", "--no-session"];
|
|
209
|
+
}
|
|
210
|
+
function finishSuccess(output) {
|
|
211
|
+
if (output)
|
|
212
|
+
process.stdout.write(output);
|
|
213
|
+
process.exitCode = 0;
|
|
214
|
+
}
|
|
215
|
+
function finishFailure(output) {
|
|
216
|
+
if (output)
|
|
217
|
+
process.stdout.write(output);
|
|
218
|
+
process.exitCode = 1;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=pi-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi-adapter.js","sourceRoot":"","sources":["../src/pi-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { TwoTierMemoryStats } from "@mobrienv/autoloop-core/memory-render";
|
|
2
|
+
import * as topology from "@mobrienv/autoloop-core/topology";
|
|
3
|
+
import type { LoopContext } from "./index.js";
|
|
4
|
+
export interface IterationContext {
|
|
5
|
+
iteration: number;
|
|
6
|
+
recentEvent: string;
|
|
7
|
+
allowedRoles: string[];
|
|
8
|
+
allowedEvents: string[];
|
|
9
|
+
backpressure: string;
|
|
10
|
+
lastRejected: string;
|
|
11
|
+
scratchpadText: string;
|
|
12
|
+
memoryText: string;
|
|
13
|
+
prompt: string;
|
|
14
|
+
roleAgent: string;
|
|
15
|
+
}
|
|
16
|
+
interface DerivedRunContext {
|
|
17
|
+
scratchpadText: string;
|
|
18
|
+
memoryText: string;
|
|
19
|
+
memoryStats: TwoTierMemoryStats;
|
|
20
|
+
tasksText: string;
|
|
21
|
+
tasksStats: {
|
|
22
|
+
open: number;
|
|
23
|
+
done: number;
|
|
24
|
+
total: number;
|
|
25
|
+
};
|
|
26
|
+
guidanceMessages: string[];
|
|
27
|
+
routing: RoutingContext;
|
|
28
|
+
backpressure: string;
|
|
29
|
+
invalidCount: number;
|
|
30
|
+
lastRejected: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function buildIterationContext(loop: LoopContext, iteration: number): IterationContext;
|
|
33
|
+
interface RoutingContext {
|
|
34
|
+
recentEvent: string;
|
|
35
|
+
allowedRoles: string[];
|
|
36
|
+
allowedEvents: string[];
|
|
37
|
+
}
|
|
38
|
+
export declare function iterationRoutingContext(topo: topology.Topology, runLines: string[]): RoutingContext;
|
|
39
|
+
export declare function routingEventFromLines(lines: string[]): string;
|
|
40
|
+
export declare function latestInvalidNote(runLines: string[]): string;
|
|
41
|
+
export declare function invalidEventCount(runLines: string[]): number;
|
|
42
|
+
export declare function lastRejectedTopic(runLines: string[]): string;
|
|
43
|
+
export declare function renderIterationPromptText(loop: LoopContext, iteration: number, derived: DerivedRunContext): string;
|
|
44
|
+
export declare function renderReviewPromptText(loop: LoopContext, iteration: number, runLines: string[]): string;
|
|
45
|
+
export declare function drainGuidance(runLines: string[]): string[];
|
|
46
|
+
export {};
|