agentweaver 0.1.16 → 0.1.18

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 (74) hide show
  1. package/README.md +148 -27
  2. package/dist/artifacts.js +114 -3
  3. package/dist/doctor/checks/executors.js +2 -2
  4. package/dist/flow-state.js +138 -1
  5. package/dist/index.js +421 -82
  6. package/dist/interactive/controller.js +305 -36
  7. package/dist/interactive/ink/index.js +24 -3
  8. package/dist/interactive/state.js +1 -0
  9. package/dist/interactive/tree.js +2 -2
  10. package/dist/interactive/web/index.js +179 -0
  11. package/dist/interactive/web/protocol.js +154 -0
  12. package/dist/interactive/web/server.js +575 -0
  13. package/dist/interactive/web/static/app.js +709 -0
  14. package/dist/interactive/web/static/index.html +77 -0
  15. package/dist/interactive/web/static/styles.css +2 -0
  16. package/dist/interactive/web/static/styles.input.css +469 -0
  17. package/dist/pipeline/auto-flow.js +9 -6
  18. package/dist/pipeline/context.js +6 -5
  19. package/dist/pipeline/declarative-flows.js +39 -20
  20. package/dist/pipeline/flow-catalog.js +40 -14
  21. package/dist/pipeline/flow-specs/auto-common-guided.json +313 -0
  22. package/dist/pipeline/flow-specs/auto-common.json +4 -1
  23. package/dist/pipeline/flow-specs/auto-golang.json +27 -1
  24. package/dist/pipeline/flow-specs/design-review/design-review-loop.json +15 -1
  25. package/dist/pipeline/flow-specs/design-review.json +2 -0
  26. package/dist/pipeline/flow-specs/implement.json +3 -1
  27. package/dist/pipeline/flow-specs/plan.json +8 -2
  28. package/dist/pipeline/flow-specs/playbook-init.json +199 -0
  29. package/dist/pipeline/flow-specs/review/review-fix.json +3 -1
  30. package/dist/pipeline/flow-specs/review/review-loop.json +4 -0
  31. package/dist/pipeline/flow-specs/review/review.json +2 -0
  32. package/dist/pipeline/launch-profile-config.js +30 -18
  33. package/dist/pipeline/node-contract.js +1 -0
  34. package/dist/pipeline/node-registry.js +119 -5
  35. package/dist/pipeline/nodes/flow-run-node.js +200 -173
  36. package/dist/pipeline/nodes/llm-prompt-node.js +15 -33
  37. package/dist/pipeline/nodes/playbook-ensure-node.js +115 -0
  38. package/dist/pipeline/nodes/playbook-inventory-node.js +51 -0
  39. package/dist/pipeline/nodes/playbook-questions-form-node.js +166 -0
  40. package/dist/pipeline/nodes/playbook-write-node.js +243 -0
  41. package/dist/pipeline/nodes/project-guidance-node.js +69 -0
  42. package/dist/pipeline/plugin-loader.js +389 -0
  43. package/dist/pipeline/plugin-types.js +1 -0
  44. package/dist/pipeline/prompt-registry.js +4 -1
  45. package/dist/pipeline/prompt-runtime.js +6 -2
  46. package/dist/pipeline/registry.js +71 -4
  47. package/dist/pipeline/spec-compiler.js +1 -0
  48. package/dist/pipeline/spec-loader.js +14 -0
  49. package/dist/pipeline/spec-types.js +19 -0
  50. package/dist/pipeline/spec-validator.js +6 -0
  51. package/dist/pipeline/value-resolver.js +41 -2
  52. package/dist/playbook/practice-candidates.js +12 -0
  53. package/dist/playbook/repo-inventory.js +208 -0
  54. package/dist/plugin-sdk.js +1 -0
  55. package/dist/prompts.js +31 -0
  56. package/dist/runtime/artifact-registry.js +3 -0
  57. package/dist/runtime/execution-routing.js +25 -19
  58. package/dist/runtime/interactive-execution-routing.js +66 -57
  59. package/dist/runtime/playbook.js +485 -0
  60. package/dist/runtime/project-guidance.js +339 -0
  61. package/dist/structured-artifact-schema-registry.js +8 -0
  62. package/dist/structured-artifact-schemas.json +235 -0
  63. package/dist/structured-artifacts.js +7 -1
  64. package/docs/declarative-workflows.md +565 -0
  65. package/docs/example/.flows/examples/claude-example.json +50 -0
  66. package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
  67. package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
  68. package/docs/examples/.flows/claude-example.json +50 -0
  69. package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
  70. package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
  71. package/docs/features.md +77 -0
  72. package/docs/playbook.md +327 -0
  73. package/docs/plugin-sdk.md +731 -0
  74. package/package.json +13 -4
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { flowRoutingGroups, flowRoutingKey } from "../pipeline/flow-catalog.js";
3
3
  import { loadNamedDeclarativeFlow } from "../pipeline/declarative-flows.js";
