@marcoscale98/pi-extensible-workflows 5.17.0-fork.1 → 6.2.1-fork.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/README.md +2 -2
  3. package/dist/src/agent-execution.d.ts +43 -10
  4. package/dist/src/agent-execution.js +442 -162
  5. package/dist/src/background-widget.d.ts +5 -1
  6. package/dist/src/background-widget.js +5 -10
  7. package/dist/src/decoders.d.ts +2 -3
  8. package/dist/src/decoders.js +59 -37
  9. package/dist/src/execution.d.ts +6 -2
  10. package/dist/src/execution.js +48 -7
  11. package/dist/src/host-delivery.js +5 -8
  12. package/dist/src/host-navigator.js +5 -2
  13. package/dist/src/host-phases.d.ts +1 -0
  14. package/dist/src/host-recovery.d.ts +11 -8
  15. package/dist/src/host-recovery.js +14 -22
  16. package/dist/src/host-runtime.js +56 -5
  17. package/dist/src/host-view.d.ts +6 -1
  18. package/dist/src/host-view.js +46 -20
  19. package/dist/src/host.d.ts +2 -2
  20. package/dist/src/host.js +246 -118
  21. package/dist/src/index.d.ts +1 -2
  22. package/dist/src/index.js +1985 -1690
  23. package/dist/src/index.js.map +3 -3
  24. package/dist/src/paths.d.ts +8 -5
  25. package/dist/src/paths.js +24 -13
  26. package/dist/src/persistence.d.ts +1 -0
  27. package/dist/src/persistence.js +1 -0
  28. package/dist/src/pi-runtime-runner.js +4 -1
  29. package/dist/src/registry.d.ts +9 -7
  30. package/dist/src/registry.js +29 -71
  31. package/dist/src/runtime/agent-runner.d.ts +2 -0
  32. package/dist/src/script-tools.d.ts +27 -0
  33. package/dist/src/script-tools.js +87 -0
  34. package/dist/src/session-lease.d.ts +1 -1
  35. package/dist/src/session-lease.js +45 -11
  36. package/dist/src/settings.d.ts +19 -3
  37. package/dist/src/settings.js +63 -50
  38. package/dist/src/store.d.ts +3 -1
  39. package/dist/src/store.js +17 -1
  40. package/dist/src/trajectory.d.ts +12 -3
  41. package/dist/src/trajectory.js +32 -11
  42. package/dist/src/types.d.ts +129 -45
  43. package/dist/src/types.js +5 -8
  44. package/dist/src/utils.d.ts +21 -1
  45. package/dist/src/utils.js +46 -6
  46. package/dist/src/validation.d.ts +14 -3
  47. package/dist/src/validation.js +143 -87
  48. package/dist/starter/index.js +8 -11
  49. package/dist/starter/index.js.map +1 -1
  50. package/dist/starter/review-loop.d.ts +0 -13
  51. package/dist/starter/review-loop.js +8 -9
  52. package/dist/subagents/index.js +904 -956
  53. package/dist/subagents/index.js.map +3 -3
  54. package/dist/subagents/src/contracts.d.ts +48 -31
  55. package/dist/subagents/src/contracts.js +20 -9
  56. package/dist/subagents/src/index.d.ts +1 -1
  57. package/dist/subagents/src/index.js +14 -8
  58. package/dist/subagents/src/manager.js +52 -73
  59. package/dist/subagents/src/navigator.js +3 -14
  60. package/dist/subagents/src/view.d.ts +1 -1
  61. package/dist/subagents/src/view.js +15 -68
  62. package/dist/subagents/src/worktree.js +1 -1
  63. package/dist/trajectory/assets/index.html +35 -13
  64. package/dist/trajectory/index.js +576 -932
  65. package/dist/trajectory/index.js.map +3 -3
  66. package/dist/trajectory/src/assets/index.html +35 -13
  67. package/dist/trajectory/src/index.js +134 -104
  68. package/dist/trajectory/src/server.js +268 -27
  69. package/dist/trajectory/src/server.js.map +3 -3
  70. package/dist/trajectory/src/startup-mutex.d.ts +17 -0
  71. package/dist/trajectory/src/startup-mutex.js +172 -0
  72. package/examples/workflow-extension-template/README.md +4 -7
  73. package/examples/workflow-extension-template/extension.test.mjs +4 -8
  74. package/examples/workflow-extension-template/index.js +0 -3
  75. package/package.json +10 -10
  76. package/skills/pi-extensible-workflows/SKILL.md +12 -8
  77. package/src/agent-execution.ts +412 -164
  78. package/src/background-widget.ts +5 -11
  79. package/src/decoders.ts +51 -37
  80. package/src/execution.ts +46 -9
  81. package/src/host-delivery.ts +5 -8
  82. package/src/host-navigator.ts +4 -2
  83. package/src/host-phases.ts +1 -1
  84. package/src/host-recovery.ts +23 -28
  85. package/src/host-runtime.ts +44 -6
  86. package/src/host-view.ts +41 -16
  87. package/src/host.ts +229 -142
  88. package/src/index.ts +1 -2
  89. package/src/paths.ts +18 -9
  90. package/src/persistence.ts +1 -0
  91. package/src/pi-runtime-runner.ts +3 -1
  92. package/src/registry.ts +28 -64
  93. package/src/runtime/agent-runner.ts +2 -0
  94. package/src/script-tools.ts +76 -0
  95. package/src/session-lease.ts +34 -6
  96. package/src/settings.ts +56 -50
  97. package/src/store.ts +18 -2
  98. package/src/trajectory.ts +32 -15
  99. package/src/types.ts +82 -34
  100. package/src/utils.ts +42 -5
  101. package/src/validation.ts +121 -80
  102. package/starter/index.ts +0 -1
  103. package/starter/review-loop.ts +8 -9
  104. package/starter/roles/developer.md +0 -1
  105. package/starter/roles/oracle.md +0 -1
  106. package/starter/roles/researcher.md +0 -1
  107. package/starter/roles/reviewer.md +0 -1
  108. package/starter/roles/scout.md +0 -1
  109. package/subagents/README.md +10 -16
  110. package/subagents/src/contracts.ts +21 -11
  111. package/subagents/src/index.ts +17 -8
  112. package/subagents/src/manager.ts +48 -128
  113. package/subagents/src/navigator.ts +4 -13
  114. package/subagents/src/view.ts +16 -63
  115. package/subagents/src/worktree.ts +1 -1
  116. package/trajectory/src/assets/index.html +35 -13
  117. package/trajectory/src/index.ts +112 -91
  118. package/trajectory/src/server.ts +46 -10
  119. package/trajectory/src/startup-mutex.ts +127 -0
  120. package/dist/src/roles.d.ts +0 -70
  121. package/dist/src/roles.js +0 -325
  122. package/dist/starter/roles/developer.md +0 -6
  123. package/dist/starter/roles/oracle.md +0 -18
  124. package/dist/starter/roles/researcher.md +0 -20
  125. package/dist/starter/roles/reviewer.md +0 -19
  126. package/dist/starter/roles/scout.md +0 -18
  127. package/examples/workflow-extension-template/roles/reviewer.md +0 -4
  128. package/src/roles.ts +0 -326
