@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.
- package/CHANGELOG.md +145 -0
- package/README.md +2 -2
- package/dist/src/agent-execution.d.ts +43 -10
- package/dist/src/agent-execution.js +442 -162
- package/dist/src/background-widget.d.ts +5 -1
- package/dist/src/background-widget.js +5 -10
- package/dist/src/decoders.d.ts +2 -3
- package/dist/src/decoders.js +59 -37
- package/dist/src/execution.d.ts +6 -2
- package/dist/src/execution.js +48 -7
- package/dist/src/host-delivery.js +5 -8
- package/dist/src/host-navigator.js +5 -2
- package/dist/src/host-phases.d.ts +1 -0
- package/dist/src/host-recovery.d.ts +11 -8
- package/dist/src/host-recovery.js +14 -22
- package/dist/src/host-runtime.js +56 -5
- package/dist/src/host-view.d.ts +6 -1
- package/dist/src/host-view.js +46 -20
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +246 -118
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +1985 -1690
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +8 -5
- package/dist/src/paths.js +24 -13
- package/dist/src/persistence.d.ts +1 -0
- package/dist/src/persistence.js +1 -0
- package/dist/src/pi-runtime-runner.js +4 -1
- package/dist/src/registry.d.ts +9 -7
- package/dist/src/registry.js +29 -71
- package/dist/src/runtime/agent-runner.d.ts +2 -0
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/session-lease.d.ts +1 -1
- package/dist/src/session-lease.js +45 -11
- package/dist/src/settings.d.ts +19 -3
- package/dist/src/settings.js +63 -50
- package/dist/src/store.d.ts +3 -1
- package/dist/src/store.js +17 -1
- package/dist/src/trajectory.d.ts +12 -3
- package/dist/src/trajectory.js +32 -11
- package/dist/src/types.d.ts +129 -45
- package/dist/src/types.js +5 -8
- package/dist/src/utils.d.ts +21 -1
- package/dist/src/utils.js +46 -6
- package/dist/src/validation.d.ts +14 -3
- package/dist/src/validation.js +143 -87
- package/dist/starter/index.js +8 -11
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +8 -9
- package/dist/subagents/index.js +904 -956
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +48 -31
- package/dist/subagents/src/contracts.js +20 -9
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +14 -8
- package/dist/subagents/src/manager.js +52 -73
- package/dist/subagents/src/navigator.js +3 -14
- package/dist/subagents/src/view.d.ts +1 -1
- package/dist/subagents/src/view.js +15 -68
- package/dist/subagents/src/worktree.js +1 -1
- package/dist/trajectory/assets/index.html +35 -13
- package/dist/trajectory/index.js +576 -932
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +35 -13
- package/dist/trajectory/src/index.js +134 -104
- package/dist/trajectory/src/server.js +268 -27
- package/dist/trajectory/src/server.js.map +3 -3
- package/dist/trajectory/src/startup-mutex.d.ts +17 -0
- package/dist/trajectory/src/startup-mutex.js +172 -0
- package/examples/workflow-extension-template/README.md +4 -7
- package/examples/workflow-extension-template/extension.test.mjs +4 -8
- package/examples/workflow-extension-template/index.js +0 -3
- package/package.json +10 -10
- package/skills/pi-extensible-workflows/SKILL.md +12 -8
- package/src/agent-execution.ts +412 -164
- package/src/background-widget.ts +5 -11
- package/src/decoders.ts +51 -37
- package/src/execution.ts +46 -9
- package/src/host-delivery.ts +5 -8
- package/src/host-navigator.ts +4 -2
- package/src/host-phases.ts +1 -1
- package/src/host-recovery.ts +23 -28
- package/src/host-runtime.ts +44 -6
- package/src/host-view.ts +41 -16
- package/src/host.ts +229 -142
- package/src/index.ts +1 -2
- package/src/paths.ts +18 -9
- package/src/persistence.ts +1 -0
- package/src/pi-runtime-runner.ts +3 -1
- package/src/registry.ts +28 -64
- package/src/runtime/agent-runner.ts +2 -0
- package/src/script-tools.ts +76 -0
- package/src/session-lease.ts +34 -6
- package/src/settings.ts +56 -50
- package/src/store.ts +18 -2
- package/src/trajectory.ts +32 -15
- package/src/types.ts +82 -34
- package/src/utils.ts +42 -5
- package/src/validation.ts +121 -80
- package/starter/index.ts +0 -1
- package/starter/review-loop.ts +8 -9
- package/starter/roles/developer.md +0 -1
- package/starter/roles/oracle.md +0 -1
- package/starter/roles/researcher.md +0 -1
- package/starter/roles/reviewer.md +0 -1
- package/starter/roles/scout.md +0 -1
- package/subagents/README.md +10 -16
- package/subagents/src/contracts.ts +21 -11
- package/subagents/src/index.ts +17 -8
- package/subagents/src/manager.ts +48 -128
- package/subagents/src/navigator.ts +4 -13
- package/subagents/src/view.ts +16 -63
- package/subagents/src/worktree.ts +1 -1
- package/trajectory/src/assets/index.html +35 -13
- package/trajectory/src/index.ts +112 -91
- package/trajectory/src/server.ts +46 -10
- package/trajectory/src/startup-mutex.ts +127 -0
- package/dist/src/roles.d.ts +0 -70
- package/dist/src/roles.js +0 -325
- package/dist/starter/roles/developer.md +0 -6
- package/dist/starter/roles/oracle.md +0 -18
- package/dist/starter/roles/researcher.md +0 -20
- package/dist/starter/roles/reviewer.md +0 -19
- package/dist/starter/roles/scout.md +0 -18
- package/examples/workflow-extension-template/roles/reviewer.md +0 -4
- package/src/roles.ts +0 -326
package/dist/src/validation.js
CHANGED
|
@@ -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,
|
|
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", "
|
|
332
|
-
function
|
|
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"))
|
|
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
|
|
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
|
|
473
|
-
return { ...placement, kind, start: call.start, end: call.end, name: staticString(first), prompt: null, model: 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
|
-
|
|
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
|
|
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]
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
634
|
-
|
|
635
|
-
|
|
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";
|
package/dist/starter/index.js
CHANGED
|
@@ -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
|
|
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
|
-
{
|
|
28
|
+
{ review: review.findings }
|
|
29
29
|
);
|
|
30
|
-
devResult = await
|
|
30
|
+
devResult = await developer.send(devPrompt);
|
|
31
31
|
review = await agent(
|
|
32
32
|
prompt(
|
|
33
|
-
`Review the implementation against the task.
|
|
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
|
-
<
|
|
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": ";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,aAAS,aAAa,GAAG,cAAc,eAAe,cAAc,GAAG;AACrE,YAAM,YACJ,eAAe,IACX,OAAO,kCAAkC,EAAE,KAAK,CAAC,IACjD;AAAA,QACE;AAAA;AAAA
|
|
4
|
+
"mappings": "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",
|
|
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
|
|
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
|
-
<
|
|
18
|
-
|
|
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;
|