4
+ import { createPipelineRegistryContext } from "../pipeline/plugin-loader.js";
4
5
  import { EXECUTION_ROUTING_GROUPS, } from "../pipeline/execution-routing-config.js";
5
6
  import { DEFAULT_LAUNCH_PROFILE, defaultModelForExecutor, isAllowedModelForExecutor, } from "../pipeline/launch-profile-config.js";
6
7
  import { FlowInterruptedError, TaskRunnerError } from "../errors.js";
@@ -58,15 +59,9 @@ function executionPresetSelectionForm(flowEntry, flowDefaultAvailable, lastUsedA
58
59
  ],
59
60
  };
60
61
  }
61
- function executorFieldOptions() {
62
- return [
63
- { value: "codex", label: "codex" },
64
- { value: "opencode", label: "opencode" },
65
- ];
66
- }
67
- function selectedExecutorFromValues(values, fieldId, fallbackExecutor) {
68
- const candidate = values[fieldId];
69
- return candidate === "codex" || candidate === "opencode" ? candidate : fallbackExecutor;
62
+ function selectedExecutorFromValues(values, fieldId, fallbackExecutor, registryContext) {
63
+ const candidate = typeof values[fieldId] === "string" ? values[fieldId] : "";
64
+ return registryContext.executors.getRouting(candidate)?.kind === "llm" ? candidate : fallbackExecutor;
70
65
  }
71
66
  function routingEditorDraftFromRouting(routing) {
72
67
  return {
@@ -83,7 +78,11 @@ function routingEditorDraftFromRouting(routing) {
83
78
  ])),
84
79
  };
85
80
  }
