@tt-a1i/openpi 0.5.0 → 0.6.1
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 +30 -20
- package/SETUP.md +10 -4
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/bin/openpi.js +25 -15
- package/extensions/ai-providers/LICENSE.upstream +23 -0
- package/extensions/ai-providers/README.md +65 -0
- package/extensions/ai-providers/antigravity/credentials.ts +52 -0
- package/extensions/ai-providers/antigravity/discovery.ts +130 -0
- package/extensions/ai-providers/antigravity/google-conversion.ts +455 -0
- package/extensions/ai-providers/antigravity/models.ts +84 -0
- package/extensions/ai-providers/antigravity/oauth.ts +700 -0
- package/extensions/ai-providers/antigravity/provider.ts +1116 -0
- package/extensions/ai-providers/antigravity/routing.ts +340 -0
- package/extensions/ai-providers/antigravity/with-resolvers.d.ts +19 -0
- package/extensions/ai-providers/cursor/constants.ts +5 -0
- package/extensions/ai-providers/cursor/credentials.ts +14 -0
- package/extensions/ai-providers/cursor/discovery.ts +291 -0
- package/extensions/ai-providers/cursor/input-images.ts +105 -0
- package/extensions/ai-providers/cursor/models.ts +45 -0
- package/extensions/ai-providers/cursor/oauth.ts +263 -0
- package/extensions/ai-providers/cursor/proto.ts +1271 -0
- package/extensions/ai-providers/cursor/protobuf.ts +1181 -0
- package/extensions/ai-providers/cursor/provider.ts +1431 -0
- package/extensions/ai-providers/cursor/proxy.ts +213 -0
- package/extensions/ai-providers/cursor/tool-bridge.ts +68 -0
- package/extensions/ai-providers/cursor/with-resolvers.d.ts +12 -0
- package/extensions/ai-providers/index.ts +86 -0
- package/extensions/ai-providers/oauth-adapter.ts +81 -0
- package/extensions/ai-providers/usage.ts +10 -0
- package/extensions/background-terminals/index.ts +8 -1
- package/extensions/background-terminals/src/manager.ts +3 -5
- package/extensions/background-terminals/src/result-delivery.ts +43 -23
- package/extensions/cron/index.ts +68 -27
- package/extensions/cron/schedule.ts +5 -1
- package/extensions/model-info/cache-diagnostics.ts +220 -0
- package/extensions/model-info/index.ts +45 -1
- package/extensions/plan-mode/index.ts +75 -4
- package/extensions/setup/index.ts +15 -3
- package/extensions/shared/child-session.ts +39 -5
- package/extensions/shared/completion-inbox.ts +193 -0
- package/extensions/shared/setup-config.ts +10 -1
- package/extensions/shared/structured-output.ts +154 -0
- package/extensions/subagents/index.ts +64 -7
- package/extensions/subagents/src/agent-types.ts +5 -17
- package/extensions/subagents/src/backends/pi.ts +130 -48
- package/extensions/subagents/src/backends/tool-preview.ts +29 -0
- package/extensions/subagents/src/domain.ts +16 -1
- package/extensions/subagents/src/manager.ts +7 -71
- package/extensions/subagents/src/prompt.ts +19 -5
- package/extensions/subagents/src/result-artifact.ts +32 -0
- package/extensions/subagents/src/result-delivery.ts +33 -14
- package/extensions/subagents/src/runtime.ts +10 -3
- package/extensions/ui-customization/footer.ts +16 -5
- package/extensions/user-input-fold/index.ts +42 -6
- package/extensions/web/index.ts +25 -2
- package/extensions/workflows/acceptance.ts +43 -19
- package/extensions/workflows/completion-projection.ts +3 -1
- package/extensions/workflows/dashboard.ts +147 -21
- package/extensions/workflows/index.ts +75 -20
- package/extensions/workflows/model.ts +5 -1
- package/extensions/workflows/progress-projection.ts +7 -1
- package/extensions/workflows/prompt.ts +4 -10
- package/extensions/workflows/result-delivery.ts +96 -22
- package/extensions/workflows/retention.ts +6 -0
- package/extensions/workflows/runner.ts +11 -233
- package/extensions/workflows/sandbox.ts +4 -0
- package/package.json +7 -7
- package/skills/subagents/REFERENCE.md +9 -9
- package/skills/subagents/SKILL.md +2 -1
- package/skills/workflows/REFERENCE.md +5 -3
- package/skills/workflows/SKILL.md +1 -1
- package/web/adapter/pi-adapter.ts +3 -0
- package/web/host/pi-coding-agent-entry.ts +162 -0
- package/web/host/web-host.ts +330 -50
- package/web/protocol/types.ts +5 -0
- package/web/runtime/pi-runtime.ts +240 -25
- package/web/runtime/types.ts +32 -1
- package/web/ui/app.js +343 -41
- package/web/ui/index.html +3 -0
- package/web/ui/styles.css +119 -37
|
@@ -16,14 +16,12 @@ import {
|
|
|
16
16
|
type AgentSessionEventListener,
|
|
17
17
|
createAgentSession,
|
|
18
18
|
DefaultResourceLoader,
|
|
19
|
-
defineTool,
|
|
20
19
|
type ExtensionAPI,
|
|
21
20
|
type ExtensionContext,
|
|
22
21
|
SessionManager,
|
|
23
22
|
SettingsManager,
|
|
24
23
|
type ToolDefinition,
|
|
25
24
|
} from "@earendil-works/pi-coding-agent";
|
|
26
|
-
import { type TSchema, Type } from "typebox";
|
|
27
25
|
import { AgentToolRenderLedger } from "../shared/agent-tool-renderer.ts";
|
|
28
26
|
import {
|
|
29
27
|
bindChildSessionExtensions,
|
|
@@ -34,10 +32,11 @@ import {
|
|
|
34
32
|
import { createToolCallTimeoutGuard } from "../shared/tool-call-timeout.ts";
|
|
35
33
|
import { type AgentUsage, emptyUsage, type TranscriptEntry } from "./model.ts";
|
|
36
34
|
import {
|
|
37
|
-
|
|
35
|
+
childToolsWithStructuredOutput,
|
|
36
|
+
createStructuredOutputTool,
|
|
38
37
|
STRUCTURED_OUTPUT_SYSTEM_INSTRUCTION,
|
|
39
|
-
|
|
40
|
-
} from "./prompt.ts";
|
|
38
|
+
} from "../shared/structured-output.ts";
|
|
39
|
+
import { buildWorkflowAgentPrompt } from "./prompt.ts";
|
|
41
40
|
import {
|
|
42
41
|
AgentProgressProjection,
|
|
43
42
|
type ProgressAssistantMessage,
|
|
@@ -51,7 +50,6 @@ import { truncateUtf8 } from "./serialization.ts";
|
|
|
51
50
|
import { bindWorkflowToolRenderer } from "./tool-renderer.ts";
|
|
52
51
|
|
|
53
52
|
const AGENT_OUTPUT_MAX_BYTES = 64 * 1024;
|
|
54
|
-
export const MODEL_PROGRESS_TIMEOUT_MS = 45_000;
|
|
55
53
|
|
|
56
54
|
export type WorkflowModel = NonNullable<ExtensionContext["model"]>;
|
|
57
55
|
export type ThinkingLevel = ReturnType<ExtensionAPI["getThinkingLevel"]>;
|
|
@@ -112,8 +110,6 @@ export interface RunAgentOptions {
|
|
|
112
110
|
replayFilesystemBoundary?: ReplayFilesystemBoundaryOptions;
|
|
113
111
|
/** Test-only override for the per-tool execution timeout. */
|
|
114
112
|
toolCallTimeoutMs?: number;
|
|
115
|
-
/** Test-only override for the per-provider-turn model-progress timeout. */
|
|
116
|
-
modelProgressTimeoutMs?: number;
|
|
117
113
|
/** Test-only override for the end-to-end abort/shutdown deadline. */
|
|
118
114
|
shutdownTimeoutMs?: number;
|
|
119
115
|
/** Test seam for lifecycle races; production always uses createAgentSession. */
|
|
@@ -142,9 +138,7 @@ export function workflowChildTools(
|
|
|
142
138
|
tools: readonly string[] | undefined,
|
|
143
139
|
structured: boolean,
|
|
144
140
|
) {
|
|
145
|
-
return tools
|
|
146
|
-
? [...new Set([...tools, ...(structured ? ["structured_output"] : [])])]
|
|
147
|
-
: undefined;
|
|
141
|
+
return childToolsWithStructuredOutput(tools, structured);
|
|
148
142
|
}
|
|
149
143
|
|
|
150
144
|
interface WorkflowToolSession {
|
|
@@ -179,68 +173,6 @@ export function guardWorkflowChildTools(
|
|
|
179
173
|
});
|
|
180
174
|
}
|
|
181
175
|
|
|
182
|
-
function isJsonSchema(value: unknown): value is TSchema {
|
|
183
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
184
|
-
const seen = new WeakSet<object>();
|
|
185
|
-
let nodes = 0;
|
|
186
|
-
const validate = (current: unknown, depth: number): boolean => {
|
|
187
|
-
if (++nodes > 10_000 || depth > 24) return false;
|
|
188
|
-
if (
|
|
189
|
-
current === null ||
|
|
190
|
-
typeof current === "string" ||
|
|
191
|
-
typeof current === "boolean"
|
|
192
|
-
) {
|
|
193
|
-
return true;
|
|
194
|
-
}
|
|
195
|
-
if (typeof current === "number") return Number.isFinite(current);
|
|
196
|
-
if (Array.isArray(current)) {
|
|
197
|
-
return current.every((item) => validate(item, depth + 1));
|
|
198
|
-
}
|
|
199
|
-
if (typeof current !== "object") return false;
|
|
200
|
-
if (seen.has(current)) return false;
|
|
201
|
-
seen.add(current);
|
|
202
|
-
return Object.keys(current).every((key) => {
|
|
203
|
-
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
204
|
-
return false;
|
|
205
|
-
}
|
|
206
|
-
return validate((current as Record<string, unknown>)[key], depth + 1);
|
|
207
|
-
});
|
|
208
|
-
};
|
|
209
|
-
return validate(value, 0);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/** Preserve the caller's full JSON Schema instead of lossy keyword conversion. */
|
|
213
|
-
function jsonSchemaToTypebox(schema: unknown): TSchema {
|
|
214
|
-
if (!isJsonSchema(schema)) {
|
|
215
|
-
throw new Error("structured output schema must be a bounded JSON object");
|
|
216
|
-
}
|
|
217
|
-
return Type.Unsafe(schema);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* One-shot terminating tool injected when a schema is supplied: the subagent
|
|
222
|
-
* calls it as its final action and we capture the validated object.
|
|
223
|
-
*/
|
|
224
|
-
function makeStructuredOutputTool(
|
|
225
|
-
schema: unknown,
|
|
226
|
-
capture: (value: unknown) => void,
|
|
227
|
-
): ToolDefinition {
|
|
228
|
-
return defineTool({
|
|
229
|
-
name: "structured_output",
|
|
230
|
-
label: "Structured Output",
|
|
231
|
-
description: STRUCTURED_OUTPUT_TOOL_DESCRIPTION,
|
|
232
|
-
parameters: jsonSchemaToTypebox(schema),
|
|
233
|
-
async execute(_toolCallId, params) {
|
|
234
|
-
capture(params);
|
|
235
|
-
return {
|
|
236
|
-
content: [{ type: "text", text: "Recorded structured result." }],
|
|
237
|
-
details: params,
|
|
238
|
-
terminate: true,
|
|
239
|
-
};
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
176
|
type AssistantMessage = ProgressAssistantMessage;
|
|
245
177
|
|
|
246
178
|
export { transcriptFromMessages };
|
|
@@ -310,119 +242,6 @@ function errorText(error: unknown): string {
|
|
|
310
242
|
);
|
|
311
243
|
}
|
|
312
244
|
|
|
313
|
-
function formatTimeout(timeoutMs: number) {
|
|
314
|
-
return timeoutMs % 1_000 === 0
|
|
315
|
-
? `${timeoutMs / 1_000} seconds`
|
|
316
|
-
: `${timeoutMs} ms`;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export function resolveModelProgressTimeoutMs(
|
|
320
|
-
settingsManager: SettingsManager,
|
|
321
|
-
override?: number,
|
|
322
|
-
) {
|
|
323
|
-
if (override !== undefined) return override;
|
|
324
|
-
const configured =
|
|
325
|
-
settingsManager.getProjectSettings().httpIdleTimeoutMs ??
|
|
326
|
-
settingsManager.getGlobalSettings().httpIdleTimeoutMs;
|
|
327
|
-
return typeof configured === "number" && Number.isFinite(configured)
|
|
328
|
-
? Math.max(MODEL_PROGRESS_TIMEOUT_MS, Math.floor(configured))
|
|
329
|
-
: MODEL_PROGRESS_TIMEOUT_MS;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/** Abort any provider turn that stops producing model-visible progress. */
|
|
333
|
-
export function createModelProgressWatchdog(
|
|
334
|
-
onTimeout: (error: Error) => Promise<unknown>,
|
|
335
|
-
options: { timeoutMs?: number; model?: string } = {},
|
|
336
|
-
) {
|
|
337
|
-
const timeoutMs = options.timeoutMs ?? MODEL_PROGRESS_TIMEOUT_MS;
|
|
338
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
339
|
-
let activeTurn = false;
|
|
340
|
-
let closed = false;
|
|
341
|
-
let rejectTimeout!: (error: Error) => void;
|
|
342
|
-
const timeout = new Promise<never>((_resolve, reject) => {
|
|
343
|
-
rejectTimeout = reject;
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
const clear = () => {
|
|
347
|
-
if (timer) clearTimeout(timer);
|
|
348
|
-
timer = undefined;
|
|
349
|
-
};
|
|
350
|
-
const schedule = () => {
|
|
351
|
-
clear();
|
|
352
|
-
if (!activeTurn || closed) return;
|
|
353
|
-
// This timer owns the awaited watchdog outcome. Keep it referenced so a
|
|
354
|
-
// short-lived Node 22 process cannot exit with the promise still pending.
|
|
355
|
-
timer = setTimeout(() => {
|
|
356
|
-
timer = undefined;
|
|
357
|
-
activeTurn = false;
|
|
358
|
-
closed = true;
|
|
359
|
-
const model = options.model ? ` for ${options.model}` : "";
|
|
360
|
-
const error = new Error(
|
|
361
|
-
`Agent provider turn${model} produced no model-visible progress for ${formatTimeout(timeoutMs)}; the provider request may be stalled. Retry the workflow.`,
|
|
362
|
-
);
|
|
363
|
-
rejectTimeout(error);
|
|
364
|
-
try {
|
|
365
|
-
void onTimeout(error).catch(() => {});
|
|
366
|
-
} catch {
|
|
367
|
-
// The timeout result remains authoritative even if abort throws before
|
|
368
|
-
// returning its promise; bounded shutdown below gets another chance.
|
|
369
|
-
}
|
|
370
|
-
}, timeoutMs);
|
|
371
|
-
};
|
|
372
|
-
const armTurn = () => {
|
|
373
|
-
if (closed) return;
|
|
374
|
-
activeTurn = true;
|
|
375
|
-
schedule();
|
|
376
|
-
};
|
|
377
|
-
const markProgress = () => {
|
|
378
|
-
if (!activeTurn || closed) return;
|
|
379
|
-
schedule();
|
|
380
|
-
};
|
|
381
|
-
const completeTurn = () => {
|
|
382
|
-
activeTurn = false;
|
|
383
|
-
clear();
|
|
384
|
-
};
|
|
385
|
-
const cancel = () => {
|
|
386
|
-
closed = true;
|
|
387
|
-
activeTurn = false;
|
|
388
|
-
clear();
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
armTurn,
|
|
393
|
-
markProgress,
|
|
394
|
-
completeTurn,
|
|
395
|
-
cancel,
|
|
396
|
-
async waitFor<T>(operation: Promise<T>) {
|
|
397
|
-
try {
|
|
398
|
-
return await Promise.race([operation, timeout]);
|
|
399
|
-
} finally {
|
|
400
|
-
cancel();
|
|
401
|
-
}
|
|
402
|
-
},
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
function isModelVisibleProgress(event: AgentSessionEvent) {
|
|
407
|
-
if (event.type !== "message_update" || event.message.role !== "assistant") {
|
|
408
|
-
return false;
|
|
409
|
-
}
|
|
410
|
-
// Raw transport heartbeats never become AgentSession events. Empty stream,
|
|
411
|
-
// text, and thinking starts likewise cannot keep a provider turn alive.
|
|
412
|
-
const update = event.assistantMessageEvent;
|
|
413
|
-
if (
|
|
414
|
-
update.type === "text_delta" ||
|
|
415
|
-
update.type === "thinking_delta" ||
|
|
416
|
-
update.type === "toolcall_delta"
|
|
417
|
-
) {
|
|
418
|
-
return update.delta.length > 0;
|
|
419
|
-
}
|
|
420
|
-
if (update.type === "text_end" || update.type === "thinking_end") {
|
|
421
|
-
return update.content.length > 0;
|
|
422
|
-
}
|
|
423
|
-
return update.type === "toolcall_start" || update.type === "toolcall_end";
|
|
424
|
-
}
|
|
425
|
-
|
|
426
245
|
export async function runAgent(
|
|
427
246
|
options: RunAgentOptions,
|
|
428
247
|
): Promise<AgentOutcome> {
|
|
@@ -432,8 +251,6 @@ export async function runAgent(
|
|
|
432
251
|
let session: AgentSession | undefined;
|
|
433
252
|
let unsubscribeToolGuards: (() => void) | undefined;
|
|
434
253
|
let aborted = false;
|
|
435
|
-
let terminalCause: "abort" | "model-progress-timeout" | undefined;
|
|
436
|
-
let modelProgressTimeoutMessage: string | undefined;
|
|
437
254
|
let abortOperation: Promise<unknown> | undefined;
|
|
438
255
|
let rejectForAbort: ((error: Error) => void) | undefined;
|
|
439
256
|
let rejectForProjectionFailure: ((error: Error) => void) | undefined;
|
|
@@ -454,7 +271,6 @@ export async function runAgent(
|
|
|
454
271
|
const onAbort = () => {
|
|
455
272
|
if (aborted) return;
|
|
456
273
|
aborted = true;
|
|
457
|
-
terminalCause ??= "abort";
|
|
458
274
|
if (session) {
|
|
459
275
|
try {
|
|
460
276
|
abortOperation ??= session.abort();
|
|
@@ -475,7 +291,7 @@ export async function runAgent(
|
|
|
475
291
|
customTools =
|
|
476
292
|
options.schema !== undefined
|
|
477
293
|
? [
|
|
478
|
-
|
|
294
|
+
createStructuredOutputTool(options.schema, (value) => {
|
|
479
295
|
if (!settled) structured = value;
|
|
480
296
|
}),
|
|
481
297
|
]
|
|
@@ -659,10 +475,6 @@ export async function runAgent(
|
|
|
659
475
|
});
|
|
660
476
|
};
|
|
661
477
|
|
|
662
|
-
let armModelProgress = () => {};
|
|
663
|
-
let markModelProgress = () => {};
|
|
664
|
-
let completeModelTurn = () => {};
|
|
665
|
-
let cancelModelProgressWatchdog = () => {};
|
|
666
478
|
let compactionReconcileQueued = false;
|
|
667
479
|
const queueCompactionReconcile = () => {
|
|
668
480
|
if (compactionReconcileQueued) return;
|
|
@@ -690,7 +502,6 @@ export async function runAgent(
|
|
|
690
502
|
};
|
|
691
503
|
const unsubscribe = childSession.subscribe((event) => {
|
|
692
504
|
if (settled) return;
|
|
693
|
-
if (event.type === "turn_start") armModelProgress();
|
|
694
505
|
if (event.type === "tool_execution_start") {
|
|
695
506
|
toolRenderer.start(
|
|
696
507
|
event.toolCallId,
|
|
@@ -713,10 +524,6 @@ export async function runAgent(
|
|
|
713
524
|
event.isError,
|
|
714
525
|
);
|
|
715
526
|
}
|
|
716
|
-
if (isModelVisibleProgress(event)) markModelProgress();
|
|
717
|
-
if (event.type === "message_end" && event.message.role === "assistant") {
|
|
718
|
-
completeModelTurn();
|
|
719
|
-
}
|
|
720
527
|
if (event.type === "message_end") {
|
|
721
528
|
assistantSettlement = observeAssistantSettlement(
|
|
722
529
|
assistantSettlement,
|
|
@@ -751,32 +558,10 @@ export async function runAgent(
|
|
|
751
558
|
captureToolRenderData(childSession.messages);
|
|
752
559
|
snapshotProjection();
|
|
753
560
|
if (!aborted) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
terminalCause ??= "model-progress-timeout";
|
|
757
|
-
if (terminalCause === "model-progress-timeout") {
|
|
758
|
-
modelProgressTimeoutMessage ??= error.message;
|
|
759
|
-
}
|
|
760
|
-
abortOperation ??= childSession.abort();
|
|
761
|
-
void abortOperation.catch(() => {});
|
|
762
|
-
return abortOperation;
|
|
763
|
-
},
|
|
764
|
-
{
|
|
765
|
-
timeoutMs: resolveModelProgressTimeoutMs(
|
|
766
|
-
options.settingsManager,
|
|
767
|
-
options.modelProgressTimeoutMs,
|
|
768
|
-
),
|
|
769
|
-
model: modelId,
|
|
770
|
-
},
|
|
771
|
-
);
|
|
772
|
-
armModelProgress = watchdog.armTurn;
|
|
773
|
-
markModelProgress = watchdog.markProgress;
|
|
774
|
-
completeModelTurn = watchdog.completeTurn;
|
|
775
|
-
cancelModelProgressWatchdog = watchdog.cancel;
|
|
561
|
+
// Pi owns transport liveness and retries. Quiet model output is not
|
|
562
|
+
// evidence of a stalled request (thinking and retry backoff can be silent).
|
|
776
563
|
await Promise.race([
|
|
777
|
-
|
|
778
|
-
childSession.prompt(buildWorkflowAgentPrompt(options.prompt)),
|
|
779
|
-
),
|
|
564
|
+
childSession.prompt(buildWorkflowAgentPrompt(options.prompt)),
|
|
780
565
|
abortRace,
|
|
781
566
|
projectionFailureRace,
|
|
782
567
|
]);
|
|
@@ -784,7 +569,6 @@ export async function runAgent(
|
|
|
784
569
|
} catch (error) {
|
|
785
570
|
promptErrorMessage ??= errorText(error);
|
|
786
571
|
} finally {
|
|
787
|
-
cancelModelProgressWatchdog();
|
|
788
572
|
options.signal?.removeEventListener("abort", onAbort);
|
|
789
573
|
settled = true;
|
|
790
574
|
unsubscribe();
|
|
@@ -823,11 +607,7 @@ export async function runAgent(
|
|
|
823
607
|
? `Cleanup failed: ${cleanupErrors.join("; ")}`
|
|
824
608
|
: undefined;
|
|
825
609
|
|
|
826
|
-
if (
|
|
827
|
-
terminalCause === "abort" ||
|
|
828
|
-
(terminalCause === undefined &&
|
|
829
|
-
assistantSettlement?.stopReason === "aborted")
|
|
830
|
-
) {
|
|
610
|
+
if (aborted || assistantSettlement?.stopReason === "aborted") {
|
|
831
611
|
return {
|
|
832
612
|
ok: false,
|
|
833
613
|
output,
|
|
@@ -844,9 +624,7 @@ export async function runAgent(
|
|
|
844
624
|
}
|
|
845
625
|
|
|
846
626
|
const failureMessage =
|
|
847
|
-
(
|
|
848
|
-
? modelProgressTimeoutMessage
|
|
849
|
-
: agentFailureMessage(assistantSettlement, promptErrorMessage)) ??
|
|
627
|
+
agentFailureMessage(assistantSettlement, promptErrorMessage) ??
|
|
850
628
|
cleanupError;
|
|
851
629
|
if (failureMessage !== undefined) {
|
|
852
630
|
return {
|
|
@@ -35,6 +35,7 @@ export interface SandboxAgentOptions {
|
|
|
35
35
|
provider?: unknown;
|
|
36
36
|
effort?: unknown;
|
|
37
37
|
isolation?: unknown;
|
|
38
|
+
working_dir?: unknown;
|
|
38
39
|
operator?: unknown;
|
|
39
40
|
inputs?: unknown;
|
|
40
41
|
}
|
|
@@ -110,6 +111,9 @@ function sanitizeAgentOptions(value: unknown): SandboxAgentOptions {
|
|
|
110
111
|
...(value.provider !== undefined ? { provider: value.provider } : {}),
|
|
111
112
|
...(value.effort !== undefined ? { effort: value.effort } : {}),
|
|
112
113
|
...(value.isolation !== undefined ? { isolation: value.isolation } : {}),
|
|
114
|
+
...(value.working_dir !== undefined
|
|
115
|
+
? { working_dir: value.working_dir }
|
|
116
|
+
: {}),
|
|
113
117
|
...(value.operator !== undefined ? { operator: value.operator } : {}),
|
|
114
118
|
...(value.inputs !== undefined ? { inputs: value.inputs } : {}),
|
|
115
119
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tt-a1i/openpi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "OpenPI — a Pi-native multi-agent workbench with background execution, isolated subagents, replay-safe workflows, goals, tasks, and observable TUI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "tt-a1i",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@biomejs/biome": "2.5.8",
|
|
66
|
-
"@earendil-works/pi-ai": "^0.
|
|
67
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
68
|
-
"@earendil-works/pi-tui": "^0.
|
|
66
|
+
"@earendil-works/pi-ai": "^0.85.1",
|
|
67
|
+
"@earendil-works/pi-coding-agent": "^0.85.1",
|
|
68
|
+
"@earendil-works/pi-tui": "^0.85.1",
|
|
69
69
|
"@effect/tsgo": "^0.24.2",
|
|
70
70
|
"@effect/vitest": "^4.0.0-beta.99",
|
|
71
71
|
"@types/node": "^26.1.1",
|
|
@@ -75,9 +75,9 @@
|
|
|
75
75
|
"vitest": "4.1.10"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@earendil-works/pi-ai": "
|
|
79
|
-
"@earendil-works/pi-coding-agent": "
|
|
80
|
-
"@earendil-works/pi-tui": "
|
|
78
|
+
"@earendil-works/pi-ai": ">=0.85.1",
|
|
79
|
+
"@earendil-works/pi-coding-agent": ">=0.85.1",
|
|
80
|
+
"@earendil-works/pi-tui": ">=0.85.1",
|
|
81
81
|
"typebox": "*"
|
|
82
82
|
},
|
|
83
83
|
"type": "module",
|
|
@@ -32,7 +32,7 @@ file:line references. You cannot modify files — do not attempt to.
|
|
|
32
32
|
| ------------------ | -------- | ------------------------------------------------------------------------------------ |
|
|
33
33
|
| `name` | yes | `[a-z0-9-]`, ≤64 chars, and must equal the filename stem. |
|
|
34
34
|
| `description` | yes | Shown to the parent model when it picks a type. ≤1024 chars. |
|
|
35
|
-
| `tools` | no | Tool allowlist. **Omit to inherit
|
|
35
|
+
| `tools` | no | Tool allowlist. **Omit to inherit active parent child-eligible tools.** |
|
|
36
36
|
| `model` | no | `provider/model-id`, or a bare id resolved against the current provider; ≤256 chars. |
|
|
37
37
|
| `reasoning_effort` | no | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. |
|
|
38
38
|
| body | no | Appended to the child's system prompt. ≤16384 chars. |
|
|
@@ -50,16 +50,15 @@ file with the same name.
|
|
|
50
50
|
|
|
51
51
|
| Role | Tools | Relative effort guidance | Purpose |
|
|
52
52
|
| ------------- | ------------------------------------------------------------------- | ------------------------ | ------------------------------------------------------ |
|
|
53
|
-
| `explorer` |
|
|
54
|
-
| `implementer` |
|
|
55
|
-
| `reviewer` |
|
|
56
|
-
| `advisor` |
|
|
53
|
+
| `explorer` | Active parent child-eligible tools | Moderate | Read-only codebase tracing; increase for harder tasks. |
|
|
54
|
+
| `implementer` | Active parent child-eligible tools | Medium-high | Focused implementation; adjust for scope and risk. |
|
|
55
|
+
| `reviewer` | Active parent child-eligible tools | High | Read-only correctness, safety, and regression review. |
|
|
56
|
+
| `advisor` | Active parent child-eligible tools | High | Deep read-only analysis and technical advice. |
|
|
57
57
|
|
|
58
58
|
These are relative selection hints, not fixed Pi thinking levels. Built-ins set
|
|
59
59
|
no model or reasoning-effort default. An explicit user requirement takes
|
|
60
60
|
priority; otherwise the parent model chooses from levels supported by the
|
|
61
|
-
resolved child model according to the role and task difficulty.
|
|
62
|
-
allowlists still intersect with plan mode and the child denylist.
|
|
61
|
+
resolved child model according to the role and task difficulty. Custom role allowlists intersect with active parent tools, Plan Mode, and the child denylist. Built-in read-only task guidance is a role instruction, not a filesystem sandbox. Existing custom role files are never widened automatically.
|
|
63
62
|
|
|
64
63
|
## Discovery
|
|
65
64
|
|
|
@@ -153,8 +152,9 @@ So `tools: [read, grep, find, ls]` yields a child that genuinely has no
|
|
|
153
152
|
`write`, `edit`, or `bash` tool to call — not one that has been asked not to.
|
|
154
153
|
Parent-only names are removed before the generated roster and spawn result are
|
|
155
154
|
shown, so a type that lists `subagent_spawn` never advertises it as usable.
|
|
156
|
-
A
|
|
157
|
-
`
|
|
155
|
+
A Workflow child with a schema, or a Direct Subagent spawned with
|
|
156
|
+
`output_schema`, additionally receives only its terminating `structured_output`
|
|
157
|
+
tool; this does not restore any denied repository tool.
|
|
158
158
|
|
|
159
159
|
While `/plan` is armed, `isolation: "worktree"` is rejected before Git is
|
|
160
160
|
changed. A selected type whose declared tools plan mode would narrow (such as
|
|
@@ -10,9 +10,10 @@ The tool definitions are canonical for parameters, limits, model syntax, isolati
|
|
|
10
10
|
- Delegate substantial independent work, not a lookup or edit the parent can do directly.
|
|
11
11
|
- Give the child a standalone prompt with paths, constraints, relevant context, and the expected report; it cannot see the parent conversation or ask the user.
|
|
12
12
|
- Inherit the parent model by default. When choosing the child's reasoning effort, honor an explicit user requirement first; otherwise use the selected role's relative guidance and the task's difficulty, choosing from levels supported by the resolved child model.
|
|
13
|
-
- Prefer a matching agent type when one exists;
|
|
13
|
+
- Prefer a matching agent type when one exists; built-ins inherit active parent tools, while an explicit custom tool list is enforced as a narrowing restriction. Model precedence is explicit spawn override, selected type-file model, configured built-in role model, then parent model. Reasoning precedence is explicit spawn override, selected type default, then parent effort. Types live in `~/.pi/agent/agents/*.md` and, for trusted projects, `.pi/agents/*.md`; see [Agent types](REFERENCE.md).
|
|
14
14
|
- Isolate concurrent writers in worktrees according to the `subagent_spawn` schema so they cannot overwrite one checkout or git index. While Plan Mode is active, use only read-only exploration types (or no type); worktree isolation and types narrowed by Plan Mode are rejected.
|
|
15
15
|
- After spawning, continue useful parent work. In an interactive session, if none remains, tell the user the child is still running and end the turn; automatic result delivery will re-invoke the parent when it settles. Do not block merely because the next step depends on the result or because there is nothing else to do. Use `subagent_wait` only when the user explicitly asks to keep the current response open for the result, or when non-interactive automation must return it in the same invocation.
|
|
16
|
+
- Use optional `output_schema` when downstream work needs a machine-validated result rather than prose. The child then receives one terminating `structured_output` tool, and the run fails if it finishes without submitting a matching value. Keep schemas small and task-specific; the validated JSON is delivered to the parent and preserved in a private content-addressed artifact. Omit the option for ordinary text reports.
|
|
16
17
|
|
|
17
18
|
## Worktree isolation
|
|
18
19
|
|
|
@@ -12,13 +12,15 @@ The `workflow` script is an async JavaScript function body executed in a restric
|
|
|
12
12
|
|
|
13
13
|
## Agent calls
|
|
14
14
|
|
|
15
|
-
`await agent(prompt, options)` runs one child and always resolves to `{ ok, output, structured?, ref?, acceptance?, error? }`. Check `ok` before reading output. Children receive normal trust-aware resources but cannot recursively orchestrate or ask the user.
|
|
15
|
+
`await agent(prompt, options)` runs one child and always resolves to `{ ok, output, structured?, ref?, acceptance?, acceptanceWarning?, error? }`. Check `ok` before reading output. Children receive normal trust-aware resources but cannot recursively orchestrate or ask the user.
|
|
16
16
|
|
|
17
|
-
Useful options include `agent_type`, `label`, `phase`, `schema`, `
|
|
17
|
+
Useful options include `agent_type`, `label`, `phase`, `schema`, `model`, `provider`, `effort`, `working_dir`, `isolation`, `operator`, and `inputs`. The legacy `acceptance` option remains readable only during the 0.x migration window described below.
|
|
18
18
|
|
|
19
|
+
- Set `working_dir` when tools must run in another repository. Relative paths resolve against the parent cwd; prompt text alone does not change it. The directory must exist. Project resource trust is checked for the target independently.
|
|
20
|
+
- Built-in roles inherit currently active parent child-eligible tools, including shell/network when available. Explicit custom tool lists only narrow this surface. Built-ins with inherited tools execute for real on resume; custom bounded read-only calls retain the replay filesystem boundary below.
|
|
19
21
|
- Prefer a matching `agent_type`. Model precedence is explicit model/provider, type file, configured built-in role, then parent. Effort precedence is explicit effort, type default, then parent.
|
|
20
22
|
- `schema` validates structured output. Use it whenever later workflow logic branches on fields.
|
|
21
|
-
- `acceptance
|
|
23
|
+
- `acceptance` is deprecated since OpenPI 0.5 and scheduled for removal in 1.0. Compatibility calls still return the child-authored ledger with `authority: "model-self-attestation"` and a migration warning, but it never determines `ok`. Use ordinary `schema` for findings, then let the parent evaluate them alongside runtime-observed exit codes, test receipts, file fingerprints, and tool results. Old DSL, journals, and artifacts remain readable during 0.x.
|
|
22
24
|
- `operator: "name"` reuses one in-memory child Session for serialized follow-ups inside the same run. Its model, role/tools, effort, structured mode, and cwd are frozen by the first activation. Operators cannot use per-call worktrees or replay, and do not survive restarts.
|
|
23
25
|
- `inputs: [ref, ...]` accepts successful opaque refs from the same workflow run only. Each conclusion is bounded to 16 KiB and total injected input to 48 KiB. The total budget is fairly distributed, so a large fan-out cannot starve later results merely because of order; partial projections are labeled. Full successful child results remain in the run's `agent-results/` artifacts. Inputs are marked as untrusted data; the resulting graph is observability, not scheduling authority.
|
|
24
26
|
- Fair projection preserves the head and tail of every partial result and names its run-relative `agent-results/agent-N.json` audit artifact. That path is provenance for the parent/operator, not a child-readable handle. Fair presence is not proof of full evidence coverage: for large fan-out, group source refs into local Report agents, then pass only their refs to a global Report. The workflow script—not Runtime—must state planned, selected, covered, failed, and deferred counts.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: workflows
|
|
3
|
-
description: Orchestrates multi-agent work with OpenPI's inline JavaScript Workflow DSL. Use when a task needs multi-phase fan-out, pipelines, barriers, structured handoffs,
|
|
3
|
+
description: Orchestrates multi-agent work with OpenPI's inline JavaScript Workflow DSL. Use when a task needs multi-phase fan-out, pipelines, barriers, structured handoffs, or resumable background orchestration.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Workflows
|
|
@@ -490,6 +490,9 @@ export class PiWebAdapter {
|
|
|
490
490
|
status: this.runtime.isIdle()
|
|
491
491
|
? ("idle" as const)
|
|
492
492
|
: ("running" as const),
|
|
493
|
+
...(this.runtime.getActiveTurn()
|
|
494
|
+
? { activeTurn: this.runtime.getActiveTurn() }
|
|
495
|
+
: {}),
|
|
493
496
|
capabilities: webCapabilitySnapshot(this.runtime.sessionManager),
|
|
494
497
|
},
|
|
495
498
|
truncation: {
|