pi-subagents 0.55.0 → 0.56.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 (32) hide show
  1. package/CHANGELOG.md +28 -3
  2. package/docs/models.md +6 -0
  3. package/package.json +1 -1
  4. package/src/agents/agent-serializer.ts +2 -0
  5. package/src/agents/agents.ts +23 -1
  6. package/src/api/preflight.ts +20 -1
  7. package/src/extension/schemas.ts +5 -0
  8. package/src/runs/background/async-execution.ts +48 -18
  9. package/src/runs/background/async-resume.ts +3 -0
  10. package/src/runs/background/subagent-runner.ts +83 -11
  11. package/src/runs/foreground/execution.ts +35 -6
  12. package/src/runs/foreground/foreground-history.ts +3 -2
  13. package/src/runs/foreground/subagent-executor.ts +59 -8
  14. package/src/runs/foreground/workflow-detach-reconcile.ts +15 -6
  15. package/src/runs/shared/acceptance.ts +10 -5
  16. package/src/runs/shared/agent-contract.ts +1 -1
  17. package/src/runs/shared/completion-guard.ts +3 -2
  18. package/src/runs/shared/dynamic-fanout.ts +1 -1
  19. package/src/runs/shared/extension-bindings.ts +78 -0
  20. package/src/runs/shared/external-cli-runner.ts +2 -0
  21. package/src/runs/shared/fast-mode-extension.ts +10 -0
  22. package/src/runs/shared/model-exclusions.ts +2 -1
  23. package/src/runs/shared/model-fallback.ts +2 -7
  24. package/src/runs/shared/mutation-evidence.ts +145 -0
  25. package/src/runs/shared/parallel-utils.ts +3 -0
  26. package/src/runs/shared/pi-args.ts +47 -0
  27. package/src/runs/shared/structured-output.ts +18 -4
  28. package/src/runs/shared/subagent-prompt-runtime.ts +9 -5
  29. package/src/shared/launch-contract.ts +6 -0
  30. package/src/shared/settings.ts +6 -1
  31. package/src/shared/types.ts +48 -0
  32. package/src/workflows/scripted-workflow.ts +50 -3
