@tt-a1i/openpi 0.4.0 → 0.6.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 (141) hide show
  1. package/README.md +116 -46
  2. package/SETUP.md +29 -7
  3. package/THIRD_PARTY_NOTICES.md +16 -0
  4. package/assets/openpi-launch-card-v1.webp +0 -0
  5. package/bin/openpi.js +155 -0
  6. package/extensions/ai-providers/LICENSE.upstream +23 -0
  7. package/extensions/ai-providers/README.md +59 -0
  8. package/extensions/ai-providers/antigravity/credentials.ts +52 -0
  9. package/extensions/ai-providers/antigravity/discovery.ts +130 -0
  10. package/extensions/ai-providers/antigravity/google-conversion.ts +455 -0
  11. package/extensions/ai-providers/antigravity/models.ts +84 -0
  12. package/extensions/ai-providers/antigravity/oauth.ts +700 -0
  13. package/extensions/ai-providers/antigravity/provider.ts +1116 -0
  14. package/extensions/ai-providers/antigravity/routing.ts +340 -0
  15. package/extensions/ai-providers/antigravity/with-resolvers.d.ts +19 -0
  16. package/extensions/ai-providers/cursor/constants.ts +5 -0
  17. package/extensions/ai-providers/cursor/credentials.ts +14 -0
  18. package/extensions/ai-providers/cursor/discovery.ts +291 -0
  19. package/extensions/ai-providers/cursor/input-images.ts +106 -0
  20. package/extensions/ai-providers/cursor/models.ts +45 -0
  21. package/extensions/ai-providers/cursor/oauth.ts +263 -0
  22. package/extensions/ai-providers/cursor/proto.ts +1064 -0
  23. package/extensions/ai-providers/cursor/protobuf.ts +1171 -0
  24. package/extensions/ai-providers/cursor/provider.ts +1175 -0
  25. package/extensions/ai-providers/cursor/proxy.ts +213 -0
  26. package/extensions/ai-providers/cursor/with-resolvers.d.ts +12 -0
  27. package/extensions/ai-providers/index.ts +86 -0
  28. package/extensions/ai-providers/oauth-adapter.ts +81 -0
  29. package/extensions/ai-providers/usage.ts +10 -0
  30. package/extensions/background-terminals/index.ts +38 -3
  31. package/extensions/background-terminals/src/domain.ts +2 -0
  32. package/extensions/background-terminals/src/manager.ts +484 -106
  33. package/extensions/background-terminals/src/output.ts +33 -0
  34. package/extensions/background-terminals/src/prompt.ts +13 -5
  35. package/extensions/background-terminals/src/result-delivery.ts +47 -24
  36. package/extensions/clear-context/index.ts +83 -0
  37. package/extensions/context-pivot/index.ts +16 -6
  38. package/extensions/cron/index.ts +68 -27
  39. package/extensions/cron/schedule.ts +12 -2
  40. package/extensions/file-mutation-display/render.ts +17 -257
  41. package/extensions/file-search/src/binaries.ts +57 -41
  42. package/extensions/git-read/index.ts +1 -3
  43. package/extensions/model-info/cache-diagnostics.ts +220 -0
  44. package/extensions/model-info/index.ts +65 -33
  45. package/extensions/model-info/session-metrics.ts +96 -0
  46. package/extensions/plan-mode/bash-policy.ts +54 -9
  47. package/extensions/plan-mode/index.ts +82 -6
  48. package/extensions/post-edit/index.ts +16 -6
  49. package/extensions/sessions/git-stats.ts +258 -72
  50. package/extensions/sessions/index.ts +153 -86
  51. package/extensions/sessions/preview-cache.ts +104 -0
  52. package/extensions/sessions/preview-loader.ts +856 -0
  53. package/extensions/sessions/sessions.ts +43 -4
  54. package/extensions/setup/index.ts +138 -130
  55. package/extensions/shared/activity-status.ts +30 -0
  56. package/extensions/shared/agent-session-page.ts +319 -0
  57. package/extensions/shared/agent-tool-renderer.ts +218 -0
  58. package/extensions/shared/agent-transcript.ts +524 -0
  59. package/extensions/shared/capability-intent.ts +1 -1
  60. package/extensions/shared/child-session.ts +457 -21
  61. package/extensions/shared/completion-inbox.ts +193 -0
  62. package/extensions/shared/result-delivery.ts +34 -0
  63. package/extensions/shared/setup-config.ts +83 -34
  64. package/extensions/shared/setup-episode-state.ts +1 -1
  65. package/extensions/shared/structured-output.ts +154 -0
  66. package/extensions/shared/terminal-text.ts +110 -23
  67. package/extensions/shared/text-projection.ts +72 -15
  68. package/extensions/shared/tool-activity.ts +382 -0
  69. package/extensions/shared/tool-surface.ts +29 -2
  70. package/extensions/shared/transcript-viewport.ts +46 -0
  71. package/extensions/shared/web-observer-registry.ts +390 -0
  72. package/extensions/shared/worktree.ts +11 -0
  73. package/extensions/subagents/index.ts +313 -62
  74. package/extensions/subagents/navigation.ts +34 -5
  75. package/extensions/subagents/src/backend.ts +12 -1
  76. package/extensions/subagents/src/backends/pi.ts +450 -70
  77. package/extensions/subagents/src/domain.ts +21 -1
  78. package/extensions/subagents/src/manager.ts +39 -2
  79. package/extensions/subagents/src/prompt.ts +49 -7
  80. package/extensions/subagents/src/result-artifact.ts +36 -0
  81. package/extensions/subagents/src/result-delivery.ts +39 -14
  82. package/extensions/subagents/src/runtime.ts +15 -1
  83. package/extensions/subagents/src/ui/takeover.ts +73 -257
  84. package/extensions/subagents/src/ui/transcript.ts +38 -535
  85. package/extensions/subagents/src/ui/wait-result.ts +103 -15
  86. package/extensions/suggestions/src/ui.ts +10 -4
  87. package/extensions/tasks/index.ts +0 -3
  88. package/extensions/ui-customization/footer.ts +16 -45
  89. package/extensions/ui-customization/index.ts +0 -4
  90. package/extensions/user-input-fold/index.ts +42 -6
  91. package/extensions/web/index.ts +257 -0
  92. package/extensions/workflows/acceptance.ts +43 -19
  93. package/extensions/workflows/artifacts.ts +137 -47
  94. package/extensions/workflows/completion-projection.ts +459 -0
  95. package/extensions/workflows/coordinator.ts +8 -10
  96. package/extensions/workflows/dashboard.ts +175 -228
  97. package/extensions/workflows/handoff.ts +70 -16
  98. package/extensions/workflows/index.ts +501 -198
  99. package/extensions/workflows/journal.ts +148 -13
  100. package/extensions/workflows/model.ts +79 -5
  101. package/extensions/workflows/navigation.ts +32 -8
  102. package/extensions/workflows/progress-projection.ts +306 -0
  103. package/extensions/workflows/prompt.ts +70 -16
  104. package/extensions/workflows/replay-safety.ts +42 -21
  105. package/extensions/workflows/result-delivery.ts +214 -76
  106. package/extensions/workflows/retention.ts +599 -0
  107. package/extensions/workflows/runner.ts +389 -345
  108. package/extensions/workflows/sandbox-child.cjs +25 -3
  109. package/extensions/workflows/sandbox.ts +62 -8
  110. package/extensions/workflows/serialization.ts +325 -17
  111. package/extensions/workflows/tool-renderer.ts +22 -0
  112. package/extensions/workflows/transcript.ts +149 -0
  113. package/extensions/workspace-cleanup-guard/index.ts +54 -0
  114. package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
  115. package/package.json +34 -14
  116. package/skills/subagents/REFERENCE.md +190 -0
  117. package/skills/subagents/SKILL.md +2 -1
  118. package/skills/workflows/REFERENCE.md +6 -4
  119. package/skills/workflows/SKILL.md +1 -1
  120. package/web/adapter/pi-adapter.ts +664 -0
  121. package/web/host/browser-launcher.ts +20 -0
  122. package/web/host/pi-coding-agent-entry.ts +162 -0
  123. package/web/host/static-assets.ts +4 -0
  124. package/web/host/terminal-status.ts +38 -0
  125. package/web/host/web-host.ts +1069 -0
  126. package/web/http-dispatcher.ts +125 -0
  127. package/web/protocol/types.ts +467 -0
  128. package/web/runtime/pi-runtime.ts +1206 -0
  129. package/web/runtime/types.ts +102 -0
  130. package/web/runtime/web-host-lease.ts +497 -0
  131. package/web/trace.ts +18 -0
  132. package/web/ui/app.js +1700 -0
  133. package/web/ui/index.html +142 -0
  134. package/web/ui/styles.css +680 -0
  135. package/web/vite.config.mjs +34 -0
  136. package/extensions/execution-convergence/active-evidence.ts +0 -129
  137. package/extensions/execution-convergence/index.ts +0 -442
  138. package/extensions/execution-convergence/workspace-provenance.ts +0 -338
  139. package/extensions/setup/intercom-fs-helper.cjs +0 -130
  140. package/extensions/setup/intercom.ts +0 -603
  141. package/extensions/subagents/src/backends/stub.ts +0 -303