@@ -2,13 +2,11 @@ import { readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import * as acorn from "acorn";
4
4
  import { Script } from "node:vm";
5
- import { assertModelThinking, validateSchema, deepFreeze, errorText, fail, jsonObject, jsonValue, mergeWorkflowExtensionSettings, modelAliasName, modelCapability, object, positiveInteger, resolveModelReference, resourcePatternHasMagic, unknownModel } from "./utils.js";
5
+ import { assertModelThinking, validateSchema, deepFreeze, errorText, fail, jsonObject, jsonValue, object, positiveInteger, resourcePatternHasMagic } from "./utils.js";
6
6
  import { WORKFLOW_CALL_KINDS } from "./types.js";
7
7
  export { validateSchema } from "./utils.js";
8
- import { loadAgentDefinitions, loadProjectAgentDefinitions } from "./roles.js";
9
- export { loadAgentDefinitions, loadProjectAgentDefinitions, parseRoleMarkdown, workflowRoleDirectories } from "./roles.js";
10
8
  import { validateContextFileScopes, validateSelectorList } from "./settings.js";
11
- export { DEFAULT_SETTINGS, loadSettings, loadSettingsOverrides, resolveAgentResourcePolicy, resolveWorkflowSettings, saveModelAliases, validateContextFileScopes, validateModelAliasAvailability, validateSelectorList, validateWorkflowExtensionSettings, workflowProjectSettingsPath, workflowSettingsPath } from "./settings.js";
9
+ export { DEFAULT_SETTINGS, loadCodemodeToolsSetting, loadSettings, loadSettingsOverrides, resolveAgentResourcePolicy, resolveWorkflowSettings, saveModelAliases, validateContextFileScopes, validateModelAliasAvailability, validateSelectorList, validateWorkflowExtensionSettings, workflowProjectSettingsPath, workflowSettingsPath, workflowToolExposure } from "./settings.js";
12
10
  export function validateCheckpoint(value) {
13
11
  if (!object(value) || Object.keys(value).some((key) => !["name", "prompt", "context"].includes(key)) || typeof value.name !== "string" || value.name.trim() === "" || typeof value.prompt !== "string" || !jsonValue(value.context))
14
12
  fail("INVALID_METADATA", "checkpoint requires only name, prompt, and JSON context");
@@ -18,23 +16,6 @@ export function validateCheckpoint(value) {
18
16
  fail("INVALID_METADATA", "checkpoint context exceeds 4096 UTF-8 bytes");
19
17
  return { name: value.name, prompt: value.prompt, context: value.context };
20
18
  }
21
- function validateRolePolicies(definitions, roles, availableModels, aliases = {}, knownModels = availableModels, settingsPath) {
22
- for (const role of roles) {
23
- const definition = definitions[role];
24
- if (!definition)
25
- continue;
26
- if (definition.model !== undefined) {
27
- const resolved = modelCapability(definition.model, aliases, knownModels, settingsPath);
28
- if (!availableModels.has(resolved)) {
29
- if (modelAliasName(definition.model, aliases))
30
- unknownModel(definition.model, resolved, settingsPath);
31
- fail("UNKNOWN_MODEL", `Unknown model for role ${role}: ${resolved}`);
32
- }
33
- }
34
- // Role tools absent from the session are tolerated at launch: the runtime resolve emits a
35
- // warning and the agent runs without them. Doctor still reports unmatched patterns.
36
- }
37
- }
38
19
  function validateWorkflowMetadata(value) {
39
20
  if (!object(value) || typeof value.name !== "string" || value.name.trim() === "")
40
21
  fail("INVALID_METADATA", "Workflow metadata requires a non-empty name");
@@ -244,6 +225,26 @@ function callHasTrailingComma(source, call) {
244
225
  }
245
226
  return current?.type.label === ")" && previous?.type.label === ",";
246
227
  }
228
+ /**
229
+ * The `tools.<id>` members a workflow script names statically. Any other use of `tools`, such as a
230
+ * local declaration that shadows the global or passing it around, makes the set unknowable: undefined.
231
+ */
232
+ export function scriptToolReferences(script) {
233
+ const body = workflowBody(script);
234
+ if (!body.trim())
235
+ return [];
236
+ const names = new Set();
237
+ const opaque = (node, parent) => {
238
+ if (node.type === "Identifier" && node.name === "tools") {
239
+ if (parent?.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier")
240
+ names.add(parent.property.name);
241
+ else if (!(parent?.type === "Property" && parent.key === node && !parent.computed && !parent.shorthand || parent?.type === "MemberExpression" && parent.property === node && !parent.computed))
242
+ return true;
243
+ }
244
+ return astChildren(node).some((child) => opaque(child, node));
245
+ };
246
+ return opaque(parseWorkflow(body)) ? undefined : [...names];
247
+ }
247
248
  export function instrumentWorkflow(script) {
248
249
  const body = workflowBody(script);
249
250
  if (!body.trim())
@@ -328,8 +329,9 @@ export function workflowPrompt(template, values) {
328
329
  return typeof value === "string" ? value : JSON.stringify(value, null, 2);
329
330
  });
330
331
  }
331
- const AGENT_OPTION_KEYS = new Set(["label", "model", "tools", "skills", "extensions", "contextFiles", "role", "outputSchema", "retries", "timeoutMs"]);
332
- function validateAgentOption(key, value, aliases, knownModels, settingsPath) {
332
+ const AGENT_OPTION_KEYS = new Set(["label", "model", "tools", "excludeTools", "skills", "extensions", "contextFiles", "outputSchema", "retries", "timeoutMs"]);
333
+ export function isCoreAgentOption(key) { return AGENT_OPTION_KEYS.has(key); }
334
+ function validateAgentOption(key, value) {
333
335
  switch (key) {
334
336
  case "label":
335
337
  if (typeof value !== "string" || !value.trim())
@@ -339,21 +341,21 @@ function validateAgentOption(key, value, aliases, knownModels, settingsPath) {
339
341
  if (typeof value !== "string" || !value.trim())
340
342
  fail("INVALID_METADATA", "agent model must be a non-empty string");
341
343
  assertModelThinking(value, "agent model");
342
- if (aliases !== undefined)
343
- resolveModelReference(value, aliases, knownModels, settingsPath);
344
344
  break;
345
345
  case "tools":
346
346
  case "skills":
347
347
  case "extensions":
348
348
  validateSelectorList(value, "agent options", key, "INVALID_METADATA", key !== "extensions");
349
349
  break;
350
+ case "excludeTools":
351
+ if (!Array.isArray(value) || value.some((tool) => typeof tool !== "string" || !tool.trim() || tool !== tool.trim() || tool.startsWith("!") || resourcePatternHasMagic(tool)))
352
+ fail("INVALID_METADATA", "agent excludeTools must be an array of exact tool names");
353
+ if (value.includes("workflow_result"))
354
+ fail("INVALID_METADATA", "agent excludeTools cannot remove workflow_result");
355
+ break;
350
356
  case "contextFiles":
351
357
  validateContextFileScopes(value, "agent options");
352
358
  break;
353
- case "role":
354
- if (typeof value !== "string" || !value.trim())
355
- fail("INVALID_METADATA", "agent role must be a non-empty string");
356
- break;
357
359
  case "outputSchema":
358
360
  validateSchema(value, "agent outputSchema");
359
361
  break;
@@ -463,43 +465,123 @@ export function inspectWorkflowScript(script) {
463
465
  knownOptionEntries.push([key, value.value]);
464
466
  }
465
467
  const knownOptions = Object.fromEntries(knownOptionEntries);
466
- const base = { ...placement, kind, start: call.start, end: call.end, name: null, prompt: staticString(first), model: staticString(propertyNode(options, "model")), label: staticString(propertyNode(options, "label")), role: staticString(propertyNode(options, "role")) };
468
+ const base = { ...placement, kind, start: call.start, end: call.end, name: null, prompt: staticString(first), model: staticString(propertyNode(options, "model")), label: staticString(propertyNode(options, "label")) };
467
469
  return { ...base, ...(retries.known && typeof retries.value === "number" ? { retries: retries.value } : {}), ...(staticOutputSchema === undefined ? {} : { outputSchema: staticOutputSchema }), ...(optionKeys.length ? { options: knownOptions, optionKeys } : {}) };
468
470
  }
469
471
  if (kind === "checkpoint")
470
- return { ...placement, kind, start: call.start, end: call.end, name: staticString(propertyNode(first, "name")), prompt: staticString(propertyNode(first, "prompt")), model: null, role: null };
472
+ return { ...placement, kind, start: call.start, end: call.end, name: staticString(propertyNode(first, "name")), prompt: staticString(propertyNode(first, "prompt")), model: null };
471
473
  if (kind === "shell")
472
- return { ...placement, kind, start: call.start, end: call.end, name: staticString(first), prompt: null, model: null, role: null };
473
- return { ...placement, kind, start: call.start, end: call.end, name: staticString(first), prompt: null, model: null, role: null };
474
+ return { ...placement, kind, start: call.start, end: call.end, name: staticString(first), prompt: null, model: null };
475
+ return { ...placement, kind, start: call.start, end: call.end, name: staticString(first), prompt: null, model: null };
476
+ });
477
+ }
478
+ /** Every option is a preparation input, because any hook may read it; one dynamic value leaves the whole call to runtime. */
479
+ function staticPreparationOptions(node, omitted) {
480
+ if (node?.type !== "ObjectExpression")
481
+ return undefined;
482
+ const options = new Map();
483
+ for (const property of node.properties) {
484
+ if (property.type === "SpreadElement" || property.computed)
485
+ return undefined;
486
+ const key = propertyKeyName(property);
487
+ if (key === undefined)
488
+ return undefined;
489
+ if (key === omitted)
490
+ continue;
491
+ const value = staticValue(property.value);
492
+ // NOTE: an output schema only shapes the result, so a computed one (a const declared elsewhere) does not hide the call.
493
+ if (key === "outputSchema" && !value.known)
494
+ continue;
495
+ if (!value.known || !jsonValue(value.value))
496
+ return undefined;
497
+ options.set(key, value.value);
498
+ }
499
+ return Object.fromEntries(options);
500
+ }
501
+ /**
502
+ * A handle prepares its first turn with the create options merged with that send's options, so create options are complete
503
+ * only when every use of the handle provably passes no send options: `agent.create(...).send(prompt)` directly, or a
504
+ * `const` handle whose every reference is `handle.send(prompt)`. Computed access, destructuring, aliases or any other
505
+ * use leave the handle to runtime.
506
+ */
507
+ function handlesWithCompleteOptions(program) {
508
+ const parents = new Map();
509
+ const references = new Map();
510
+ const visit = (node) => {
511
+ if (node.type === "Identifier")
512
+ references.set(node.name, [...(references.get(node.name) ?? []), node]);
513
+ for (const child of astChildren(node)) {
514
+ parents.set(child, node);
515
+ visit(child);
516
+ }
517
+ };
518
+ visit(program);
519
+ const plainSend = (member) => {
520
+ const call = member ? parents.get(member) : undefined;
521
+ return member?.type === "MemberExpression" && !member.computed && member.property.type === "Identifier" && member.property.name === "send"
522
+ && call?.type === "CallExpression" && call.callee === member && call.arguments.length <= 1 && call.arguments.every((argument) => argument.type !== "SpreadElement");
523
+ };
524
+ return agentCreateCalls(program).filter((create) => {
525
+ const parent = parents.get(create);
526
+ if (parent?.type === "MemberExpression" && parent.object === create)
527
+ return plainSend(parent);
528
+ const declaration = parent ? parents.get(parent) : undefined;
529
+ if (parent?.type !== "VariableDeclarator" || parent.init !== create || parent.id.type !== "Identifier" || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const")
530
+ return false;
531
+ const name = parent.id.name;
532
+ return (references.get(name) ?? []).every((reference) => reference === parent.id || (parents.get(reference)?.type === "MemberExpression" && parents.get(reference).object === reference && plainSend(parents.get(reference))));
474
533
  });
475
534
  }
476
- function validateStaticAgentOptions(node, aliases = {}, knownModels, settingsPath) {
535
+ /**
536
+ * Distinct, completely static option objects of agent(...) and agent.create(...) calls, so launch can inspect them
537
+ * before any effect. Calls with any dynamic option stay runtime-checked, and so do handles whose sends may add options.
538
+ */
539
+ export function staticAgentPreparationOptions(script, registered = {}) {
540
+ const program = parseWorkflow(script);
541
+ const sources = [program, ...registeredFunctionPrograms(program, registered)];
542
+ const distinct = new Map();
543
+ for (const source of sources)
544
+ for (const options of programAgentOptions(source))
545
+ distinct.set(JSON.stringify(options), deepFreeze(options));
546
+ return [...distinct.values()];
547
+ }
548
+ /** Programs of the registered functions the script calls by name, so their agents are inspected before launch like inline ones. */
549
+ function registeredFunctionPrograms(program, registered) {
550
+ const called = new Set();
551
+ const visit = (node) => {
552
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && Object.prototype.hasOwnProperty.call(registered, node.callee.name))
553
+ called.add(node.callee.name);
554
+ for (const child of astChildren(node))
555
+ visit(child);
556
+ };
557
+ visit(program);
558
+ const programs = [];
559
+ for (const name of called) {
560
+ const source = registered[name]?.run.toString() ?? "";
561
+ // A method prints as `async run(...) {}`, which only parses inside an object literal.
562
+ for (const wrapped of [`(${source})`, `({${source}})`]) {
563
+ try {
564
+ programs.push(acorn.parse(wrapped, { ecmaVersion: "latest", sourceType: "module" }));
565
+ break;
566
+ }
567
+ catch { /* try the next form; an unparsable function stays runtime-checked */ }
568
+ }
569
+ }
570
+ return programs;
571
+ }
572
+ function programAgentOptions(program) {
573
+ const calls = workflowCalls(program).filter((call) => call.callee.name === "agent").map((call) => call.arguments.some((argument) => argument.type === "SpreadElement") ? undefined : call.arguments.length < 2 ? {} : staticPreparationOptions(callArgument(call, 1)));
574
+ const handles = handlesWithCompleteOptions(program).map((call) => staticPreparationOptions(callArgument(call, 0), "name"));
575
+ return [...calls, ...handles].filter((options) => options !== undefined);
576
+ }
577
+ function validateStaticAgentOptions(node) {
477
578
  if (node?.type !== "ObjectExpression")
478
579
  return;
479
580
  for (const key of AGENT_OPTION_KEYS) {
480
581
  const value = staticValue(propertyNode(node, key));
481
582
  if (value.known)
482
- validateAgentOption(key, value.value, aliases, knownModels, settingsPath);
483
- }
484
- }
485
- function hasDynamicAgentRole(node) {
486
- if (!node)
487
- return false;
488
- if (node.type !== "ObjectExpression")
489
- return true;
490
- for (let index = node.properties.length - 1; index >= 0; index -= 1) {
491
- const property = node.properties[index];
492
- if (!property || property.type === "SpreadElement" || property.computed)
493
- return true;
494
- const key = propertyKeyName(property);
495
- if (key === "role") {
496
- const roleValue = staticValue(property.value);
497
- if (roleValue.known && typeof roleValue.value === "string")
498
- return false;
499
- return true;
500
- }
583
+ validateAgentOption(key, value.value);
501
584
  }
502
- return false;
503
585
  }
504
586
  function validateStaticShellOptions(call) {
505
587
  if (call.arguments.some((argument) => argument.type === "SpreadElement"))
@@ -544,7 +626,7 @@ export function preflight(script, capabilities, schemas = [], metadata = { name:
544
626
  for (const call of calls) {
545
627
  const operation = call.callee.name;
546
628
  if (operation === "agent")
547
- validateStaticAgentOptions(call.arguments[1], capabilities.modelAliases ?? {}, capabilities.knownModels ?? capabilities.models, capabilities.settingsPath);
629
+ validateStaticAgentOptions(call.arguments[1]);
548
630
  if (operation === "withWorktree")
549
631
  validateStaticWithWorktree(call, compatibility);
550
632
  if (operation === "shell")
@@ -562,11 +644,10 @@ export function preflight(script, capabilities, schemas = [], metadata = { name:
562
644
  const options = call.arguments.length === 1 ? callArgument(call, 0) : undefined;
563
645
  if (options?.type !== "ObjectExpression" || !literalString(propertyNode(options, "name"))?.trim())
564
646
  fail("INVALID_METADATA", "agent.create requires an options object with a stable explicit name");
565
- validateStaticAgentOptions(options, capabilities.modelAliases ?? {}, capabilities.knownModels ?? capabilities.models, capabilities.settingsPath);
647
+ validateStaticAgentOptions(options);
566
648
  return options;
567
649
  });
568
650
  const agentOptions = [...calls.filter((call) => call.callee.name === "agent").map((call) => callArgument(call, 1)), ...handleOptions];
569
- const dynamicAgentRoles = agentOptions.some((options) => hasDynamicAgentRole(options));
570
651
  const staticSchemas = [];
571
652
  for (const options of agentOptions) {
572
653
  const value = staticValue(propertyNode(options, "outputSchema"));
@@ -577,28 +658,17 @@ export function preflight(script, capabilities, schemas = [], metadata = { name:
577
658
  staticSchemas.push(schema);
578
659
  }
579
660
  checkedSchemas.push(...staticSchemas);
580
- const modelRefs = agentOptions.flatMap((options) => { const requested = literalString(propertyNode(options, "model")); return requested === undefined ? [] : [{ requested, resolved: modelCapability(requested, capabilities.modelAliases, capabilities.knownModels ?? capabilities.models, capabilities.settingsPath) }]; });
581
- const models = modelRefs.map(({ resolved }) => resolved);
661
+ const models = [];
582
662
  const tools = agentOptions.flatMap((options) => {
583
663
  const value = propertyNode(options, "tools");
584
664
  return value?.type === "ArrayExpression" ? value.elements.flatMap((element) => { const tool = element && element.type !== "SpreadElement" ? literalString(element) : undefined; return tool === undefined ? [] : [tool]; }) : [];
585
665
  });
586
- const agentTypes = agentOptions.flatMap((options) => { const value = staticString(propertyNode(options, "role")); return value === null ? [] : [value]; });
587
666
  for (const pattern of tools) {
588
667
  const body = pattern.startsWith("!") ? pattern.slice(1) : pattern;
589
668
  if (!pattern.startsWith("!") && !resourcePatternHasMagic(pattern) && !capabilities.tools.has(body))
590
669
  fail("UNKNOWN_TOOL", `Unknown tool: ${body}`);
591
670
  }
592
- const missingModel = capabilities.skipModelAvailability ? undefined : modelRefs.find(({ resolved }) => !capabilities.models.has(resolved));
593
- if (missingModel) {
594
- if (modelAliasName(missingModel.requested, capabilities.modelAliases ?? {}))
595
- unknownModel(missingModel.requested, missingModel.resolved, capabilities.settingsPath);
596
- fail("UNKNOWN_MODEL", `Unknown model: ${missingModel.resolved}`);
597
- }
598
- const missingType = agentTypes.find((type) => !capabilities.agentTypes.has(type));
599
- if (missingType)
600
- fail("UNKNOWN_AGENT_TYPE", `Unknown agent type: ${missingType}`);
601
- return Object.freeze({ metadata: deepFreeze(checkedMetadata), referenced: deepFreeze({ phases, models, tools, agentTypes }), schemas: deepFreeze(checkedSchemas), dynamicAgentRoles });
671
+ return Object.freeze({ metadata: deepFreeze(checkedMetadata), referenced: deepFreeze({ phases, models, tools }), schemas: deepFreeze(checkedSchemas) });
602
672
  }
603
673
  export function validateWorkflowLaunch(params, context, registry) {
604
674
  return validateWorkflowLaunchWithRegistry(params, context, registry);
@@ -630,22 +700,8 @@ export function validateWorkflowLaunchWithRegistry(params, context, registry) {
630
700
  if (!script)
631
701
  fail("INVALID_SYNTAX", "Provide script or scriptPath");
632
702
  const metadata = validateWorkflowMetadata({ name: explicitName, ...(typeof params.description === "string" ? { description: params.description } : {}) });
633
- const globalAgentDefinitions = loadAgentDefinitions(context.cwd, context.agentDir, false, registry && typeof registry.roleDirectoryRegistrations === "function" ? registry.roleDirectoryRegistrations() : registry && typeof registry.roleDirectories === "function" ? registry.roleDirectories() : undefined);
634
- const projectAgentDefinitions = context.projectTrusted ? loadProjectAgentDefinitions(context.cwd) : {};
635
- const agentDefinitions = deepFreeze({ ...globalAgentDefinitions, ...projectAgentDefinitions });
636
- const aliases = context.modelAliases ?? {};
637
- const knownModels = context.knownModels ?? context.availableModels;
638
- const checked = preflight(script, { models: context.availableModels, tools: context.rootTools, agentTypes: new Set(Object.keys(agentDefinitions)), modelAliases: aliases, knownModels, ...(context.settingsPath ? { settingsPath: context.settingsPath } : {}) }, [], metadata);
639
- const roleNames = checked.dynamicAgentRoles ? Object.keys(agentDefinitions) : checked.referenced.agentTypes;
640
- validateRolePolicies(agentDefinitions, roleNames, context.availableModels, aliases, knownModels, context.settingsPath);
641
- if (registry?.validateExtensionSettings) {
642
- const validate = registry.validateExtensionSettings.bind(registry);
643
- const validatorContext = (source, role) => ({ source, cwd: context.cwd, projectTrusted: context.projectTrusted, ...(context.settingsPath ? { settingsPath: context.settingsPath } : {}), ...(role === undefined ? {} : { role }) });
644
- validate(context.extensionSettings, validatorContext("effective"));
645
- for (const role of roleNames) {
646
- validate(mergeWorkflowExtensionSettings(context.extensionSettings, agentDefinitions[role]?.extensionSettings), validatorContext("role", role));
647
- }
648
- }
649
- return { script, checked, agentDefinitions, projectAgentDefinitions, roleNames };
703
+ const checked = preflight(script, { models: context.availableModels, tools: context.rootTools, modelAliases: context.modelAliases ?? {}, knownModels: context.knownModels ?? context.availableModels, ...(context.settingsPath ? { settingsPath: context.settingsPath } : {}) }, [], metadata);
704
+ registry?.validateExtensionSettings(context.extensionSettings, { source: "effective", cwd: context.cwd, projectTrusted: context.projectTrusted, ...(context.settingsPath ? { settingsPath: context.settingsPath } : {}) });
705
+ return { script, checked };
650
706
  }
651
707
  export { createLaunchSnapshot, loadLaunchSnapshot } from "./utils.js";
@@ -20,23 +20,23 @@ var reviewLoop = defineWorkflowFunction({
20
20
  );
21
21
  let devResult = null;
22
22
  let review = { pass: false, findings: [] };
23
+ const developer = agent.create({ name: "developer", label: "Developer", role: "developer" });
23
24
  for (let iterations = 1; iterations <= maxIterations; iterations += 1) {
24
25
  const devPrompt = iterations === 1 ? prompt("Implement this task:\n\n{task}", { task }) : prompt(
25
- `Address the previous review findings and complete the task.
26
- <original_task>{task}</original_task>
26
+ `Address the latest review findings and complete the original task.
27
27
  <last_review>{review}</last_review>`,
28
- { task, review: review.findings }
28
+ { review: review.findings }
29
29
  );
30
- devResult = await agent(devPrompt, { role: "developer" });
30
+ devResult = await developer.send(devPrompt);
31
31
  review = await agent(
32
32
  prompt(
33
- `Review the implementation against the task. Set pass=true only when the task is complete and correct. The developer may have addressed a previous review run of yours. So its summary is related to the last round of review if present.
33
+ `Review the actual current implementation against the entire original task. Inspect the code and run relevant checks yourself. Do not rely on the developer summary or on previous findings; they may be incomplete or wrong. Set pass=true only when the whole task is complete and correct.
34
34
  <original_task>{task}</original_task>
35
- <last_review>{review}</last_review>
35
+ <previous_review_findings>{review}</previous_review_findings>
36
36
  <dev_summary>{devResult}</dev_summary>`,
37
37
  { task, devResult, review: review.findings }
38
38
  ),
39
- { role: "reviewer", outputSchema: reviewSchema }
39
+ { label: `Reviewer ${String(iterations)}`, role: "reviewer", outputSchema: reviewSchema }
40
40
  );
41
41
  if (review.pass) return { pass: true, iterations, devResult, review };
42
42
  }
@@ -65,8 +65,6 @@ var reviewLoop = defineWorkflowFunction({
65
65
  { additionalProperties: false }
66
66
  )
67
67
  });
68
- var reviewLoopInputSchema = reviewLoop.input;
69
- var reviewLoopOutputSchema = reviewLoop.output;
70
68
 
71
69
  // starter/index.ts
72
70
  var extension = {
@@ -91,8 +89,7 @@ var extension = {
91
89
  "researcher-model": {
92
90
  resolve: ({ rootModel }) => `${rootModel.provider}/${rootModel.model}`
93
91
  }
94
- },
95
- roleDirectories: [new URL("./roles/", import.meta.url)]
92
+ }
96
93
  };
97
94
  function starter_default() {
98
95
  registerWorkflowExtension(extension);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../starter/index.ts", "../../starter/review-loop.ts"],
4
- "mappings": "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",
4
+ "mappings": ";AAAA;AAAA,EACE;AAAA,OAEK;;;ACHP,SAAS,YAAyB;AAClC;AAAA,EACE;AAAA,OAEK;AAEA,IAAM,aAAa,uBAAuB;AAAA,EAC/C,aACE;AAAA,EACF,MAAM,IAAI,EAAE,MAAM,gBAAgB,EAAE,GAAG,EAAE,OAAO,OAAO,GAAG;AACxD,UAAM,eAAe,KAAK;AAAA,MACxB;AAAA,QACE,MAAM,KAAK,QAAQ;AAAA,QACnB,UAAU,KAAK,MAAM,KAAK,OAAO,CAAC;AAAA,MACpC;AAAA,MACA,EAAE,sBAAsB,MAAM;AAAA,IAChC;AACA,QAAI,YAAuB;AAC3B,QAAI,SAAsC,EAAE,MAAM,OAAO,UAAU,CAAC,EAAE;AAEtE,UAAM,YAAY,MAAM,OAAO,EAAE,MAAM,aAAa,OAAO,aAAa,MAAM,YAAY,CAAC;AAE3F,aAAS,aAAa,GAAG,cAAc,eAAe,cAAc,GAAG;AACrE,YAAM,YACJ,eAAe,IACX,OAAO,kCAAkC,EAAE,KAAK,CAAC,IACjD;AAAA,QACE;AAAA;AAAA,QAEA,EAAE,QAAQ,OAAO,SAAS;AAAA,MAC5B;AACN,kBAAY,MAAM,UAAU,KAAK,SAAS;AAC1C,eAAS,MAAM;AAAA,QACb;AAAA,UACE;AAAA;AAAA;AAAA;AAAA,UAIA,EAAE,MAAM,WAAW,QAAQ,OAAO,SAAS;AAAA,QAC7C;AAAA,QACA,EAAE,OAAO,YAAY,OAAO,UAAU,CAAC,IAAI,MAAM,YAAY,cAAc,aAAa;AAAA,MAC1F;AAEA,UAAI,OAAO,KAAM,QAAO,EAAE,MAAM,MAAM,YAAY,WAAW,OAAO;AAAA,IACtE;AAEA,WAAO,EAAE,MAAM,OAAO,YAAY,eAAe,WAAW,OAAO;AAAA,EACrE;AAAA,EACA,OAAO,KAAK;AAAA,IACV;AAAA,MACE,MAAM,KAAK,OAAO;AAAA,MAClB,eAAe,KAAK,SAAS,KAAK,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;AAAA,IAC3D;AAAA,IACA,EAAE,sBAAsB,MAAM;AAAA,EAChC;AAAA,EACA,QAAQ,KAAK;AAAA,IACX;AAAA,MACE,MAAM,KAAK,QAAQ;AAAA,MACnB,YAAY,KAAK,QAAQ;AAAA,MACzB,WAAW,KAAK,IAAI;AAAA,MACpB,QAAQ,KAAK;AAAA,QACX;AAAA,UACE,MAAM,KAAK,QAAQ;AAAA,UACnB,UAAU,KAAK,MAAM,KAAK,OAAO,CAAC;AAAA,QACpC;AAAA,QACA,EAAE,sBAAsB,MAAM;AAAA,MAChC;AAAA,IACF;AAAA,IACA,EAAE,sBAAsB,MAAM;AAAA,EAChC;AACF,CAAC;;;ADhED,IAAM,YAA+B;AAAA,EACnC,QAAQ,YAAY;AAAA,EACpB,cAAc,CAAC,SAAS;AAAA,EACxB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,WAAW,EAAE,WAAW;AAAA,EACxB,cAAc;AAAA,IACZ,kBAAkB;AAAA,MAChB,SAAS,CAAC,EAAE,UAAU,MAAM,GAAG,UAAU,QAAQ,IAAI,UAAU,KAAK;AAAA,IACtE;AAAA,IACA,mBAAmB;AAAA,MACjB,SAAS,CAAC,EAAE,UAAU,MAAM,GAAG,UAAU,QAAQ,IAAI,UAAU,KAAK;AAAA,IACtE;AAAA,IACA,eAAe;AAAA,MACb,SAAS,CAAC,EAAE,UAAU,MAAM,GAAG,UAAU,QAAQ,IAAI,UAAU,KAAK;AAAA,IACtE;AAAA,IACA,gBAAgB;AAAA,MACd,SAAS,CAAC,EAAE,UAAU,MAAM,GAAG,UAAU,QAAQ,IAAI,UAAU,KAAK;AAAA,IACtE;AAAA,IACA,oBAAoB;AAAA,MAClB,SAAS,CAAC,EAAE,UAAU,MAAM,GAAG,UAAU,QAAQ,IAAI,UAAU,KAAK;AAAA,IACtE;AAAA,EACF;AACF;AAEe,SAAR,kBAA0B;AAC/B,4BAA0B,SAAS;AACrC;",
5
5
  "names": []
6
6
  }
@@ -57,16 +57,3 @@ export declare const reviewLoop: Omit<{
57
57
  };
58
58
  }>;
59
59
  };
60
- export declare const reviewLoopInputSchema: Type.TObject<{
61
- task: Type.TString;
62
- maxIterations: Type.TOptional<Type.TInteger>;
63
- }>;
64
- export declare const reviewLoopOutputSchema: Type.TObject<{
65
- pass: Type.TBoolean;
66
- iterations: Type.TInteger;
67
- devResult: Type.TAny;
68
- review: Type.TObject<{
69
- pass: Type.TBoolean;
70
- findings: Type.TArray<Type.TString>;
71
- }>;
72
- }>;
@@ -9,17 +9,18 @@ export const reviewLoop = defineWorkflowFunction({
9
9
  }, { additionalProperties: false });
10
10
  let devResult = null;
11
11
  let review = { pass: false, findings: [] };
12
+ // One developer keeps its transcript across iterations; each reviewer starts fresh.
13
+ const developer = agent.create({ name: "developer", label: "Developer", role: "developer" });
12
14
  for (let iterations = 1; iterations <= maxIterations; iterations += 1) {
13
15
  const devPrompt = iterations === 1
14
16
  ? prompt("Implement this task:\n\n{task}", { task })
15
- : prompt(`Address the previous review findings and complete the task.
17
+ : prompt(`Address the latest review findings and complete the original task.
18
+ <last_review>{review}</last_review>`, { review: review.findings });
19
+ devResult = await developer.send(devPrompt);
20
+ review = await agent(prompt(`Review the actual current implementation against the entire original task. Inspect the code and run relevant checks yourself. Do not rely on the developer summary or on previous findings; they may be incomplete or wrong. Set pass=true only when the whole task is complete and correct.
16
21
  <original_task>{task}</original_task>
17
- <last_review>{review}</last_review>`, { task, review: review.findings });
18
- devResult = await agent(devPrompt, { role: "developer" });
19
- review = await agent(prompt(`Review the implementation against the task. Set pass=true only when the task is complete and correct. The developer may have addressed a previous review run of yours. So its summary is related to the last round of review if present.
20
- <original_task>{task}</original_task>
21
- <last_review>{review}</last_review>
22
- <dev_summary>{devResult}</dev_summary>`, { task, devResult, review: review.findings }), { role: "reviewer", outputSchema: reviewSchema });
22
+ <previous_review_findings>{review}</previous_review_findings>
23
+ <dev_summary>{devResult}</dev_summary>`, { task, devResult, review: review.findings }), { label: `Reviewer ${String(iterations)}`, role: "reviewer", outputSchema: reviewSchema });
23
24
  if (review.pass)
24
25
  return { pass: true, iterations, devResult, review };
25
26
  }
@@ -39,5 +40,3 @@ export const reviewLoop = defineWorkflowFunction({
39
40
  }, { additionalProperties: false }),
40
41
  }, { additionalProperties: false }),
41
42
  });
42
- export const reviewLoopInputSchema = reviewLoop.input;
43
- export const reviewLoopOutputSchema = reviewLoop.output;