@@ -150,7 +150,15 @@ function trackObservationTracker(tracker, promise, allowFutureObservations = fal
150
150
  get(promiseTarget, prop) {
151
151
  if (prop === "then") return function promiseThen(onFulfilled, onRejected) {
152
152
  consumeTrackedObservations(tracker);
153
- return trackObservationTracker({ observations: tracker.observations, consumed: false, dependencies: [tracker] }, promiseTarget.then(onFulfilled, onRejected), true);
153
+ const chainTracker = { observations: tracker.observations, consumed: false, dependencies: [tracker] };
154
+ const wrapHandler = (handler) => typeof handler === "function"
155
+ ? function trackedThenHandler(...args) {
156
+ const value = handler.apply(this, args);
157
+ addTrackerDependency(chainTracker, promiseObservationTracker(value));
158
+ return trackedPromiseTarget(value);
159
+ }
160
+ : handler;
161
+ return trackObservationTracker(chainTracker, promiseTarget.then(wrapHandler(onFulfilled), wrapHandler(onRejected)), true);
154
162
  };
155
163
  if (prop === "catch") return function promiseCatch(onRejected) {
156
164
  consumeTrackedObservations(tracker);
@@ -276,8 +284,43 @@ function formatRef(result) {
276
284
  return "[" + parts.join("; ") + "]";
277
285
  }
278
286
 
287
+ function formatChildResultString(result) {
288
+ const output = typeof result?.output === "string" ? result.output.trim() : "";
289
+ return output || formatRef(result);
290
+ }
291
+
292
+ function decorateWorkflowChildResult(result) {
293
+ if (!result || typeof result !== "object" || Array.isArray(result)) return result;
294
+ Object.defineProperties(result, {
295
+ toString: { value() { return formatChildResultString(this); }, enumerable: false, configurable: true },
296
+ });
297
+ return result;
298
+ }
299
+
279
300
  let runFingerprints = new Map();
280
301
 
302
+ function validateExtensionBindings(value, label) {
303
+ if (value === undefined) return;
304
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(label + " extensionBindings must be a plain JSON object.");
305
+ const keys = Object.keys(value);
306
+ if (keys.length > 16) throw new Error(label + " extensionBindings supports at most 16 namespaces.");
307
+ for (const key of keys) if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62})\/[1-9][0-9]{0,8}$/.test(key)) throw new Error(label + " extensionBindings namespace '" + key + "' must use a package-like name followed by '/<positive-version>'.");
308
+ assertJsonValue(value, label + " extensionBindings");
309
+ let propertyCount = 0;
310
+ function visit(entry, depth) {
311
+ if (!entry || typeof entry !== "object") return;
312
+ if (depth > 16) throw new Error(label + " extensionBindings exceeds the maximum nesting depth of 16.");
313
+ if (Array.isArray(entry)) { for (const item of entry) visit(item, depth + 1); return; }
314
+ for (const child of Object.values(entry)) {
315
+ propertyCount++;
316
+ if (propertyCount > 256) throw new Error(label + " extensionBindings exceeds 256 total properties.");
317
+ visit(child, depth + 1);
318
+ }
319
+ }
320
+ visit(value, 0);
321
+ if (new TextEncoder().encode(stableRunJson(value)).byteLength > 16384) throw new Error(label + " extensionBindings canonical JSON exceeds 16384 bytes.");
322
+ }
323
+
281
324
  function validateRunCall(key, params, label, fingerprints) {
282
325
  if (typeof key !== "string" || !runKeyPattern.test(key)) throw new Error(label + " has an invalid key.");
283
326
  if (!params || typeof params !== "object" || Array.isArray(params)) throw new Error(label + " requires a params object.");
@@ -290,6 +333,7 @@ function validateRunCall(key, params, label, fingerprints) {
290
333
  if (params.gate !== undefined && (typeof params.gate !== "string" || !params.gate.trim())) throw new Error(label + " gate must be a non-empty command string.");
291
334
  if (params.gate !== undefined && params.acceptance !== undefined) throw new Error(label + " gate cannot be combined with acceptance; use one gate command or acceptance.verify.");
292
335
  if (params.gate !== undefined && params.resume !== undefined) throw new Error(label + " gate is not supported with retained resume.");
336
+ if (params.extensionBindings !== undefined && params.resume !== undefined) throw new Error(label + " extensionBindings is not supported with retained resume; resume uses the original retained child binding.");
293
337
  if (params.resume !== undefined && typeof params.resume !== "string") {
294
338
  const reference = params.resume;
295
339
  if (!reference || typeof reference !== "object" || Array.isArray(reference)) throw new Error(label + " resume must be a retained run id or keyed workflow receipt reference.");
@@ -302,6 +346,7 @@ function validateRunCall(key, params, label, fingerprints) {
302
346
  if (typeof params.resume === "string" && !params.resume.trim()) throw new Error(label + " resume must be a non-empty retained run id.");
303
347
  if (params.resume !== undefined && params.agent !== undefined) throw new Error(label + " resume and agent are mutually exclusive.");
304
348
  if (params.resume !== undefined && (typeof params.task !== "string" || !params.task.trim())) throw new Error(label + " resume requires a non-empty task follow-up.");
349
+ validateExtensionBindings(params.extensionBindings, label);
305
350
  assertJsonValue(params, label + " params");
306
351
  const fingerprint = stableRunJson(params);
307
352
  const existing = fingerprints.get(key);
@@ -312,7 +357,8 @@ function validateRunCall(key, params, label, fingerprints) {
312
357
  const runs = Object.freeze({
313
358
  run(key, params) {
314
359
  validateRunCall(key, params, "runs.run", runFingerprints);
315
- return hostCall("run", { key, params }, { key, operation: "run" });
360
+ const launched = runHostCall(key, params, false);
361
+ return trackRunObservation([{ key, operation: "run", callId: launched.callId }], launched.promise.then(decorateWorkflowChildResult));
316
362
  },
317
363
  all(items) {
318
364
  if (!Array.isArray(items)) throw new Error("runs.all(items) requires an array.");
@@ -329,7 +375,7 @@ const runs = Object.freeze({
329
375
  runFingerprints = fingerprints;
330
376
  const batch = { id: "batch-" + (++nextCallId), calls };
331
377
  const launched = calls.map(({ key, params }) => runHostCall(key, params, true, batch));
332
- return trackRunObservation(launched.map(({ key, callId }) => ({ key, operation: "run", callId })), Promise.all(launched.map(({ promise }) => promise)).then((results) => wrapRunsAllResults(results, calls.map(({ key }) => key))));
378
+ return trackRunObservation(launched.map(({ key, callId }) => ({ key, operation: "run", callId })), Promise.all(launched.map(({ promise }) => promise)).then((results) => wrapRunsAllResults(results.map(decorateWorkflowChildResult), calls.map(({ key }) => key))));
333
379
  },
334
380
  steer(key, message, options = {}) {
335
381
  if (typeof key !== "string" || !runKeyPattern.test(key)) throw new Error("runs.steer has an invalid key.");
@@ -599,6 +645,7 @@ export interface WorkflowScriptChildResult {
599
645
  output: string;
600
646
  error?: string;
601
647
  detached?: boolean;
648
+ interrupted?: boolean;
602
649
  structuredOutput?: unknown;
603
650
  requestedContext?: "fresh" | "fork";
604
651
  resolvedContext?: "fresh" | "fork" | "mixed";