@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.
Files changed (109) hide show
  1. package/README.md +102 -40
  2. package/SETUP.md +22 -6
  3. package/assets/openpi-launch-card-v1.webp +0 -0
  4. package/bin/openpi.js +145 -0
  5. package/extensions/background-terminals/index.ts +30 -2
  6. package/extensions/background-terminals/src/domain.ts +2 -0
  7. package/extensions/background-terminals/src/manager.ts +486 -106
  8. package/extensions/background-terminals/src/output.ts +33 -0
  9. package/extensions/background-terminals/src/prompt.ts +13 -5
  10. package/extensions/background-terminals/src/result-delivery.ts +4 -1
  11. package/extensions/clear-context/index.ts +83 -0
  12. package/extensions/context-pivot/index.ts +16 -6
  13. package/extensions/cron/schedule.ts +7 -1
  14. package/extensions/file-mutation-display/render.ts +17 -257
  15. package/extensions/file-search/src/binaries.ts +57 -41
  16. package/extensions/git-read/index.ts +1 -3
  17. package/extensions/model-info/index.ts +21 -33
  18. package/extensions/model-info/session-metrics.ts +96 -0
  19. package/extensions/plan-mode/bash-policy.ts +54 -9
  20. package/extensions/plan-mode/index.ts +7 -2
  21. package/extensions/post-edit/index.ts +16 -6
  22. package/extensions/sessions/git-stats.ts +258 -72
  23. package/extensions/sessions/index.ts +153 -86
  24. package/extensions/sessions/preview-cache.ts +104 -0
  25. package/extensions/sessions/preview-loader.ts +856 -0
  26. package/extensions/sessions/sessions.ts +43 -4
  27. package/extensions/setup/index.ts +123 -127
  28. package/extensions/shared/activity-status.ts +30 -0
  29. package/extensions/shared/agent-session-page.ts +319 -0
  30. package/extensions/shared/agent-tool-renderer.ts +218 -0
  31. package/extensions/shared/agent-transcript.ts +524 -0
  32. package/extensions/shared/capability-intent.ts +1 -1
  33. package/extensions/shared/child-session.ts +437 -21
  34. package/extensions/shared/result-delivery.ts +34 -0
  35. package/extensions/shared/setup-config.ts +73 -33
  36. package/extensions/shared/setup-episode-state.ts +1 -1
  37. package/extensions/shared/terminal-text.ts +110 -23
  38. package/extensions/shared/text-projection.ts +72 -15
  39. package/extensions/shared/tool-activity.ts +382 -0
  40. package/extensions/shared/tool-surface.ts +29 -2
  41. package/extensions/shared/transcript-viewport.ts +46 -0
  42. package/extensions/shared/web-observer-registry.ts +390 -0
  43. package/extensions/shared/worktree.ts +11 -0
  44. package/extensions/subagents/index.ts +270 -59
  45. package/extensions/subagents/navigation.ts +34 -5
  46. package/extensions/subagents/src/backend.ts +12 -1
  47. package/extensions/subagents/src/backends/pi.ts +375 -66
  48. package/extensions/subagents/src/domain.ts +5 -0
  49. package/extensions/subagents/src/manager.ts +34 -2
  50. package/extensions/subagents/src/prompt.ts +32 -4
  51. package/extensions/subagents/src/result-artifact.ts +4 -0
  52. package/extensions/subagents/src/result-delivery.ts +7 -1
  53. package/extensions/subagents/src/runtime.ts +15 -1
  54. package/extensions/subagents/src/ui/takeover.ts +73 -257
  55. package/extensions/subagents/src/ui/transcript.ts +38 -535
  56. package/extensions/subagents/src/ui/wait-result.ts +103 -15
  57. package/extensions/suggestions/src/ui.ts +10 -4
  58. package/extensions/tasks/index.ts +0 -3
  59. package/extensions/ui-customization/footer.ts +0 -40
  60. package/extensions/ui-customization/index.ts +0 -4
  61. package/extensions/user-input-fold/index.ts +1 -1
  62. package/extensions/web/index.ts +234 -0
  63. package/extensions/workflows/artifacts.ts +137 -47
  64. package/extensions/workflows/completion-projection.ts +457 -0
  65. package/extensions/workflows/coordinator.ts +8 -10
  66. package/extensions/workflows/dashboard.ts +167 -228
  67. package/extensions/workflows/handoff.ts +70 -16
  68. package/extensions/workflows/index.ts +488 -198
  69. package/extensions/workflows/journal.ts +148 -13
  70. package/extensions/workflows/model.ts +74 -4
  71. package/extensions/workflows/navigation.ts +32 -8
  72. package/extensions/workflows/progress-projection.ts +306 -0
  73. package/extensions/workflows/prompt.ts +66 -6
  74. package/extensions/workflows/replay-safety.ts +42 -21
  75. package/extensions/workflows/result-delivery.ts +128 -64
  76. package/extensions/workflows/retention.ts +593 -0
  77. package/extensions/workflows/runner.ts +388 -279
  78. package/extensions/workflows/sandbox-child.cjs +25 -3
  79. package/extensions/workflows/sandbox.ts +62 -8
  80. package/extensions/workflows/serialization.ts +325 -17
  81. package/extensions/workflows/tool-renderer.ts +22 -0
  82. package/extensions/workflows/transcript.ts +149 -0
  83. package/extensions/workspace-cleanup-guard/index.ts +54 -0
  84. package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
  85. package/package.json +28 -8
  86. package/skills/subagents/REFERENCE.md +189 -0
  87. package/skills/subagents/SKILL.md +1 -1
  88. package/skills/workflows/REFERENCE.md +4 -2
  89. package/web/adapter/pi-adapter.ts +661 -0
  90. package/web/host/browser-launcher.ts +20 -0
  91. package/web/host/static-assets.ts +4 -0
  92. package/web/host/terminal-status.ts +38 -0
  93. package/web/host/web-host.ts +789 -0
  94. package/web/http-dispatcher.ts +125 -0
  95. package/web/protocol/types.ts +462 -0
  96. package/web/runtime/pi-runtime.ts +991 -0
  97. package/web/runtime/types.ts +71 -0
  98. package/web/runtime/web-host-lease.ts +497 -0
  99. package/web/trace.ts +18 -0
  100. package/web/ui/app.js +1398 -0
  101. package/web/ui/index.html +139 -0
  102. package/web/ui/styles.css +598 -0
  103. package/web/vite.config.mjs +34 -0
  104. package/extensions/execution-convergence/active-evidence.ts +0 -129
  105. package/extensions/execution-convergence/index.ts +0 -442
  106. package/extensions/execution-convergence/workspace-provenance.ts +0 -338
  107. package/extensions/setup/intercom-fs-helper.cjs +0 -130
  108. package/extensions/setup/intercom.ts +0 -603
  109. 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
- });