@tt-a1i/openpi 0.4.0 → 0.5.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 +102 -40
- package/SETUP.md +22 -6
- package/assets/openpi-launch-card-v1.webp +0 -0
- package/bin/openpi.js +145 -0
- package/extensions/background-terminals/index.ts +30 -2
- package/extensions/background-terminals/src/domain.ts +2 -0
- package/extensions/background-terminals/src/manager.ts +486 -106
- package/extensions/background-terminals/src/output.ts +33 -0
- package/extensions/background-terminals/src/prompt.ts +13 -5
- package/extensions/background-terminals/src/result-delivery.ts +4 -1
- package/extensions/clear-context/index.ts +83 -0
- package/extensions/context-pivot/index.ts +16 -6
- package/extensions/cron/schedule.ts +7 -1
- package/extensions/file-mutation-display/render.ts +17 -257
- package/extensions/file-search/src/binaries.ts +57 -41
- package/extensions/git-read/index.ts +1 -3
- package/extensions/model-info/index.ts +21 -33
- package/extensions/model-info/session-metrics.ts +96 -0
- package/extensions/plan-mode/bash-policy.ts +54 -9
- package/extensions/plan-mode/index.ts +7 -2
- package/extensions/post-edit/index.ts +16 -6
- package/extensions/sessions/git-stats.ts +258 -72
- package/extensions/sessions/index.ts +153 -86
- package/extensions/sessions/preview-cache.ts +104 -0
- package/extensions/sessions/preview-loader.ts +856 -0
- package/extensions/sessions/sessions.ts +43 -4
- package/extensions/setup/index.ts +123 -127
- package/extensions/shared/activity-status.ts +30 -0
- package/extensions/shared/agent-session-page.ts +319 -0
- package/extensions/shared/agent-tool-renderer.ts +218 -0
- package/extensions/shared/agent-transcript.ts +524 -0
- package/extensions/shared/capability-intent.ts +1 -1
- package/extensions/shared/child-session.ts +437 -21
- package/extensions/shared/result-delivery.ts +34 -0
- package/extensions/shared/setup-config.ts +73 -33
- package/extensions/shared/setup-episode-state.ts +1 -1
- package/extensions/shared/terminal-text.ts +110 -23
- package/extensions/shared/text-projection.ts +72 -15
- package/extensions/shared/tool-activity.ts +382 -0
- package/extensions/shared/tool-surface.ts +29 -2
- package/extensions/shared/transcript-viewport.ts +46 -0
- package/extensions/shared/web-observer-registry.ts +390 -0
- package/extensions/shared/worktree.ts +11 -0
- package/extensions/subagents/index.ts +270 -59
- package/extensions/subagents/navigation.ts +34 -5
- package/extensions/subagents/src/backend.ts +12 -1
- package/extensions/subagents/src/backends/pi.ts +375 -66
- package/extensions/subagents/src/domain.ts +5 -0
- package/extensions/subagents/src/manager.ts +34 -2
- package/extensions/subagents/src/prompt.ts +32 -4
- package/extensions/subagents/src/result-artifact.ts +4 -0
- package/extensions/subagents/src/result-delivery.ts +7 -1
- package/extensions/subagents/src/runtime.ts +15 -1
- package/extensions/subagents/src/ui/takeover.ts +73 -257
- package/extensions/subagents/src/ui/transcript.ts +38 -535
- package/extensions/subagents/src/ui/wait-result.ts +103 -15
- package/extensions/suggestions/src/ui.ts +10 -4
- package/extensions/tasks/index.ts +0 -3
- package/extensions/ui-customization/footer.ts +0 -40
- package/extensions/ui-customization/index.ts +0 -4
- package/extensions/user-input-fold/index.ts +1 -1
- package/extensions/web/index.ts +234 -0
- package/extensions/workflows/artifacts.ts +137 -47
- package/extensions/workflows/completion-projection.ts +457 -0
- package/extensions/workflows/coordinator.ts +8 -10
- package/extensions/workflows/dashboard.ts +167 -228
- package/extensions/workflows/handoff.ts +70 -16
- package/extensions/workflows/index.ts +488 -198
- package/extensions/workflows/journal.ts +148 -13
- package/extensions/workflows/model.ts +74 -4
- package/extensions/workflows/navigation.ts +32 -8
- package/extensions/workflows/progress-projection.ts +306 -0
- package/extensions/workflows/prompt.ts +66 -6
- package/extensions/workflows/replay-safety.ts +42 -21
- package/extensions/workflows/result-delivery.ts +128 -64
- package/extensions/workflows/retention.ts +593 -0
- package/extensions/workflows/runner.ts +388 -279
- package/extensions/workflows/sandbox-child.cjs +25 -3
- package/extensions/workflows/sandbox.ts +62 -8
- package/extensions/workflows/serialization.ts +325 -17
- package/extensions/workflows/tool-renderer.ts +22 -0
- package/extensions/workflows/transcript.ts +149 -0
- package/extensions/workspace-cleanup-guard/index.ts +54 -0
- package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
- package/package.json +28 -8
- package/skills/subagents/REFERENCE.md +189 -0
- package/skills/subagents/SKILL.md +1 -1
- package/skills/workflows/REFERENCE.md +4 -2
- package/web/adapter/pi-adapter.ts +661 -0
- package/web/host/browser-launcher.ts +20 -0
- package/web/host/static-assets.ts +4 -0
- package/web/host/terminal-status.ts +38 -0
- package/web/host/web-host.ts +789 -0
- package/web/http-dispatcher.ts +125 -0
- package/web/protocol/types.ts +462 -0
- package/web/runtime/pi-runtime.ts +991 -0
- package/web/runtime/types.ts +71 -0
- package/web/runtime/web-host-lease.ts +497 -0
- package/web/trace.ts +18 -0
- package/web/ui/app.js +1398 -0
- package/web/ui/index.html +139 -0
- package/web/ui/styles.css +598 -0
- package/web/vite.config.mjs +34 -0
- package/extensions/execution-convergence/active-evidence.ts +0 -129
- package/extensions/execution-convergence/index.ts +0 -442
- package/extensions/execution-convergence/workspace-provenance.ts +0 -338
- package/extensions/setup/intercom-fs-helper.cjs +0 -130
- package/extensions/setup/intercom.ts +0 -603
- package/extensions/subagents/src/backends/stub.ts +0 -303
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scripted stub sessions, test-only: they stand in for the pi backend so the
|
|
3
|
-
* manager can be driven without a real child session. A stub session:
|
|
4
|
-
*
|
|
5
|
-
* - streams a plausible turn (thinking deltas, one fake tool cycle, text
|
|
6
|
-
* deltas, usage ramp, a final assistant message, RunSettled) over a few
|
|
7
|
-
* seconds so streaming UI, wait, and the footer counters are observable;
|
|
8
|
-
* - supports send() while running (queued-steer rendering) and while idle
|
|
9
|
-
* (fresh run);
|
|
10
|
-
* - supports interrupt (RunSettled Interrupted -> status "error", matching v1);
|
|
11
|
-
* - fails the run when the prompt starts with "FAIL:", and refuses to spawn
|
|
12
|
-
* at all when it starts with "SPAWNFAIL:" (error-path testing);
|
|
13
|
-
* - hangs the run after RunStarted without any assistant event when the
|
|
14
|
-
* prompt starts with "HANG:" (first-response watchdog testing);
|
|
15
|
-
* - appends every event to a JSONL "session file" in tmpdir so the
|
|
16
|
-
* "full transcript in session file" pointers resolve.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import * as fs from "node:fs";
|
|
20
|
-
import * as os from "node:os";
|
|
21
|
-
import * as path from "node:path";
|
|
22
|
-
import type { Cause, Scope } from "effect";
|
|
23
|
-
import { Duration, Effect, Fiber, Queue, Ref, Stream } from "effect";
|
|
24
|
-
import type { SubagentBackend, SubagentSession } from "../backend.ts";
|
|
25
|
-
import type {
|
|
26
|
-
BackendName,
|
|
27
|
-
QueuedMessage,
|
|
28
|
-
SpawnTask,
|
|
29
|
-
SubagentEvent,
|
|
30
|
-
SubagentMeta,
|
|
31
|
-
} from "../domain.ts";
|
|
32
|
-
import { SendError, SpawnError } from "../domain.ts";
|
|
33
|
-
|
|
34
|
-
export interface StubProfile {
|
|
35
|
-
readonly backend: BackendName;
|
|
36
|
-
readonly defaultModelLabel: string;
|
|
37
|
-
readonly contextWindow: number;
|
|
38
|
-
readonly toolName: string;
|
|
39
|
-
/** Delay between scripted events; varies per backend so streams differ. */
|
|
40
|
-
readonly cadenceMs: number;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const STUB_DIR = path.join(os.tmpdir(), "subagents-stub");
|
|
44
|
-
let sessionCounter = 0;
|
|
45
|
-
|
|
46
|
-
export function makeStubBackend(profile: StubProfile): SubagentBackend {
|
|
47
|
-
return {
|
|
48
|
-
name: profile.backend,
|
|
49
|
-
spawn: (task) =>
|
|
50
|
-
task.prompt.startsWith("SPAWNFAIL:")
|
|
51
|
-
? Effect.fail(new SpawnError({ message: "stub refused to spawn" }))
|
|
52
|
-
: makeStubSession(profile, task),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function firstLine(text: string): string {
|
|
57
|
-
return (
|
|
58
|
-
text
|
|
59
|
-
.split("\n")
|
|
60
|
-
.find((line) => line.trim())
|
|
61
|
-
?.trim() ?? ""
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function chunked(text: string, size: number): string[] {
|
|
66
|
-
const chunks: string[] = [];
|
|
67
|
-
for (let i = 0; i < text.length; i += size)
|
|
68
|
-
chunks.push(text.slice(i, i + size));
|
|
69
|
-
return chunks;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const makeStubSession = (
|
|
73
|
-
profile: StubProfile,
|
|
74
|
-
task: SpawnTask,
|
|
75
|
-
): Effect.Effect<SubagentSession, never, Scope.Scope> =>
|
|
76
|
-
Effect.gen(function* () {
|
|
77
|
-
const sessionId = `stub-${profile.backend}-${++sessionCounter}`;
|
|
78
|
-
const sessionFile = path.join(STUB_DIR, `${sessionId}.jsonl`);
|
|
79
|
-
|
|
80
|
-
const state = {
|
|
81
|
-
meta: {
|
|
82
|
-
backend: profile.backend,
|
|
83
|
-
modelLabel: task.model ?? profile.defaultModelLabel,
|
|
84
|
-
contextWindow: profile.contextWindow,
|
|
85
|
-
sessionFilePath: sessionFile,
|
|
86
|
-
} satisfies SubagentMeta as SubagentMeta,
|
|
87
|
-
pending: [] as string[],
|
|
88
|
-
turnCount: 0,
|
|
89
|
-
closed: false,
|
|
90
|
-
/** True between the driver dequeuing a prompt and registering its turn fiber. */
|
|
91
|
-
dispatching: false,
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const events = yield* Queue.make<SubagentEvent, Cause.Done>();
|
|
95
|
-
const inbox = yield* Queue.make<string, Cause.Done>();
|
|
96
|
-
const activeTurn = yield* Ref.make<Fiber.Fiber<void> | undefined>(
|
|
97
|
-
undefined,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const emit = (event: SubagentEvent) =>
|
|
101
|
-
Effect.suspend(() => {
|
|
102
|
-
try {
|
|
103
|
-
fs.appendFileSync(sessionFile, `${JSON.stringify(event)}\n`);
|
|
104
|
-
} catch {
|
|
105
|
-
// The fake session file is best-effort.
|
|
106
|
-
}
|
|
107
|
-
if (event._tag === "MetaChanged") {
|
|
108
|
-
state.meta = { ...state.meta, ...event.meta };
|
|
109
|
-
}
|
|
110
|
-
return Queue.offer(events, event);
|
|
111
|
-
}).pipe(Effect.asVoid);
|
|
112
|
-
|
|
113
|
-
const pause = Effect.sleep(Duration.millis(profile.cadenceMs));
|
|
114
|
-
|
|
115
|
-
const runTurn = (userText: string, turn: number) =>
|
|
116
|
-
Effect.gen(function* () {
|
|
117
|
-
yield* emit({ _tag: "RunStarted" });
|
|
118
|
-
const failing = userText.trimStart().startsWith("FAIL:");
|
|
119
|
-
// Stand in for a provider that accepts the request but never emits
|
|
120
|
-
// its first assistant event.
|
|
121
|
-
if (userText.trimStart().startsWith("HANG:")) {
|
|
122
|
-
return yield* Effect.never;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const thinking = "Looking at the task and planning an approach...";
|
|
126
|
-
for (const delta of chunked(thinking, 16)) {
|
|
127
|
-
yield* emit({ _tag: "AssistantDelta", kind: "thinking", delta });
|
|
128
|
-
yield* pause;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const toolId = `${sessionId}-tool-${turn}`;
|
|
132
|
-
const argsPreview = `{"command":"ls ${task.cwd}"}`;
|
|
133
|
-
yield* emit({
|
|
134
|
-
_tag: "AssistantMessage",
|
|
135
|
-
parts: [
|
|
136
|
-
{ type: "thinking", text: thinking },
|
|
137
|
-
{
|
|
138
|
-
type: "text",
|
|
139
|
-
text: `I'll run ${profile.toolName} to look around first.`,
|
|
140
|
-
},
|
|
141
|
-
{ type: "toolCall", toolId, name: profile.toolName, argsPreview },
|
|
142
|
-
],
|
|
143
|
-
});
|
|
144
|
-
yield* emit({
|
|
145
|
-
_tag: "ToolStart",
|
|
146
|
-
toolId,
|
|
147
|
-
name: profile.toolName,
|
|
148
|
-
argsPreview,
|
|
149
|
-
});
|
|
150
|
-
yield* pause;
|
|
151
|
-
yield* emit({
|
|
152
|
-
_tag: "ToolUpdate",
|
|
153
|
-
toolId,
|
|
154
|
-
outputPreview: "src docs package.json",
|
|
155
|
-
});
|
|
156
|
-
yield* pause;
|
|
157
|
-
yield* emit({
|
|
158
|
-
_tag: "ToolEnd",
|
|
159
|
-
toolId,
|
|
160
|
-
name: profile.toolName,
|
|
161
|
-
isError: false,
|
|
162
|
-
outputPreview: "src docs package.json",
|
|
163
|
-
});
|
|
164
|
-
yield* emit({
|
|
165
|
-
_tag: "UsageChanged",
|
|
166
|
-
tokens: Math.min(profile.contextWindow, 2400 * (turn + 1)),
|
|
167
|
-
contextWindow: profile.contextWindow,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
if (failing) {
|
|
171
|
-
yield* pause;
|
|
172
|
-
yield* emit({
|
|
173
|
-
_tag: "RunSettled",
|
|
174
|
-
outcome: {
|
|
175
|
-
_tag: "Failed",
|
|
176
|
-
errorText: `[stub:${profile.backend}] task failed as requested by FAIL: prefix`,
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const finalText =
|
|
183
|
-
`[stub:${profile.backend}] completed: ${firstLine(userText).slice(0, 200)}\n\n` +
|
|
184
|
-
`This is a stubbed ${profile.backend} subagent turn ${turn + 1}. ` +
|
|
185
|
-
`The real backend integration will replace this scripted output.`;
|
|
186
|
-
for (const delta of chunked(finalText, 24)) {
|
|
187
|
-
yield* emit({ _tag: "AssistantDelta", kind: "text", delta });
|
|
188
|
-
yield* pause;
|
|
189
|
-
}
|
|
190
|
-
yield* emit({
|
|
191
|
-
_tag: "AssistantMessage",
|
|
192
|
-
parts: [{ type: "text", text: finalText }],
|
|
193
|
-
});
|
|
194
|
-
yield* emit({
|
|
195
|
-
_tag: "UsageChanged",
|
|
196
|
-
tokens: Math.min(profile.contextWindow, 2400 * (turn + 1) + 900),
|
|
197
|
-
contextWindow: profile.contextWindow,
|
|
198
|
-
});
|
|
199
|
-
yield* emit({
|
|
200
|
-
_tag: "RunSettled",
|
|
201
|
-
outcome: { _tag: "Completed", finalText },
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const queuedView = (): ReadonlyArray<QueuedMessage> =>
|
|
206
|
-
state.pending.map((text) => ({ text, kind: "steer" as const }));
|
|
207
|
-
|
|
208
|
-
// Driver: one turn at a time, in submission order. Turns run as child
|
|
209
|
-
// fibers so interrupt() stops the turn without killing the driver.
|
|
210
|
-
const driver = Effect.gen(function* () {
|
|
211
|
-
while (true) {
|
|
212
|
-
const text = yield* Queue.take(inbox);
|
|
213
|
-
state.dispatching = true;
|
|
214
|
-
state.pending.shift();
|
|
215
|
-
yield* emit({ _tag: "QueueChanged", queued: queuedView() });
|
|
216
|
-
yield* emit({ _tag: "UserMessage", text });
|
|
217
|
-
const turn = state.turnCount++;
|
|
218
|
-
const fiber = yield* Effect.forkChild(
|
|
219
|
-
runTurn(text, turn).pipe(
|
|
220
|
-
Effect.onInterrupt(() =>
|
|
221
|
-
emit({
|
|
222
|
-
_tag: "RunSettled",
|
|
223
|
-
outcome: { _tag: "Interrupted" },
|
|
224
|
-
}).pipe(Effect.ignore),
|
|
225
|
-
),
|
|
226
|
-
),
|
|
227
|
-
);
|
|
228
|
-
yield* Ref.set(activeTurn, fiber);
|
|
229
|
-
state.dispatching = false;
|
|
230
|
-
yield* Fiber.await(fiber);
|
|
231
|
-
yield* Ref.set(activeTurn, undefined);
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
yield* Effect.forkScoped(driver.pipe(Effect.ignore));
|
|
235
|
-
|
|
236
|
-
yield* Effect.addFinalizer(() =>
|
|
237
|
-
Effect.gen(function* () {
|
|
238
|
-
state.closed = true;
|
|
239
|
-
yield* Queue.end(inbox).pipe(Effect.ignore);
|
|
240
|
-
yield* Queue.end(events).pipe(Effect.ignore);
|
|
241
|
-
}),
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
const submit = (text: string) =>
|
|
245
|
-
Effect.gen(function* () {
|
|
246
|
-
if (state.closed) {
|
|
247
|
-
return yield* new SendError({
|
|
248
|
-
message: "Subagent session is closed.",
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
state.pending.push(text);
|
|
252
|
-
const busy = (yield* Ref.get(activeTurn)) !== undefined;
|
|
253
|
-
if (busy) {
|
|
254
|
-
// Show the queued steer line until the driver picks it up.
|
|
255
|
-
yield* emit({ _tag: "QueueChanged", queued: queuedView() });
|
|
256
|
-
}
|
|
257
|
-
yield* Queue.offer(inbox, text);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Announce metadata, then kick off the initial run.
|
|
261
|
-
yield* Effect.try(() => fs.mkdirSync(STUB_DIR, { recursive: true })).pipe(
|
|
262
|
-
Effect.ignore, // The fake session file directory is best-effort.
|
|
263
|
-
);
|
|
264
|
-
yield* emit({ _tag: "MetaChanged", meta: state.meta });
|
|
265
|
-
// The session cannot be closed yet, so the initial submit cannot fail.
|
|
266
|
-
yield* submit(task.prompt).pipe(Effect.orDie);
|
|
267
|
-
|
|
268
|
-
return {
|
|
269
|
-
meta: Effect.sync(() => state.meta),
|
|
270
|
-
events: Stream.fromQueue(events),
|
|
271
|
-
send: submit,
|
|
272
|
-
interrupt: Effect.gen(function* () {
|
|
273
|
-
// Drop queued prompts so interrupting cannot immediately start
|
|
274
|
-
// another turn, then stop the active turn. A prompt may be mid-flight
|
|
275
|
-
// between the driver dequeuing it and registering its fiber, so wait
|
|
276
|
-
// that window out instead of silently missing the turn.
|
|
277
|
-
const cleared = yield* Queue.clear(inbox).pipe(
|
|
278
|
-
Effect.orElseSucceed(() => []),
|
|
279
|
-
);
|
|
280
|
-
state.pending = [];
|
|
281
|
-
yield* emit({ _tag: "QueueChanged", queued: [] });
|
|
282
|
-
while (true) {
|
|
283
|
-
const fiber = yield* Ref.get(activeTurn);
|
|
284
|
-
if (fiber) {
|
|
285
|
-
yield* Fiber.interrupt(fiber);
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
if (!state.dispatching) {
|
|
289
|
-
// No turn ever started. If we cancelled queued prompts, the run
|
|
290
|
-
// still needs a terminal event or it would look running forever.
|
|
291
|
-
if (cleared.length > 0) {
|
|
292
|
-
yield* emit({
|
|
293
|
-
_tag: "RunSettled",
|
|
294
|
-
outcome: { _tag: "Interrupted" },
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
yield* Effect.sleep(Duration.millis(5));
|
|
300
|
-
}
|
|
301
|
-
}),
|
|
302
|
-
} satisfies SubagentSession;
|
|
303
|
-
});
|