@@ -1,338 +0,0 @@
1
- import { lstat } from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- type Origin = "baseline" | "session_created";
5
-
6
- interface PendingEffects {
7
- creations: Array<{
8
- path: string;
9
- existed: boolean;
10
- observeOnCommandError: boolean;
11
- }>;
12
- removals: string[];
13
- }
14
-
15
- interface Options {
16
- confirmDelete: (paths: readonly string[]) => Promise<boolean>;
17
- }
18
-
19
- interface BashAttempt {
20
- id: string;
21
- command: string;
22
- cwd: string;
23
- }
24
-
25
- interface WriteAttempt {
26
- id: string;
27
- path: string;
28
- cwd: string;
29
- }
30
-
31
- interface ShellInspection {
32
- creations: string[];
33
- removals: string[];
34
- opaqueDestructiveCommand: boolean;
35
- }
36
-
37
- const DYNAMIC_PATH = /[*?[$`]/u;
38
-
39
- function splitShellSegments(command: string) {
40
- const segments: string[] = [];
41
- let current = "";
42
- let quote: "'" | '"' | undefined;
43
- let escaped = false;
44
- for (let index = 0; index < command.length; index += 1) {
45
- const char = command[index] ?? "";
46
- const next = command[index + 1] ?? "";
47
- if (escaped) {
48
- current += char;
49
- escaped = false;
50
- continue;
51
- }
52
- if (char === "\\" && quote !== "'") {
53
- current += char;
54
- escaped = true;
55
- continue;
56
- }
57
- if (quote) {
58
- current += char;
59
- if (char === quote) quote = undefined;
60
- continue;
61
- }
62
- if (char === "'" || char === '"') {
63
- quote = char;
64
- current += char;
65
- continue;
66
- }
67
- if (
68
- char === "\n" ||
69
- char === ";" ||
70
- (char === "&" && next === "&") ||
71
- (char === "|" && next === "|")
72
- ) {
73
- if (current.trim()) segments.push(current.trim());
74
- current = "";
75
- if ((char === "&" || char === "|") && next === char) index += 1;
76
- continue;
77
- }
78
- current += char;
79
- }
80
- if (current.trim()) segments.push(current.trim());
81
- return segments;
82
- }
83
-
84
- function shellTokens(segment: string) {
85
- const tokens: string[] = [];
86
- let current = "";
87
- let quote: "'" | '"' | undefined;
88
- let escaped = false;
89
- const push = () => {
90
- if (!current) return;
91
- tokens.push(current);
92
- current = "";
93
- };
94
- for (const char of segment) {
95
- if (escaped) {
96
- current += char;
97
- escaped = false;
98
- continue;
99
- }
100
- if (char === "\\" && quote !== "'") {
101
- escaped = true;
102
- continue;
103
- }
104
- if (quote) {
105
- if (char === quote) quote = undefined;
106
- else current += char;
107
- continue;
108
- }
109
- if (char === "'" || char === '"') {
110
- quote = char;
111
- continue;
112
- }
113
- if (/\s/u.test(char)) {
114
- push();
115
- continue;
116
- }
117
- if (char === ">") {
118
- push();
119
- tokens.push(">");
120
- continue;
121
- }
122
- current += char;
123
- }
124
- push();
125
- return tokens;
126
- }
127
-
128
- function literalPath(value: string | undefined) {
129
- if (!value || value === "--" || DYNAMIC_PATH.test(value)) return undefined;
130
- return value;
131
- }
132
-
133
- function inspectShell(command: string): ShellInspection {
134
- const creations = new Set<string>();
135
- const removals = new Set<string>();
136
- let opaqueDestructiveCommand = false;
137
- for (const segment of splitShellSegments(command)) {
138
- const tokens = shellTokens(segment);
139
- const nestedBash = tokens.findIndex(
140
- (token, index) =>
141
- token.split("/").at(-1) === "bash" && tokens[index + 1] === "-c",
142
- );
143
- if (nestedBash >= 0) {
144
- const nestedCommand = tokens[nestedBash + 2];
145
- if (nestedCommand) {
146
- const nested: ShellInspection = inspectShell(nestedCommand);
147
- for (const target of nested.creations) creations.add(target);
148
- for (const target of nested.removals) removals.add(target);
149
- opaqueDestructiveCommand ||= nested.opaqueDestructiveCommand;
150
- }
151
- continue;
152
- }
153
- for (let index = 0; index < tokens.length; index += 1) {
154
- if (tokens[index] !== ">") continue;
155
- const target = literalPath(tokens[index + 1]);
156
- if (target) creations.add(target);
157
- }
158
-
159
- const executable = tokens[0]?.split("/").at(-1);
160
- if (executable === "mkdir") {
161
- const targets: string[] = [];
162
- let afterOptions = false;
163
- let supported = true;
164
- for (const token of tokens.slice(1)) {
165
- if (!afterOptions && token === "--") {
166
- afterOptions = true;
167
- continue;
168
- }
169
- if (!afterOptions && (token === "-p" || token === "--parents")) {
170
- continue;
171
- }
172
- if (!afterOptions && token.startsWith("-")) {
173
- supported = false;
174
- break;
175
- }
176
- const target = literalPath(token);
177
- if (!target) {
178
- supported = false;
179
- break;
180
- }
181
- targets.push(target);
182
- }
183
- if (supported) {
184
- for (const target of targets) creations.add(target);
185
- }
186
- }
187
- if (executable !== "rm") continue;
188
- let sawLiteral = false;
189
- let afterOptions = false;
190
- for (const token of tokens.slice(1)) {
191
- if (!afterOptions && token === "--") {
192
- afterOptions = true;
193
- continue;
194
- }
195
- if (!afterOptions && token.startsWith("-")) continue;
196
- const target = literalPath(token);
197
- if (!target) {
198
- opaqueDestructiveCommand = true;
199
- continue;
200
- }
201
- sawLiteral = true;
202
- removals.add(target);
203
- }
204
- if (!sawLiteral) opaqueDestructiveCommand = true;
205
- }
206
- if (/\brm\b/u.test(command) && removals.size === 0) {
207
- opaqueDestructiveCommand = true;
208
- }
209
- return {
210
- creations: [...creations],
211
- removals: [...removals],
212
- opaqueDestructiveCommand,
213
- };
214
- }
215
-
216
- function containedPath(cwd: string, candidate: string) {
217
- const absolute = path.resolve(cwd, candidate);
218
- const relative = path.relative(cwd, absolute);
219
- if (
220
- relative === "" ||
221
- relative.startsWith("..") ||
222
- path.isAbsolute(relative)
223
- ) {
224
- return undefined;
225
- }
226
- return { absolute, relative: relative.split(path.sep).join("/") };
227
- }
228
-
229
- async function exists(candidate: string) {
230
- try {
231
- await lstat(candidate);
232
- return true;
233
- } catch (error) {
234
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
235
- return false;
236
- }
237
- throw error;
238
- }
239
- }
240
-
241
- export function createWorkspaceCleanupGuard(options: Options) {
242
- const origins = new Map<string, Origin>();
243
- const pending = new Map<string, PendingEffects>();
244
-
245
- const prepareCreation = async (
246
- cwd: string,
247
- candidate: string,
248
- observeOnCommandError: boolean,
249
- ) => {
250
- const contained = containedPath(cwd, candidate);
251
- if (!contained) return undefined;
252
- const existed = await exists(contained.absolute);
253
- if (existed && !origins.has(contained.absolute)) {
254
- origins.set(contained.absolute, "baseline");
255
- }
256
- return { path: contained.absolute, existed, observeOnCommandError };
257
- };
258
-
259
- return {
260
- async beforeWrite(attempt: WriteAttempt) {
261
- const creation = await prepareCreation(attempt.cwd, attempt.path, false);
262
- pending.set(attempt.id, {
263
- creations: creation ? [creation] : [],
264
- removals: [],
265
- });
266
- },
267
-
268
- async before(attempt: BashAttempt) {
269
- const inspected = inspectShell(attempt.command);
270
- const creations: PendingEffects["creations"] = [];
271
- for (const candidate of inspected.creations) {
272
- const creation = await prepareCreation(attempt.cwd, candidate, true);
273
- if (creation) creations.push(creation);
274
- }
275
-
276
- const removals: string[] = [];
277
- const protectedPaths: string[] = [];
278
- for (const candidate of inspected.removals) {
279
- const contained = containedPath(attempt.cwd, candidate);
280
- if (!contained) continue;
281
- const origin = origins.get(contained.absolute);
282
- const present = await exists(contained.absolute);
283
- if (present && !origin) origins.set(contained.absolute, "baseline");
284
- if (present && origins.get(contained.absolute) !== "session_created") {
285
- protectedPaths.push(contained.relative);
286
- }
287
- removals.push(contained.absolute);
288
- }
289
-
290
- if (
291
- protectedPaths.length > 0 &&
292
- !(await options.confirmDelete(protectedPaths))
293
- ) {
294
- return {
295
- kind: "block" as const,
296
- protectedPaths,
297
- reason: `Blocked cleanup: ${protectedPaths.join(", ")} existed before this agent changed it and is not proven session-created scratch. Retry the cleanup without that path, or obtain explicit user confirmation to delete it.`,
298
- opaqueDestructiveCommand: inspected.opaqueDestructiveCommand,
299
- };
300
- }
301
-
302
- pending.set(attempt.id, { creations, removals });
303
- return {
304
- kind: "allow" as const,
305
- opaqueDestructiveCommand: inspected.opaqueDestructiveCommand,
306
- };
307
- },
308
-
309
- async after(result: { id: string; isError: boolean }) {
310
- const effects = pending.get(result.id);
311
- pending.delete(result.id);
312
- if (!effects) return;
313
- for (const creation of effects.creations) {
314
- if (
315
- !creation.existed &&
316
- (!result.isError || creation.observeOnCommandError) &&
317
- !origins.has(creation.path) &&
318
- (await exists(creation.path))
319
- ) {
320
- origins.set(creation.path, "session_created");
321
- }
322
- }
323
- for (const removed of effects.removals) {
324
- if (
325
- origins.get(removed) === "session_created" &&
326
- !(await exists(removed))
327
- ) {
328
- origins.delete(removed);
329
- }
330
- }
331
- },
332
-
333
- reset() {
334
- origins.clear();
335
- pending.clear();
336
- },
337
- };
338
- }
@@ -1,130 +0,0 @@
1
- const { randomUUID } = require("node:crypto");
2
- const {
3
- constants,
4
- closeSync,
5
- fstatSync,
6
- openSync,
7
- readFileSync,
8
- renameSync,
9
- statSync,
10
- unlinkSync,
11
- writeFileSync,
12
- } = require("node:fs");
13
-
14
- const [operation, expectedDev, expectedIno, name, encoded] =
15
- process.argv.slice(2);
16
- const allowedNames = new Set([
17
- "config.json",
18
- "config.json.openpi-install.lock",
19
- ]);
20
- const fail = (code, message) => {
21
- process.stderr.write(`OPENPI:${code}:${message}\n`);
22
- process.exit(1);
23
- };
24
- const identityMatches = () => {
25
- const current = statSync(".", { bigint: true });
26
- return (
27
- current.isDirectory() &&
28
- String(current.dev) === expectedDev &&
29
- String(current.ino) === expectedIno
30
- );
31
- };
32
-
33
- if (!allowedNames.has(name)) fail("INPUT", "unsupported file name");
34
- if (!identityMatches()) fail("IDENTITY", "directory identity mismatch");
35
-
36
- const payload = Buffer.from(encoded ?? "", "base64");
37
- if (payload.length > 8_192) fail("INPUT", "payload too large");
38
-
39
- if (operation === "create") {
40
- let fd;
41
- try {
42
- fd = openSync(
43
- name,
44
- constants.O_WRONLY |
45
- constants.O_CREAT |
46
- constants.O_EXCL |
47
- (constants.O_NOFOLLOW ?? 0),
48
- 0o600,
49
- );
50
- } catch (error) {
51
- if (error && error.code === "EEXIST") fail("EEXIST", "file exists");
52
- fail("IO", error instanceof Error ? error.message : String(error));
53
- }
54
- try {
55
- const metadata = fstatSync(fd);
56
- if (!metadata.isFile()) fail("TYPE", "created path is not a regular file");
57
- writeFileSync(fd, payload);
58
- } finally {
59
- closeSync(fd);
60
- }
61
- if (!identityMatches()) fail("IDENTITY", "directory identity changed");
62
- process.exit(0);
63
- }
64
-
65
- if (operation === "remove-owned") {
66
- // Atomically move the current lock to a unique claim before inspecting it.
67
- // A replacement lock can then appear at `name`, but cleanup never unlinks
68
- // that pathname: it removes only inode-pinned, randomly named claims.
69
- const claim = `.${name}.release.${process.pid}.${randomUUID()}`;
70
- const markerClaim = `${claim}.marker`;
71
- const releaseToken = Buffer.from(`release:${process.pid}:${randomUUID()}\n`);
72
- try {
73
- renameSync(name, claim);
74
- } catch (error) {
75
- fail("IO", error instanceof Error ? error.message : String(error));
76
- }
77
-
78
- let markerCreated = false;
79
- let markerFd;
80
- try {
81
- markerFd = openSync(
82
- name,
83
- constants.O_WRONLY |
84
- constants.O_CREAT |
85
- constants.O_EXCL |
86
- (constants.O_NOFOLLOW ?? 0),
87
- 0o600,
88
- );
89
- writeFileSync(markerFd, releaseToken);
90
- markerCreated = true;
91
- } catch (error) {
92
- if (!error || error.code !== "EEXIST") {
93
- fail("IO", error instanceof Error ? error.message : String(error));
94
- }
95
- } finally {
96
- if (markerFd !== undefined) closeSync(markerFd);
97
- }
98
-
99
- let claimFd;
100
- try {
101
- claimFd = openSync(claim, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
102
- if (!fstatSync(claimFd).isFile())
103
- fail("TYPE", "claim is not a regular file");
104
- if (!readFileSync(claimFd).equals(payload))
105
- fail("OWNER", "owner token mismatch");
106
- } finally {
107
- if (claimFd !== undefined) closeSync(claimFd);
108
- }
109
- unlinkSync(claim);
110
-
111
- if (markerCreated) {
112
- renameSync(name, markerClaim);
113
- let releaseFd;
114
- try {
115
- releaseFd = openSync(
116
- markerClaim,
117
- constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
118
- );
119
- if (!readFileSync(releaseFd).equals(releaseToken))
120
- fail("OWNER", "release marker ownership mismatch");
121
- } finally {
122
- if (releaseFd !== undefined) closeSync(releaseFd);
123
- }
124
- unlinkSync(markerClaim);
125
- }
126
- if (!identityMatches()) fail("IDENTITY", "directory identity changed");
127
- process.exit(0);
128
- }
129
-
130
- fail("INPUT", "unsupported operation");