@tt-a1i/openpi 0.1.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 (114) hide show
  1. package/README.md +643 -0
  2. package/SETUP.md +74 -0
  3. package/THIRD_PARTY_NOTICES.md +16 -0
  4. package/assets/openpi-package.png +0 -0
  5. package/assets/readme-hero-mobile.svg +72 -0
  6. package/assets/readme-hero.svg +118 -0
  7. package/assets/readme-runtime-mobile.svg +91 -0
  8. package/assets/readme-runtime.svg +111 -0
  9. package/extensions/ask-user/handoff.ts +205 -0
  10. package/extensions/ask-user/index.ts +1110 -0
  11. package/extensions/ask-user/limits.ts +89 -0
  12. package/extensions/ask-user/prompt.ts +76 -0
  13. package/extensions/background-terminals/index.ts +653 -0
  14. package/extensions/background-terminals/src/domain.ts +99 -0
  15. package/extensions/background-terminals/src/manager.ts +989 -0
  16. package/extensions/background-terminals/src/output.ts +84 -0
  17. package/extensions/background-terminals/src/prompt.ts +195 -0
  18. package/extensions/background-terminals/src/result-delivery.ts +43 -0
  19. package/extensions/background-terminals/src/runtime.ts +36 -0
  20. package/extensions/background-terminals/src/ui/output-view.ts +55 -0
  21. package/extensions/background-terminals/src/ui/ps.ts +642 -0
  22. package/extensions/background-terminals/src/ui/tool-result.ts +146 -0
  23. package/extensions/background-terminals/src/watch.ts +192 -0
  24. package/extensions/context-pivot/index.ts +222 -0
  25. package/extensions/copy-all/index.ts +65 -0
  26. package/extensions/cron/index.ts +173 -0
  27. package/extensions/cron/schedule.ts +127 -0
  28. package/extensions/file-mutation-display/index.ts +105 -0
  29. package/extensions/file-mutation-display/render.ts +107 -0
  30. package/extensions/file-search/index.ts +515 -0
  31. package/extensions/file-search/src/args.ts +129 -0
  32. package/extensions/file-search/src/binaries.ts +419 -0
  33. package/extensions/file-search/src/output.ts +142 -0
  34. package/extensions/file-search/src/process.ts +309 -0
  35. package/extensions/file-search/src/prompt.ts +53 -0
  36. package/extensions/git-info/index.ts +272 -0
  37. package/extensions/git-info/src/changed-files-view.ts +414 -0
  38. package/extensions/git-info/src/process.ts +107 -0
  39. package/extensions/git-info/src/refresh-coordinator.ts +13 -0
  40. package/extensions/git-info/src/runtime.ts +28 -0
  41. package/extensions/goal/controller.ts +794 -0
  42. package/extensions/goal/index.ts +521 -0
  43. package/extensions/goal/prompts.ts +122 -0
  44. package/extensions/goal/state.ts +763 -0
  45. package/extensions/goal/ui.ts +158 -0
  46. package/extensions/model-info/index.ts +234 -0
  47. package/extensions/plan-mode/bash-policy.ts +313 -0
  48. package/extensions/plan-mode/index.ts +539 -0
  49. package/extensions/post-edit/index.ts +129 -0
  50. package/extensions/sessions/LICENSE.upstream +21 -0
  51. package/extensions/sessions/git-stats.ts +226 -0
  52. package/extensions/sessions/index.ts +1092 -0
  53. package/extensions/sessions/sessions.ts +385 -0
  54. package/extensions/setup/index.ts +408 -0
  55. package/extensions/shared/activity-status.ts +65 -0
  56. package/extensions/shared/below-editor-navigation.ts +343 -0
  57. package/extensions/shared/child-session.ts +352 -0
  58. package/extensions/shared/context-utilization.ts +47 -0
  59. package/extensions/shared/dashboard-state.ts +102 -0
  60. package/extensions/shared/plan-mode-state.ts +65 -0
  61. package/extensions/shared/setup-config.ts +971 -0
  62. package/extensions/shared/subagent-roles.ts +22 -0
  63. package/extensions/shared/terminal-text.ts +38 -0
  64. package/extensions/shared/tool-call-timeout.ts +104 -0
  65. package/extensions/shared/worktree.ts +526 -0
  66. package/extensions/subagents/index.ts +1225 -0
  67. package/extensions/subagents/navigation.ts +121 -0
  68. package/extensions/subagents/src/agent-types.ts +543 -0
  69. package/extensions/subagents/src/backend.ts +63 -0
  70. package/extensions/subagents/src/backends/pi.ts +493 -0
  71. package/extensions/subagents/src/backends/stub.ts +296 -0
  72. package/extensions/subagents/src/by-the-way.ts +21 -0
  73. package/extensions/subagents/src/domain.ts +271 -0
  74. package/extensions/subagents/src/format.ts +48 -0
  75. package/extensions/subagents/src/manager.ts +769 -0
  76. package/extensions/subagents/src/prompt.ts +190 -0
  77. package/extensions/subagents/src/result-delivery.ts +20 -0
  78. package/extensions/subagents/src/runtime.ts +51 -0
  79. package/extensions/subagents/src/ui/takeover.ts +615 -0
  80. package/extensions/subagents/src/ui/transcript.ts +293 -0
  81. package/extensions/subagents/src/ui/wait-result.ts +89 -0
  82. package/extensions/suggestions/index.ts +172 -0
  83. package/extensions/suggestions/src/config.ts +12 -0
  84. package/extensions/suggestions/src/predictor.ts +147 -0
  85. package/extensions/suggestions/src/prompt.ts +20 -0
  86. package/extensions/suggestions/src/transcript.ts +233 -0
  87. package/extensions/suggestions/src/ui.ts +224 -0
  88. package/extensions/tasks/index.ts +512 -0
  89. package/extensions/tasks/tasks.ts +649 -0
  90. package/extensions/tasks/ui.ts +421 -0
  91. package/extensions/turn-time/index.ts +61 -0
  92. package/extensions/ui-customization/footer.ts +512 -0
  93. package/extensions/ui-customization/index.ts +217 -0
  94. package/extensions/workflows/acceptance.ts +298 -0
  95. package/extensions/workflows/artifacts.ts +225 -0
  96. package/extensions/workflows/controller.ts +210 -0
  97. package/extensions/workflows/dashboard.ts +1226 -0
  98. package/extensions/workflows/index.ts +1884 -0
  99. package/extensions/workflows/journal.ts +188 -0
  100. package/extensions/workflows/meta.ts +250 -0
  101. package/extensions/workflows/model.ts +423 -0
  102. package/extensions/workflows/navigation.ts +93 -0
  103. package/extensions/workflows/prompt.ts +212 -0
  104. package/extensions/workflows/replay-safety.ts +577 -0
  105. package/extensions/workflows/runner.ts +786 -0
  106. package/extensions/workflows/sandbox-child.cjs +402 -0
  107. package/extensions/workflows/sandbox.ts +397 -0
  108. package/extensions/workflows/serialization.ts +162 -0
  109. package/extensions/workflows/worktree-handoff.ts +216 -0
  110. package/package.json +87 -0
  111. package/scripts/prepare-effect-tsgo.mjs +16 -0
  112. package/skills/background-terminals/SKILL.md +30 -0
  113. package/skills/subagents/SKILL.md +15 -0
  114. package/themes/github-dark-default.json +89 -0