86
- function advancedRoutingEditorForm(activeGroups, draft, validationMessage) {
81
+ function advancedRoutingEditorForm(activeGroups, draft, registryContext, validationMessage) {
82
+ const executorOptions = registryContext.executors.llmExecutors().map((entry) => ({
83
+ value: entry.id,
84
+ label: entry.id,
85
+ }));
87
86
  const fields = [
88
87
  {
89
88
  id: "default_route_executor",
@@ -91,7 +90,7 @@ function advancedRoutingEditorForm(activeGroups, draft, validationMessage) {
91
90
  label: "Default route executor",
92
91
  required: true,
93
92
  default: draft.defaultRoute.executor,
94
- options: executorFieldOptions(),
93
+ options: executorOptions,
95
94
  },
96
95
  {
97
96
  id: "default_route_model",
@@ -100,8 +99,8 @@ function advancedRoutingEditorForm(activeGroups, draft, validationMessage) {
100
99
  help: "Available models follow the selected default executor.",
101
100
  required: true,
102
101
  default: draft.defaultRoute.model,
103
- options: modelOptionsForExecutor(draft.defaultRoute.executor),
104
- optionsFromValues: (values) => modelOptionsForExecutor(selectedExecutorFromValues(values, "default_route_executor", draft.defaultRoute.executor)),
102
+ options: modelOptionsForExecutor(draft.defaultRoute.executor, registryContext.executors),
103
+ optionsFromValues: (values) => modelOptionsForExecutor(selectedExecutorFromValues(values, "default_route_executor", draft.defaultRoute.executor, registryContext), registryContext.executors),
105
104
  },
106
105
  ...activeGroups.flatMap((group) => {
107
106
  const route = draft.groups[group];
@@ -112,7 +111,7 @@ function advancedRoutingEditorForm(activeGroups, draft, validationMessage) {
112
111
  label: `${routingGroupLabel(group)} executor`,
113
112
  required: true,
114
113
  default: route.executor,
115
- options: executorFieldOptions(),
114
+ options: executorOptions,
116
115
  },
117
116
  {
118
117
  id: `${group}_model`,
@@ -121,8 +120,8 @@ function advancedRoutingEditorForm(activeGroups, draft, validationMessage) {
121
120
  help: `Available models follow the selected ${routingGroupLabel(group)} executor.`,
122
121
  required: true,
123
122
  default: route.model,
124
- options: modelOptionsForExecutor(route.executor),
125
- optionsFromValues: (values) => modelOptionsForExecutor(selectedExecutorFromValues(values, `${group}_executor`, route.executor)),
123
+ options: modelOptionsForExecutor(route.executor, registryContext.executors),
124
+ optionsFromValues: (values) => modelOptionsForExecutor(selectedExecutorFromValues(values, `${group}_executor`, route.executor, registryContext), registryContext.executors),
126
125
  },
127
126
  ];
128
127
  }),
@@ -202,8 +201,8 @@ function presetNameForm() {
202
201
  ],
203
202
  };
204
203
  }
205
- function normalizeEditableRoute(label, route) {
206
- if (isAllowedModelForExecutor(route.executor, route.model)) {
204
+ function normalizeEditableRoute(label, route, executors) {
205
+ if (isAllowedModelForExecutor(route.executor, route.model, executors)) {
207
206
  return {
208
207
  route: { ...route },
209
208
  validationErrors: [],
@@ -212,20 +211,20 @@ function normalizeEditableRoute(label, route) {
212
211
  return {
213
212
  route: {
214
213
  executor: route.executor,
215
- model: defaultModelForExecutor(route.executor),
214
+ model: defaultModelForExecutor(route.executor, executors),
216
215
  },
217
216
  validationErrors: [
218
217
  `${label} model '${route.model}' is not allowed for executor '${route.executor}'. Select a ${route.executor} model.`,
219
218
  ],
220
219
  };
221
220
  }
222
- function normalizeEditableRoutingDraft(draft) {
223
- const defaultRoute = normalizeEditableRoute("Default route", draft.defaultRoute);
221
+ function normalizeEditableRoutingDraft(draft, executors) {
222
+ const defaultRoute = normalizeEditableRoute("Default route", draft.defaultRoute, executors);
224
223
  const groups = {};
225
224
  const currentRunOverrides = {};
226
225
  const validationErrors = [...defaultRoute.validationErrors];
227
226
  for (const group of EXECUTION_ROUTING_GROUPS) {
228
- const normalizedRoute = normalizeEditableRoute(routingGroupLabel(group), draft.groups[group]);
227
+ const normalizedRoute = normalizeEditableRoute(routingGroupLabel(group), draft.groups[group], executors);
229
228
  groups[group] = normalizedRoute.route;
230
229
  if (normalizedRoute.route.executor !== defaultRoute.route.executor
231
230
  || normalizedRoute.route.model !== defaultRoute.route.model) {
@@ -274,40 +273,47 @@ function formatAsciiTable(headers, rows, maxWidths) {
274
273
  const separator = `|-${widths.map((width) => "-".repeat(width)).join("-|-")}-|`;
275
274
  return [formatRow(headers), separator, ...rows.map((row) => formatRow(row))];
276
275
  }
277
- function collectEffectiveRoutedStepRows(flow, cwd, routing, prefixSegments = [], ancestry = []) {
276
+ function collectEffectiveRoutedStepRows(flow, cwd, routing, registryContext, prefixSegments = [], ancestry = []) {
278
277
  if (ancestry.includes(flow.absolutePath)) {
279
- return [];
278
+ return Promise.resolve([]);
280
279
  }
281
280
  const nextAncestry = [...ancestry, flow.absolutePath];
282
281
  const rows = [];
283
- for (const phase of flow.phases) {
284
- for (const step of phase.steps) {
285
- const stepRef = `${phase.id}.${step.id}`;
286
- if (step.routingGroup) {
287
- const route = routing.groups[step.routingGroup];
288
- rows.push({
289
- step: [...prefixSegments, stepRef].join(" > "),
290
- group: routingGroupLabel(step.routingGroup),
291
- executor: route.executor,
292
- model: route.model,
282
+ const run = async () => {
283
+ for (const phase of flow.phases) {
284
+ for (const step of phase.steps) {
285
+ const stepRef = `${phase.id}.${step.id}`;
286
+ if (step.routingGroup) {
287
+ const route = routing.groups[step.routingGroup];
288
+ rows.push({
289
+ step: [...prefixSegments, stepRef].join(" > "),
290
+ group: routingGroupLabel(step.routingGroup),
291
+ executor: route.executor,
292
+ model: route.model,
293
+ });
294
+ }
295
+ if (step.node !== "flow-run") {
296
+ continue;
297
+ }
298
+ const nestedFlowName = step.params?.fileName;
299
+ if (!nestedFlowName || !("const" in nestedFlowName) || typeof nestedFlowName.const !== "string") {
300
+ continue;
301
+ }
302
+ const nestedFlow = await loadNamedDeclarativeFlow(nestedFlowName.const, cwd, {
303
+ ...(registryContext ? { registryContext } : {}),
293
304
  });
305
+ const nestedFlowLabel = path.basename(nestedFlow.fileName, path.extname(nestedFlow.fileName));
306
+ rows.push(...await collectEffectiveRoutedStepRows(nestedFlow, cwd, routing, registryContext, [...prefixSegments, stepRef, nestedFlowLabel], nextAncestry));
294
307
  }
295
- if (step.node !== "flow-run") {
296
- continue;
297
- }
298
- const nestedFlowName = step.params?.fileName;
299
- if (!nestedFlowName || !("const" in nestedFlowName) || typeof nestedFlowName.const !== "string") {
300
- continue;
301
- }
302
- const nestedFlow = loadNamedDeclarativeFlow(nestedFlowName.const, cwd);
303
- const nestedFlowLabel = path.basename(nestedFlow.fileName, path.extname(nestedFlow.fileName));
304
- rows.push(...collectEffectiveRoutedStepRows(nestedFlow, cwd, routing, [...prefixSegments, stepRef, nestedFlowLabel], nextAncestry));
305
308
  }
306
- }
307
- return rows;
309
+ return rows;
310
+ };
311
+ return run();
308
312
  }
309
- export function describeEffectiveRoutingPreview(flowEntry, routing, cwd) {
310
- const previewGroups = flowRoutingGroups(flowEntry, cwd);
313
+ export async function describeEffectiveRoutingPreview(flowEntry, routing, cwd, registryContext) {
314
+ const previewGroups = await flowRoutingGroups(flowEntry, cwd, {
315
+ ...(registryContext ? { registryContext } : {}),
316
+ });
311
317
  const summaryRows = [
312
318
  ["Default", routing.defaultRoute.executor, routing.defaultRoute.model],
313
319
  ...previewGroups.map((group) => [
@@ -320,7 +326,7 @@ export function describeEffectiveRoutingPreview(flowEntry, routing, cwd) {
320
326
  "Effective routes:",
321
327
  ...formatAsciiTable(["Scope", "Executor", "Model"], summaryRows, [18, 12, 36]),
322
328
  ];
323
- const routedSteps = collapseRepeatedEffectiveRoutedStepRows(collectEffectiveRoutedStepRows(flowEntry.flow, cwd, routing));
329
+ const routedSteps = collapseRepeatedEffectiveRoutedStepRows(await collectEffectiveRoutedStepRows(flowEntry.flow, cwd, routing, registryContext));
324
330
  if (routedSteps.length === 0) {
325
331
  return lines.join("\n");
326
332
  }
@@ -332,7 +338,8 @@ export function describeEffectiveRoutingPreview(flowEntry, routing, cwd) {
332
338
  ].join("\n");
333
339
  }
334
340
  export async function requestInteractiveExecutionRouting(flowEntry, requestUserInput) {
335
- const previewGroups = flowRoutingGroups(flowEntry, process.cwd());
341
+ const registryContext = await createPipelineRegistryContext(process.cwd());
342
+ const previewGroups = await flowRoutingGroups(flowEntry, process.cwd(), { registryContext });
336
343
  const flowKey = flowRoutingKey(flowEntry);
337
344
  const namedPresets = getNamedExecutionPresets();
338
345
  const flowDefault = getFlowDefaultExecutionRouting(flowKey);
@@ -346,14 +353,14 @@ export async function requestInteractiveExecutionRouting(flowEntry, requestUserI
346
353
  throw new TaskRunnerError("Flow default routing is unavailable.");
347
354
  }
348
355
  selectedPreset = { kind: "flow-default", label: "Flow default" };
349
- routing = resolveStoredExecutionRoutingSnapshot(flowDefault.routing);
356
+ routing = resolveStoredExecutionRoutingSnapshot(flowDefault.routing, registryContext.executors);
350
357
  }
351
358
  else if (selectedPresetValue === "last-used") {
352
359
  if (!lastUsed) {
353
360
  throw new TaskRunnerError("Last-used routing is unavailable.");
354
361
  }
355
362
  selectedPreset = { kind: "last-used", label: "Last used" };
356
- routing = resolveStoredExecutionRoutingSnapshot(lastUsed.routing);
363
+ routing = resolveStoredExecutionRoutingSnapshot(lastUsed.routing, registryContext.executors);
357
364
  }
358
365
  else if (selectedPresetValue.startsWith("named:")) {
359
366
  const presetName = selectedPresetValue.slice("named:".length);
@@ -362,7 +369,7 @@ export async function requestInteractiveExecutionRouting(flowEntry, requestUserI
362
369
  throw new TaskRunnerError(`Named preset '${presetName}' is unavailable.`);
363
370
  }
364
371
  selectedPreset = { kind: "named", presetId: presetName, label: `Named preset: ${presetName}` };
365
- routing = resolveStoredExecutionRoutingSnapshot(namedPreset.routing);
372
+ routing = resolveStoredExecutionRoutingSnapshot(namedPreset.routing, registryContext.executors);
366
373
  }
367
374
  else if (selectedPresetValue.startsWith("built-in:")) {
368
375
  const presetId = selectedPresetValue.slice("built-in:".length);
@@ -371,11 +378,12 @@ export async function requestInteractiveExecutionRouting(flowEntry, requestUserI
371
378
  throw new TaskRunnerError(`Unknown execution preset '${presetId}'.`);
372
379
  }
373
380
  selectedPreset = { kind: "built-in", presetId, label: preset.label };
374
- routing = resolveExecutionRouting({ presetId });
381
+ routing = resolveExecutionRouting({ presetId, executors: registryContext.executors });
375
382
  }
376
383
  else {
377
384
  selectedPreset = { kind: "custom", label: "Custom" };
378
385
  routing = resolveExecutionRouting({
386
+ executors: registryContext.executors,
379
387
  defaultRoute: {
380
388
  executor: DEFAULT_LAUNCH_PROFILE.executor,
381
389
  model: DEFAULT_LAUNCH_PROFILE.model,
@@ -385,7 +393,7 @@ export async function requestInteractiveExecutionRouting(flowEntry, requestUserI
385
393
  let editorDraft = routingEditorDraftFromRouting(routing);
386
394
  let editorValidationMessage;
387
395
  for (;;) {
388
- const previewText = `Preset: ${selectedPreset.label}\n${describeEffectiveRoutingPreview(flowEntry, routing, process.cwd())}`;
396
+ const previewText = `Preset: ${selectedPreset.label}\n${await describeEffectiveRoutingPreview(flowEntry, routing, process.cwd(), registryContext)}`;
389
397
  const actionResult = await requestUserInput(routingActionForm(previewText));
390
398
  const action = String(actionResult.values.action ?? "start");
391
399
  if (action === "cancel") {
@@ -395,7 +403,7 @@ export async function requestInteractiveExecutionRouting(flowEntry, requestUserI
395
403
  saveLastUsedExecutionRouting(flowKey, routing, selectedPreset);
396
404
  return { routing, selectedPreset };
397
405
  }
398
- const routingFormResult = await requestUserInput(advancedRoutingEditorForm(previewGroups, editorDraft, editorValidationMessage));
406
+ const routingFormResult = await requestUserInput(advancedRoutingEditorForm(previewGroups, editorDraft, registryContext, editorValidationMessage));
399
407
  const requestedDefaultRoute = {
400
408
  executor: String(routingFormResult.values.default_route_executor ?? editorDraft.defaultRoute.executor),
401
409
  model: String(routingFormResult.values.default_route_model ?? editorDraft.defaultRoute.model),
@@ -418,7 +426,7 @@ export async function requestInteractiveExecutionRouting(flowEntry, requestUserI
418
426
  ];
419
427
  })),
420
428
  };
421
- const normalizedDraft = normalizeEditableRoutingDraft(requestedDraft);
429
+ const normalizedDraft = normalizeEditableRoutingDraft(requestedDraft, registryContext.executors);
422
430
  editorDraft = normalizedDraft.draft;
423
431
  if (normalizedDraft.validationErrors.length > 0) {
424
432
  editorValidationMessage = normalizedDraft.validationErrors.join("\n");
@@ -426,6 +434,7 @@ export async function requestInteractiveExecutionRouting(flowEntry, requestUserI
426
434
  }
427
435
  try {
428
436
  routing = resolveExecutionRouting({
437
+ executors: registryContext.executors,
429
438
  defaultRoute: {
430
439
  executor: normalizedDraft.draft.defaultRoute.executor,
431
440
  model: normalizedDraft.draft.defaultRoute.model,