@@ -0,0 +1,385 @@
1
+ import { sanitizeTerminalText } from "../shared/terminal-text.ts";
2
+
3
+ export interface SessionInfoLike {
4
+ id: string;
5
+ name?: string;
6
+ cwd: string;
7
+ modified: Date;
8
+ created?: Date;
9
+ firstMessage: string;
10
+ path: string;
11
+ messageCount?: number;
12
+ }
13
+
14
+ export type PreviewContent =
15
+ | string
16
+ | Array<
17
+ | { type: "text"; text: string }
18
+ | { type: "image"; mimeType?: string }
19
+ | { type: "toolCall"; name: string; arguments?: Record<string, unknown> }
20
+ | { type: "thinking"; thinking?: string; redacted?: boolean }
21
+ >;
22
+
23
+ export interface PreviewMessageLike {
24
+ role: string;
25
+ content?: PreviewContent;
26
+ toolName?: string;
27
+ isError?: boolean;
28
+ command?: string;
29
+ output?: string;
30
+ summary?: string;
31
+ }
32
+
33
+ export type PreviewBlock =
34
+ | { kind: "notice"; text: string }
35
+ | { kind: "user"; text: string }
36
+ | { kind: "assistant"; text: string }
37
+ | { kind: "thinking"; text: string; redacted?: boolean }
38
+ | { kind: "toolCall"; name: string; args?: string }
39
+ | { kind: "toolResult"; name?: string; text: string; isError?: boolean }
40
+ | { kind: "bash"; command: string; output?: string; isError?: boolean }
41
+ | { kind: "summary"; label: string; text: string }
42
+ | { kind: "custom"; label: string; text: string };
43
+
44
+ export interface SessionPreview {
45
+ title: string;
46
+ subtitle: string;
47
+ blocks: PreviewBlock[];
48
+ error?: string;
49
+ }
50
+
51
+ export interface SessionPaneLayout {
52
+ mode: "single" | "split";
53
+ listWidth: number;
54
+ previewWidth: number;
55
+ }
56
+
57
+ export function parseLimit(args: string | undefined, defaultLimit = 5): number {
58
+ if (!args) return defaultLimit;
59
+ const parsed = Number.parseInt(args.trim(), 10);
60
+ if (!Number.isFinite(parsed) || parsed < 1) return defaultLimit;
61
+ return parsed;
62
+ }
63
+
64
+ const pad = (value: number): string => value.toString().padStart(2, "0");
65
+
66
+ export function formatTimestamp(date: Date): string {
67
+ const year = date.getFullYear();
68
+ const month = pad(date.getMonth() + 1);
69
+ const day = pad(date.getDate());
70
+ const hours = pad(date.getHours());
71
+ const minutes = pad(date.getMinutes());
72
+ return `${year}-${month}-${day} ${hours}:${minutes}`;
73
+ }
74
+
75
+ export function formatRelativeTime(date: Date): string {
76
+ const now = new Date();
77
+ const diffMs = now.getTime() - date.getTime();
78
+ const diffSec = Math.floor(diffMs / 1000);
79
+ const diffMin = Math.floor(diffSec / 60);
80
+ const diffHour = Math.floor(diffMin / 60);
81
+ const diffDay = Math.floor(diffHour / 24);
82
+
83
+ if (diffSec < 60) return "Just now";
84
+ if (diffMin < 60) return `${diffMin}m ago`;
85
+ if (diffHour < 24) return `${diffHour}h ago`;
86
+ if (diffDay === 1) return "Yesterday";
87
+ return `${diffDay}d ago`;
88
+ }
89
+
90
+ const cleanDisplayLine = (text: string) =>
91
+ sanitizeTerminalText(text).replace(/\s+/gu, " ").trim();
92
+
93
+ export function buildSessionLabel(session: SessionInfoLike): string {
94
+ const trimmedName = cleanDisplayLine(session.name ?? "");
95
+ if (trimmedName) return trimmedName;
96
+ const id = cleanDisplayLine(session.id);
97
+ return id.length > 8 ? id.slice(0, 8) : id;
98
+ }
99
+
100
+ const normalizeSnippet = (text: string, maxLength: number): string => {
101
+ const cleaned = cleanDisplayLine(text);
102
+ const fallback = cleaned.length > 0 ? cleaned : "No messages";
103
+ if (maxLength < 1) return "";
104
+ if (fallback.length <= maxLength) return fallback;
105
+ if (maxLength === 1) return "…";
106
+ return `${fallback.slice(0, maxLength - 1)}…`;
107
+ };
108
+
109
+ export function buildSessionDescription(
110
+ session: SessionInfoLike,
111
+ snippetMax = 60,
112
+ ): string {
113
+ const snippet = normalizeSnippet(session.firstMessage ?? "", snippetMax);
114
+ return `${formatTimestamp(session.modified)} • ${snippet} — ${cleanDisplayLine(session.cwd)}`;
115
+ }
116
+
117
+ export interface SessionSearchEntry {
118
+ session: SessionInfoLike;
119
+ searchText: string;
120
+ }
121
+
122
+ export const buildSearchText = (session: SessionInfoLike): string =>
123
+ [
124
+ session.name?.trim() ?? "",
125
+ session.id,
126
+ session.cwd,
127
+ session.firstMessage ?? "",
128
+ ]
129
+ .join(" ")
130
+ .toLowerCase();
131
+
132
+ export function buildSessionSearchEntries(
133
+ sessions: SessionInfoLike[],
134
+ ): SessionSearchEntry[] {
135
+ return sessions.map((session) => ({
136
+ session,
137
+ searchText: buildSearchText(session),
138
+ }));
139
+ }
140
+
141
+ export function filterSessionEntries(
142
+ entries: SessionSearchEntry[],
143
+ filter: string,
144
+ ): SessionSearchEntry[] {
145
+ const normalized = filter.trim().toLowerCase();
146
+ if (!normalized) return entries;
147
+
148
+ const tokens = normalized.split(/\s+/).filter(Boolean);
149
+ if (tokens.length === 0) return entries;
150
+
151
+ return entries.filter((entry) =>
152
+ tokens.every((token) => entry.searchText.includes(token)),
153
+ );
154
+ }
155
+
156
+ export function filterSessionInfos(
157
+ sessions: SessionInfoLike[],
158
+ filter: string,
159
+ ): SessionInfoLike[] {
160
+ return filterSessionEntries(buildSessionSearchEntries(sessions), filter).map(
161
+ (entry) => entry.session,
162
+ );
163
+ }
164
+
165
+ export function getSessionPaneLayout(width: number): SessionPaneLayout {
166
+ if (width < 80) {
167
+ return { mode: "single", listWidth: width, previewWidth: 0 };
168
+ }
169
+
170
+ const dividerWidth = 3;
171
+ const available = Math.max(0, width - dividerWidth);
172
+ let listRatio = 0.36;
173
+ if (width < 110) {
174
+ listRatio = 0.42;
175
+ } else if (width >= 180) {
176
+ listRatio = 0.32;
177
+ }
178
+
179
+ const listWidth = Math.max(
180
+ 34,
181
+ Math.min(58, Math.floor(available * listRatio)),
182
+ );
183
+ return {
184
+ mode: "split",
185
+ listWidth,
186
+ previewWidth: Math.max(0, available - listWidth),
187
+ };
188
+ }
189
+
190
+ const cleanPreviewText = (text: string) =>
191
+ sanitizeTerminalText(text).replace(/\s+$/g, "");
192
+
193
+ function sanitizePreviewValue(value: unknown): unknown {
194
+ if (typeof value === "string") return sanitizeTerminalText(value);
195
+ if (Array.isArray(value)) return value.map(sanitizePreviewValue);
196
+ if (typeof value !== "object" || value === null) return value;
197
+ return Object.fromEntries(
198
+ Object.entries(value).map(([key, nested]) => [
199
+ sanitizeTerminalText(key),
200
+ sanitizePreviewValue(nested),
201
+ ]),
202
+ );
203
+ }
204
+
205
+ function textBlocksFromContent(
206
+ content: PreviewContent | undefined,
207
+ ): PreviewBlock[] {
208
+ if (!content) return [];
209
+ if (typeof content === "string") {
210
+ const text = cleanPreviewText(content);
211
+ return text.trim() ? [{ kind: "assistant", text }] : [];
212
+ }
213
+
214
+ const blocks: PreviewBlock[] = [];
215
+ for (const part of content) {
216
+ if (part.type === "text") {
217
+ const text = cleanPreviewText(part.text);
218
+ if (text.trim()) blocks.push({ kind: "assistant", text });
219
+ } else if (part.type === "image") {
220
+ const mimeType = cleanDisplayLine(part.mimeType ?? "");
221
+ blocks.push({
222
+ kind: "notice",
223
+ text: `[image${mimeType ? `: ${mimeType}` : ""}]`,
224
+ });
225
+ } else if (part.type === "toolCall") {
226
+ const args =
227
+ part.arguments && Object.keys(part.arguments).length > 0
228
+ ? JSON.stringify(sanitizePreviewValue(part.arguments))
229
+ : undefined;
230
+ blocks.push({
231
+ kind: "toolCall",
232
+ name: cleanDisplayLine(part.name),
233
+ args,
234
+ });
235
+ } else if (part.type === "thinking") {
236
+ blocks.push({
237
+ kind: "thinking",
238
+ text: part.redacted
239
+ ? "[thinking redacted]"
240
+ : cleanPreviewText(part.thinking ?? "[thinking]"),
241
+ redacted: part.redacted,
242
+ });
243
+ }
244
+ }
245
+ return blocks;
246
+ }
247
+
248
+ function contentToPlainText(content: PreviewContent | undefined): string {
249
+ if (!content) return "";
250
+ if (typeof content === "string") return content;
251
+ return content
252
+ .map((part) => {
253
+ if (part.type === "text") return part.text;
254
+ if (part.type === "image")
255
+ return `[image${part.mimeType ? `: ${part.mimeType}` : ""}]`;
256
+ if (part.type === "toolCall") return `[tool call: ${part.name}]`;
257
+ if (part.type === "thinking")
258
+ return part.redacted
259
+ ? "[thinking redacted]"
260
+ : (part.thinking ?? "[thinking]");
261
+ return "";
262
+ })
263
+ .filter(Boolean)
264
+ .join("\n");
265
+ }
266
+
267
+ function messageToBlocks(message: PreviewMessageLike): PreviewBlock[] {
268
+ if (message.role === "user") {
269
+ const text = cleanPreviewText(contentToPlainText(message.content));
270
+ return text ? [{ kind: "user", text }] : [];
271
+ }
272
+
273
+ if (message.role === "assistant") {
274
+ return textBlocksFromContent(message.content);
275
+ }
276
+
277
+ if (message.role === "toolResult" || message.role === "tool") {
278
+ const text = cleanPreviewText(contentToPlainText(message.content));
279
+ return text
280
+ ? [
281
+ {
282
+ kind: "toolResult",
283
+ name: message.toolName
284
+ ? cleanDisplayLine(message.toolName)
285
+ : undefined,
286
+ text,
287
+ isError: message.isError,
288
+ },
289
+ ]
290
+ : [];
291
+ }
292
+
293
+ if (message.role === "bashExecution") {
294
+ const command = message.command ?? "";
295
+ return command || message.output
296
+ ? [
297
+ {
298
+ kind: "bash",
299
+ command: cleanPreviewText(command),
300
+ output: cleanPreviewText(message.output ?? ""),
301
+ isError: message.isError,
302
+ },
303
+ ]
304
+ : [];
305
+ }
306
+
307
+ if (
308
+ message.role === "compactionSummary" ||
309
+ message.role === "branchSummary"
310
+ ) {
311
+ const text = cleanPreviewText(
312
+ message.summary ?? contentToPlainText(message.content),
313
+ );
314
+ return text
315
+ ? [
316
+ {
317
+ kind: "summary",
318
+ label:
319
+ message.role === "branchSummary" ? "Branch summary" : "Summary",
320
+ text,
321
+ },
322
+ ]
323
+ : [];
324
+ }
325
+
326
+ const text = cleanPreviewText(
327
+ message.summary ?? contentToPlainText(message.content),
328
+ );
329
+ return text
330
+ ? [
331
+ {
332
+ kind: "custom",
333
+ label: cleanDisplayLine(message.role) || "Message",
334
+ text,
335
+ },
336
+ ]
337
+ : [];
338
+ }
339
+
340
+ export function buildSessionPreview(
341
+ session: SessionInfoLike,
342
+ messages: PreviewMessageLike[],
343
+ options: { maxMessages?: number } = {},
344
+ ): SessionPreview {
345
+ const maxMessages = options.maxMessages ?? 80;
346
+ const blocks: PreviewBlock[] = [];
347
+ const omitted = Math.max(0, messages.length - maxMessages);
348
+ const visibleMessages = omitted > 0 ? messages.slice(-maxMessages) : messages;
349
+
350
+ if (omitted > 0) {
351
+ blocks.push({
352
+ kind: "notice",
353
+ text: `… ${omitted} earlier messages omitted`,
354
+ });
355
+ }
356
+
357
+ for (const message of visibleMessages) {
358
+ blocks.push(...messageToBlocks(message));
359
+ }
360
+
361
+ const messageCount = session.messageCount ?? messages.length;
362
+ return {
363
+ title: buildSessionLabel(session),
364
+ subtitle: `${formatTimestamp(session.modified)} · ${messageCount} messages · ${cleanDisplayLine(session.cwd)}`,
365
+ blocks:
366
+ blocks.length > 0
367
+ ? blocks
368
+ : [{ kind: "notice", text: "No previewable messages." }],
369
+ };
370
+ }
371
+
372
+ export function buildPreviewError(
373
+ session: SessionInfoLike,
374
+ error: unknown,
375
+ ): SessionPreview {
376
+ const message = cleanPreviewText(
377
+ error instanceof Error ? error.message : String(error),
378
+ );
379
+ return {
380
+ title: buildSessionLabel(session),
381
+ subtitle: `${formatTimestamp(session.modified)} · preview unavailable`,
382
+ blocks: [{ kind: "notice", text: `Failed to load preview: ${message}` }],
383
+ error: message,
384
+ };
385
+ }