@automatalabs/workflows 0.30.1 → 0.32.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.
- package/README.md +62 -17
- package/dist/cli.js +7 -3
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -23
- package/dist/isolation.d.ts +24 -0
- package/dist/isolation.d.ts.map +1 -0
- package/dist/isolation.js +54 -0
- package/dist/script-backends.d.ts +7 -0
- package/dist/script-backends.d.ts.map +1 -0
- package/dist/script-backends.js +20 -0
- package/dist/validate-internal.d.ts +12 -0
- package/dist/validate-internal.d.ts.map +1 -0
- package/dist/validate-internal.js +13 -0
- package/dist/validate.d.ts +19 -6
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +165 -25
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -15,10 +15,10 @@ If you want to expose a `workflow` tool to an MCP host (Claude Code, Zed, …),
|
|
|
15
15
|
you want to embed the runner in your own program, use this one.
|
|
16
16
|
|
|
17
17
|
It is a **programmatic library**, not an MCP stdio server. It is a thin facade over the engine +
|
|
18
|
-
ACP packages and adds
|
|
19
|
-
|
|
20
|
-
optional StructuredOutput tool for eligible agents; consumers still interact
|
|
21
|
-
workflow/runner APIs rather than MCP server schemas.
|
|
18
|
+
ACP packages and adds ACP-defaulted helpers for ordinary runs (`runDynamicWorkflow`) and
|
|
19
|
+
substitution tests (`runIsolation`). The ACP layer does use `@modelcontextprotocol/sdk` internally
|
|
20
|
+
when it hosts the optional StructuredOutput tool for eligible agents; consumers still interact
|
|
21
|
+
through this SDK's workflow/runner APIs rather than MCP server schemas.
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -116,6 +116,27 @@ Every script **must** begin with `export const meta = { name, description, phase
|
|
|
116
116
|
statement, and must be **deterministic** — `Date.now()`, `Math.random()`, and `new Date()` are
|
|
117
117
|
unavailable inside the realm (they would break journal replay on resume).
|
|
118
118
|
|
|
119
|
+
### Substitution testing (isolation mode)
|
|
120
|
+
|
|
121
|
+
Record a normal managed run, then re-run its recorded script with one selected step live while all
|
|
122
|
+
other calls are served from the recording:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { runIsolation } from "@automatalabs/workflows";
|
|
126
|
+
|
|
127
|
+
const isolated = await runIsolation({
|
|
128
|
+
baselineRunId: recorded.runId,
|
|
129
|
+
live: [{ label: "step-2", model: "codex/gpt-5.3-codex" }],
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const target = isolated.report.calls.find((call) => call.mode === "live-target");
|
|
133
|
+
console.log(isolated.status, target?.recordedUsage, target?.liveUsage);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The SDK defaults the live runner to ACP and disposes it; pass `runner` to inject and retain your own.
|
|
137
|
+
See the [Isolation mode API](../../docs/api.md#isolation-mode) for baseline admissibility, target
|
|
138
|
+
selection, typed refusals, report semantics, and the lower-level `createReplayRunner` composition.
|
|
139
|
+
|
|
119
140
|
### b) `createAcpRunner().run(...)` — drive a single agent
|
|
120
141
|
|
|
121
142
|
Skip the script realm entirely and call one agent directly. The runner is the default ACP
|
|
@@ -143,7 +164,7 @@ try {
|
|
|
143
164
|
}
|
|
144
165
|
```
|
|
145
166
|
|
|
146
|
-
`run(prompt, options?)` accepts the seam's `RunOptions`: `schema`, `maxSchemaRetries`, `model`, `mode`, `tier`, `cwd`,
|
|
167
|
+
`run(prompt, options?)` accepts the seam's `RunOptions`: `schema`, `maxSchemaRetries`, `model`, `mode`, `configOptions`, `tier`, `cwd`,
|
|
147
168
|
`instructions`, `label`, `toolNames` / `disallowedToolNames`, `signal`, `mcpServers`, `images`,
|
|
148
169
|
`backends`, `meta` / `promptMeta` (generic ACP `_meta` passthroughs merged into `session/new` /
|
|
149
170
|
`session/prompt`), `baseInstructions` / `developerInstructions` (Codex-only), `keepSession`, and
|
|
@@ -467,23 +488,30 @@ promises — `parallel([() => agent("a"), () => agent("b")])`, not `parallel([ag
|
|
|
467
488
|
|
|
468
489
|
## Validating scripts — `agentprism-workflows validate`
|
|
469
490
|
|
|
470
|
-
The package ships a bin that validates a workflow script **without spending tokens
|
|
471
|
-
any agent process** — no backend auth needed:
|
|
491
|
+
The package ships a bin that validates a workflow script **without spending tokens**:
|
|
472
492
|
|
|
473
493
|
```bash
|
|
474
494
|
npx @automatalabs/workflows validate my-workflow.js --args '{"target":"src/"}'
|
|
475
495
|
```
|
|
476
496
|
|
|
477
|
-
|
|
497
|
+
Three passes: a **static parse** (the `meta` literal, syntax, the determinism blocklist), then a
|
|
478
498
|
**dry run** — the script executes in the real engine realm while every `agent()` call is served
|
|
479
499
|
by an in-process mock `AgentRunner` that fabricates schema-conforming results. The dry run
|
|
480
500
|
catches what a parse can't: thunk-vs-promise mistakes, reference errors, broken plumbing between
|
|
481
|
-
calls.
|
|
501
|
+
calls. Finally, validation opens one no-prompt session on every distinct routed ACP harness,
|
|
502
|
+
surfaces the complete advertised config-option catalog, and checks every authored `configOptions`
|
|
503
|
+
bag against it. This probe uses zero tokens. A harness that cannot spawn, authenticate, or open a
|
|
504
|
+
session contributes one warning and `probed:false`; only that harness's option checks are skipped,
|
|
505
|
+
so probe failure alone never invalidates the script. There is no cached catalog or opt-out flag.
|
|
506
|
+
A mock live confirm answers checkpoints with `default ?? true`, so `headless: "pause"`
|
|
482
507
|
dry-runs cleanly; `headless: "abort"` warns because a truly unattended run would abort.
|
|
483
508
|
Script-declared `meta.backends` are treated as approved (with a warning that real runs require
|
|
484
|
-
approval). The report lists every agent call with
|
|
485
|
-
|
|
486
|
-
|
|
509
|
+
approval). The report lists every agent call with its backend attribution and `configOptions`
|
|
510
|
+
echo, every checkpoint, the full option table for every routed harness (even when no call authors
|
|
511
|
+
options), and warnings. Unknown ids, invalid select values, non-boolean boolean values, and the
|
|
512
|
+
reserved `"model"` id make the report invalid with exit code `2`; each diagnostic names the call,
|
|
513
|
+
authored value, and advertised alternatives. Exit codes are `0` valid, `1` parse failure, `2`
|
|
514
|
+
dry-run or config-option failure, `3` usage error.
|
|
487
515
|
|
|
488
516
|
Flags: `--args <json>` / `--args-file <path>`, `--workflows-dir <dir>` (repeatable — validate by
|
|
489
517
|
NAME and resolve nested `workflow("<name>")` calls from your folder), `--parse-only`,
|
|
@@ -537,6 +565,8 @@ const mockAnswers: MockAnswers = {
|
|
|
537
565
|
const report = await validateWorkflowScript(script, { args: { target: "src/" }, mockAnswers });
|
|
538
566
|
report.ok; // parse ok AND dry run completed
|
|
539
567
|
report.dryRun?.agentCalls; // calls include mockAnswer: { glob, sequenceIndex?, sequenceLength? }
|
|
568
|
+
report.dryRun?.harnessOptions;
|
|
569
|
+
// [{ backendId, probed, options?: SessionConfigOption[], error?: string }]
|
|
540
570
|
report.dryRun?.mockAnswers;// normalized rule counters + item-level unused records
|
|
541
571
|
report.warnings; // approval reminders, phase mismatches, headless-abort checkpoints, …
|
|
542
572
|
```
|
|
@@ -605,6 +635,14 @@ retry, echo verification, or fallback. Brackets, dots, slashes, and provider-sty
|
|
|
605
635
|
ordinary id characters. A harness error follows the existing agent-error path. Per-backend pool
|
|
606
636
|
size is `AGENTPRISM_ACP_POOL_SIZE` (or `AcpPoolOptions.size`).
|
|
607
637
|
|
|
638
|
+
`agent({ configOptions })` applies the rest of that selected harness's ACP option surface with the
|
|
639
|
+
same verbatim philosophy. Exact ids and string/boolean values are sent in ascending option-id
|
|
640
|
+
order, after model selection and before the prompt; there are no aliases, coercion, defaults,
|
|
641
|
+
catalog matching, or client-side option vocabulary. The `"model"` key is rejected before a session
|
|
642
|
+
opens because the dedicated `model` field is its only channel. Run `validate` and read the routed
|
|
643
|
+
harness's advertised-options table before choosing ids or select values. A live harness rejection
|
|
644
|
+
otherwise follows the existing agent-error path.
|
|
645
|
+
|
|
608
646
|
---
|
|
609
647
|
|
|
610
648
|
## Exports
|
|
@@ -612,9 +650,11 @@ size is `AGENTPRISM_ACP_POOL_SIZE` (or `AcpPoolOptions.size`).
|
|
|
612
650
|
```ts
|
|
613
651
|
// ── Run entry & helper ──
|
|
614
652
|
runDynamicWorkflow, // (script, { args?, runner?, exec? }) => Promise<WorkflowRunResult>
|
|
653
|
+
runIsolation, // ACP-defaulted single-target substitution over a recorded run
|
|
654
|
+
createReplayRunner, // backend-neutral in-memory replay composition primitive
|
|
615
655
|
runWorkflow, // the bare engine run (no status trio)
|
|
616
656
|
parseWorkflowScript, // parse a script's meta + body
|
|
617
|
-
validateWorkflowScript, // token-free parse + mock
|
|
657
|
+
validateWorkflowScript, // token-free parse + mock dry run + no-prompt harness option probes
|
|
618
658
|
fabricateFromSchema, // the dry run's JSON-Schema value fabricator
|
|
619
659
|
formatValidateReport, // render a ValidateWorkflowReport as CLI text
|
|
620
660
|
openWorkflowDir, // read-only view over folders of workflow scripts (name = filename stem)
|
|
@@ -639,16 +679,21 @@ WorkflowError, WorkflowErrorCode, isWorkflowError, isProviderUsageLimit, isAuthR
|
|
|
639
679
|
AGENTPRISM_PERSISTENCE_ROOT_ENV,
|
|
640
680
|
|
|
641
681
|
// ── Types ──
|
|
642
|
-
RunDynamicWorkflowOptions,
|
|
682
|
+
RunDynamicWorkflowOptions, RunIsolationSdkOptions, RunIsolationOptions, IsolationRunResult,
|
|
683
|
+
IsolationTarget, ReplayRunnerOptions, ReplayRunner, ReplayObservation, ReplayReport,
|
|
684
|
+
ReplayCallReport, ReplayDivergenceEvent, ResolvedIsolationTarget,
|
|
685
|
+
WorkflowRunOptions, AgentOptions, ExecOptions, CheckpointCallContext,
|
|
643
686
|
MockAnswerJson, MockAnswerSequence, MockAnswerRule, MockAnswers,
|
|
644
687
|
ValidatedMockAnswerUse, ValidatedMockAnswerRule, UnusedMockAnswer, ValidatedMockAnswers,
|
|
645
|
-
ValidateWorkflowOptions, ValidateWorkflowReport,
|
|
688
|
+
ValidateWorkflowOptions, ValidateWorkflowReport, ValidateHarnessOptions,
|
|
689
|
+
ValidatedAgentCall, ValidatedCheckpoint,
|
|
646
690
|
WorkflowManagerOptions, CheckpointOptions, WorkflowRunResult, WorkflowRunFallback,
|
|
647
691
|
WorkflowCheckpointTaken, WorkflowCheckpointSource, WorkflowSnapshot,
|
|
648
692
|
WorkflowPathOptions, RunPersistence, RunPersistenceOptions,
|
|
649
693
|
AcpPoolOptions, AcpRunnerOptions, AgentRunner, RunOptions, AgentResult, AgentUsage, JournalEntry,
|
|
650
|
-
AgentSessionRef, AgentSessionRecord, WorkflowBackendConfig,
|
|
651
|
-
InteractiveSessionOptions, InteractiveTurn,
|
|
694
|
+
AgentSessionRef, AgentSessionRecord, WorkflowBackendConfig, WorkflowCallRecord, WorkflowRecordedError,
|
|
695
|
+
InteractiveSessionOptions, InteractiveTurn, ProbedConfigOptions, SessionConfigOption,
|
|
696
|
+
PermissionResolver,
|
|
652
697
|
AuthResolver, AuthContext, AuthResolution, AuthMethodDescriptor, AuthCapableRunner,
|
|
653
698
|
ProviderCapableRunner, // duck-type gate for the MCP provider tools (providers/list|set|disable)
|
|
654
699
|
ClientHandlers, FsHandlers, TerminalHandlers, McpHandlers, AcpSessionContext, NegotiatedCapabilities,
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
//
|
|
6
6
|
// Validates a workflow script without spending tokens: static parse (meta literal,
|
|
7
7
|
// syntax, determinism blocklist), then a dry run over an in-process mock AgentRunner
|
|
8
|
-
// that fabricates schema-conforming results
|
|
8
|
+
// that fabricates schema-conforming results, then one no-prompt option probe per routed
|
|
9
|
+
// ACP harness. See
|
|
9
10
|
// ./validate.ts for the programmatic API (`validateWorkflowScript`).
|
|
10
11
|
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
11
12
|
import { resolve } from "node:path";
|
|
@@ -16,8 +17,11 @@ const USAGE = `Usage: agentprism-workflows validate <workflow-file-or-name> [opt
|
|
|
16
17
|
Validates an AgentPrism workflow script without spending tokens:
|
|
17
18
|
1. static parse — the meta literal, syntax, and the determinism blocklist
|
|
18
19
|
2. dry run — the script executes against a mock agent backend that fabricates
|
|
19
|
-
schema-conforming results; no
|
|
20
|
-
|
|
20
|
+
schema-conforming results; no tokens are spent, and a mock live confirm
|
|
21
|
+
resolves checkpoints to their declared defaults
|
|
22
|
+
3. config probe — each routed ACP harness opens once with no prompt; advertised
|
|
23
|
+
options are reported and authored configOptions are checked. Probe failures warn
|
|
24
|
+
and skip that harness's checks without making validation fail
|
|
21
25
|
|
|
22
26
|
Options:
|
|
23
27
|
--args <json> the script's \`args\` global for the dry run (a JSON value)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { WorkflowManager as EngineWorkflowManager } from "@automatalabs/workflow-engine";
|
|
2
2
|
import type { AcpEventName, AcpRunnerEventMap } from "@automatalabs/acp-agents";
|
|
3
3
|
import type { ExecOptions, WorkflowDir, WorkflowManagerOptions } from "@automatalabs/workflow-engine";
|
|
4
|
-
import type { AgentRunner,
|
|
4
|
+
import type { AgentRunner, WorkflowRunResult } from "@automatalabs/shared-types";
|
|
5
|
+
import { type ScriptBackendApproval } from "./script-backends.js";
|
|
5
6
|
export { runWorkflow, parseWorkflowScript, redactText, truncateUtf8 } from "@automatalabs/workflow-engine";
|
|
7
|
+
export { runIsolation, createReplayRunner, type RunIsolationSdkOptions } from "./isolation.js";
|
|
8
|
+
export type { RunIsolationOptions, IsolationRunResult, ReplayRunnerOptions, ResolvedIsolationTarget, IsolationTarget, ReplayRunner, ReplayObservation, ReplayReport, ReplayCallReport, ReplayDivergenceEvent, CheckpointCallContext, WorkflowCallRecord, WorkflowRecordedError, } from "./isolation.js";
|
|
6
9
|
export { openWorkflowDir, type WorkflowDir, type WorkflowDirEntry, type OpenWorkflowDirOptions, } from "@automatalabs/workflow-engine";
|
|
7
10
|
export { validateWorkflowScript, fabricateFromSchema, formatValidateReport, MOCK_TOKENS_PER_AGENT } from "./validate.js";
|
|
8
|
-
export type { MockAnswerJson, MockAnswerRule, MockAnswers, MockAnswerSequence, UnusedMockAnswer, ValidateWorkflowOptions, ValidateWorkflowReport, ValidatedAgentCall, ValidatedCheckpoint, ValidatedMockAnswerRule, ValidatedMockAnswers, ValidatedMockAnswerUse, } from "./validate.js";
|
|
11
|
+
export type { MockAnswerJson, MockAnswerRule, MockAnswers, MockAnswerSequence, UnusedMockAnswer, ValidateWorkflowOptions, ValidateWorkflowReport, ValidateHarnessOptions, ValidatedAgentCall, ValidatedCheckpoint, ValidatedMockAnswerRule, ValidatedMockAnswers, ValidatedMockAnswerUse, } from "./validate.js";
|
|
9
12
|
export type { WorkflowRunOptions, AgentOptions, ExecOptions, WorkflowManagerOptions, CheckpointOptions, WorkflowRunResult, WorkflowSnapshot, WorkflowPathOptions, RunPersistence, RunPersistenceOptions, PersistedRunState, PersistedAgentState, WorkflowLogTail, WorkflowRunCallStatus, WorkflowRunInspectionOptions, WorkflowRunStatus, WorkflowRunStatusTruncation, WorkflowRunFallback, WorkflowCheckpointSource, WorkflowCheckpointTaken, } from "@automatalabs/workflow-engine";
|
|
10
13
|
export { AGENTPRISM_PERSISTENCE_ROOT_ENV, WorkflowError, WorkflowErrorCode, isWorkflowError, isProviderUsageLimit, isAuthRequired, } from "@automatalabs/workflow-engine";
|
|
11
14
|
export { createAcpRunner, AcpAgentRunner, InteractiveSession, selectBackend, ClaudeBackend, CodexBackend, CustomAcpBackend, AGENT_METHODS, CLIENT_METHODS, AGENT_METHOD_COVERAGE, CLIENT_METHOD_COVERAGE, ACP_AUTH_REQUIRED_ERROR_CODE, clientCapabilitiesFor, adaptPromptContent, resolveBackendRegistry, BACKENDS_ENV, toJsonSchema, toStrictJsonSchema, } from "@automatalabs/acp-agents";
|
|
12
|
-
export type { AcpPoolOptions, AcpRunnerOptions, AuthenticateOptions, AuthMethodsOptions, DisableProviderOptions, DeleteSessionOptions, InteractiveSessionOptions, InteractiveTurn, ListProvidersOptions, ListSessionsOptions, LogoutOptions, ReattachSessionOptions, SetProviderOptions, BackendRegistry, CustomBackendConfig, RegisteredBackend, ClientCapabilityOptions, ClientHandlers, FsHandlers, McpHandlers, TerminalHandlers, AcpSessionContext, NegotiatedCapabilities, PermissionResolver, AgentAuthCapabilities, AgentRequestMethod, AgentRequestParamsByMethod, AgentRequestResponsesByMethod, AuthCapabilities, AuthEnvVar, AuthenticateRequest, AuthenticateResponse, AuthMethod, AuthMethodAgent, AuthMethodEnvVar, AuthMethodId, AuthMethodTerminal, ConnectMcpRequest, ConnectMcpResponse, DeleteSessionRequest, DeleteSessionResponse, DisableProviderRequest, DisableProviderResponse, DisconnectMcpRequest, DisconnectMcpResponse, ListProvidersRequest, ListProvidersResponse, ListSessionsRequest, ListSessionsResponse, LlmProtocol, LoadSessionRequest, LoadSessionResponse, LogoutCapabilities, LogoutRequest, LogoutResponse, McpConnectionId, McpServerAcp, McpServerAcpId, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse, ProviderCurrentConfig, ProviderId, ProviderInfo, ProvidersCapabilities, ResumeSessionRequest, ResumeSessionResponse, SetProviderRequest, SetProviderResponse, AgentNotificationMethod, AgentNotificationParamsByMethod, CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse, ElicitationAcceptAction, ElicitationCapabilities, ElicitationContentValue, ElicitationFormCapabilities, ElicitationFormMode, ElicitationId, ElicitationPropertySchema, ElicitationRequestScope, ElicitationResolver, ElicitationSchema, ElicitationSchemaType, ElicitationSessionScope, ElicitationUrlCapabilities, ElicitationUrlMode, SessionMode, SessionModeState, SessionInfo, SendRequestOptions, AgentMethodCoverage, ClientMethodCoverage, } from "@automatalabs/acp-agents";
|
|
15
|
+
export type { AcpPoolOptions, AcpRunnerOptions, AuthenticateOptions, AuthMethodsOptions, DisableProviderOptions, DeleteSessionOptions, InteractiveSessionOptions, InteractiveTurn, ListProvidersOptions, ListSessionsOptions, LogoutOptions, ProbedConfigOptions, ReattachSessionOptions, SetProviderOptions, BackendRegistry, CustomBackendConfig, RegisteredBackend, ClientCapabilityOptions, ClientHandlers, FsHandlers, McpHandlers, TerminalHandlers, AcpSessionContext, NegotiatedCapabilities, PermissionResolver, AgentAuthCapabilities, AgentRequestMethod, AgentRequestParamsByMethod, AgentRequestResponsesByMethod, AuthCapabilities, AuthEnvVar, AuthenticateRequest, AuthenticateResponse, AuthMethod, AuthMethodAgent, AuthMethodEnvVar, AuthMethodId, AuthMethodTerminal, ConnectMcpRequest, ConnectMcpResponse, DeleteSessionRequest, DeleteSessionResponse, DisableProviderRequest, DisableProviderResponse, DisconnectMcpRequest, DisconnectMcpResponse, ListProvidersRequest, ListProvidersResponse, ListSessionsRequest, ListSessionsResponse, LlmProtocol, LoadSessionRequest, LoadSessionResponse, LogoutCapabilities, LogoutRequest, LogoutResponse, McpConnectionId, McpServerAcp, McpServerAcpId, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse, ProviderCurrentConfig, ProviderId, ProviderInfo, ProvidersCapabilities, ResumeSessionRequest, ResumeSessionResponse, SetProviderRequest, SetProviderResponse, AgentNotificationMethod, AgentNotificationParamsByMethod, CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse, ElicitationAcceptAction, ElicitationCapabilities, ElicitationContentValue, ElicitationFormCapabilities, ElicitationFormMode, ElicitationId, ElicitationPropertySchema, ElicitationRequestScope, ElicitationResolver, ElicitationSchema, ElicitationSchemaType, ElicitationSessionScope, ElicitationUrlCapabilities, ElicitationUrlMode, SessionMode, SessionModeState, SessionConfigOption, SessionInfo, SendRequestOptions, AgentMethodCoverage, ClientMethodCoverage, } from "@automatalabs/acp-agents";
|
|
13
16
|
export type { AuthResolver, AuthContext, AuthResolution, AuthMethodDescriptor, CompleteAuthOptions, AuthOutcome, AuthController, AuthStatusSnapshot, AuthCapableRunner, ProviderCapableRunner, } from "@automatalabs/acp-agents";
|
|
14
17
|
export type { AuthErrorContext, CheckpointContext } from "@automatalabs/shared-types";
|
|
15
18
|
export { TypedEventEmitter } from "@automatalabs/acp-agents";
|
|
@@ -78,9 +81,7 @@ export declare class WorkflowManager extends EngineWorkflowManager {
|
|
|
78
81
|
* runDynamicWorkflow THROWS with guidance rather than running a script whose declared
|
|
79
82
|
* dependencies were dropped.
|
|
80
83
|
*/
|
|
81
|
-
export type ScriptBackendApproval
|
|
82
|
-
name: string;
|
|
83
|
-
} & WorkflowBackendConfig) => boolean | Promise<boolean>);
|
|
84
|
+
export type { ScriptBackendApproval } from "./script-backends.js";
|
|
84
85
|
/** Options for {@link runDynamicWorkflow}. */
|
|
85
86
|
export interface RunDynamicWorkflowOptions {
|
|
86
87
|
/**
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAKL,eAAe,IAAI,qBAAqB,EACzC,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAoB,YAAY,EAAE,iBAAiB,EAAiB,MAAM,0BAA0B,CAAC;AACjH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACtG,OAAO,KAAK,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
// @automatalabs/workflows — the importable SDK for the AgentPrism dynamic-workflow
|
|
3
3
|
// orchestrator. A FACADE re-export barrel: it re-exports the clean public surface of
|
|
4
4
|
// the three engine packages, adds the SDK-level WorkflowManager ACP-event bridge, and
|
|
5
|
-
// adds
|
|
6
|
-
//
|
|
5
|
+
// adds ACP-defaulted convenience helpers for ordinary and isolation runs. It is
|
|
6
|
+
// SEPARATE from @automatalabs/mcp-server (the stdio MCP server)
|
|
7
7
|
// and stays a PURE library — it pulls in neither @modelcontextprotocol/sdk nor zod.
|
|
8
8
|
//
|
|
9
9
|
// The DSL globals available INSIDE a workflow script (agent, parallel, pipeline, …) are
|
|
10
10
|
// vm-realm globals, NOT importable symbols; they are documented for author IntelliSense
|
|
11
11
|
// in ./dsl.d.ts (referenced above), not exported here.
|
|
12
12
|
import { ACP_CROSS_CUTTING_EVENT_NAMES, createAcpRunner } from "@automatalabs/acp-agents";
|
|
13
|
-
import { openWorkflowDir, parseWorkflowScript,
|
|
13
|
+
import { openWorkflowDir, parseWorkflowScript, WorkflowManager as EngineWorkflowManager, } from "@automatalabs/workflow-engine";
|
|
14
|
+
import { approveScriptBackends } from "./script-backends.js";
|
|
14
15
|
// ── Engine: run entry, script parsing, the managed-run lifecycle, and the
|
|
15
16
|
// option/result + error types the host composes against. ──
|
|
16
17
|
export { runWorkflow, parseWorkflowScript, redactText, truncateUtf8 } from "@automatalabs/workflow-engine";
|
|
18
|
+
// ── Isolation mode: deterministic substitution testing over a recorded run. The SDK
|
|
19
|
+
// wrapper defaults the live target runner to ACP and owns that runner's disposal. ──
|
|
20
|
+
export { runIsolation, createReplayRunner } from "./isolation.js";
|
|
17
21
|
// ── Workflow directory view: openWorkflowDir("./workflows") binds a read-only,
|
|
18
22
|
// per-call-fresh view over folders of versioned workflow scripts (name = filename
|
|
19
23
|
// stem). `view.resolve` IS a loadSavedWorkflow resolver; runDynamicWorkflow accepts
|
|
@@ -200,7 +204,10 @@ export async function runDynamicWorkflow(script, opts = {}) {
|
|
|
200
204
|
}
|
|
201
205
|
let exec = opts.exec;
|
|
202
206
|
if (declared && Object.keys(declared).length > 0) {
|
|
203
|
-
exec = {
|
|
207
|
+
exec = {
|
|
208
|
+
...(exec ?? {}),
|
|
209
|
+
scriptBackends: await approveScriptBackends(declared, opts.allowScriptBackends, "runDynamicWorkflow"),
|
|
210
|
+
};
|
|
204
211
|
}
|
|
205
212
|
const owned = opts.runner === undefined;
|
|
206
213
|
const runner = opts.runner ?? createAcpRunner();
|
|
@@ -214,22 +221,3 @@ export async function runDynamicWorkflow(script, opts = {}) {
|
|
|
214
221
|
await runner.dispose();
|
|
215
222
|
}
|
|
216
223
|
}
|
|
217
|
-
/** Resolve the embedder's approval policy over the declared backends; throw with guidance when
|
|
218
|
-
* approval is missing or any backend is declined (an unapproved dependency must abort, never
|
|
219
|
-
* silently reroute). */
|
|
220
|
-
async function approveScriptBackends(declared, approval) {
|
|
221
|
-
const names = Object.keys(declared).join(", ");
|
|
222
|
-
if (approval === undefined || approval === false) {
|
|
223
|
-
throw new WorkflowError(`script declares custom ACP backends (meta.backends: ${names}) — these spawn commands on this machine and require explicit approval. ` +
|
|
224
|
-
`Pass allowScriptBackends: true (or a per-backend approval callback) to runDynamicWorkflow, ` +
|
|
225
|
-
`or thread an approved registry yourself via exec.scriptBackends.`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false });
|
|
226
|
-
}
|
|
227
|
-
if (approval === true)
|
|
228
|
-
return declared;
|
|
229
|
-
for (const [name, config] of Object.entries(declared)) {
|
|
230
|
-
if (!(await approval({ name, ...config }))) {
|
|
231
|
-
throw new WorkflowError(`script backend "${name}" (command: ${config.command}) was declined by the allowScriptBackends callback — aborting the run`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false });
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return declared;
|
|
235
|
-
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createReplayRunner } from "@automatalabs/workflow-engine";
|
|
2
|
+
import type { CheckpointCallContext, IsolationRunResult, IsolationTarget, ReplayCallReport, ReplayDivergenceEvent, ReplayObservation, ReplayReport, ReplayRunner, ReplayRunnerOptions, ResolvedIsolationTarget, RunIsolationOptions } from "@automatalabs/workflow-engine";
|
|
3
|
+
import type { AgentRunner, WorkflowCallRecord, WorkflowRecordedError } from "@automatalabs/shared-types";
|
|
4
|
+
import { type ScriptBackendApproval } from "./script-backends.js";
|
|
5
|
+
type OwnedRunner = AgentRunner & {
|
|
6
|
+
dispose: () => Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
type DefaultRunnerFactory = () => OwnedRunner;
|
|
9
|
+
/** Test-only injection point for the owned default runner. Deliberately absent from the package barrel. */
|
|
10
|
+
export declare function __setDefaultRunnerFactoryForTests(factory: DefaultRunnerFactory | undefined): void;
|
|
11
|
+
export interface RunIsolationSdkOptions extends Omit<RunIsolationOptions, "runner" | "scriptBackends"> {
|
|
12
|
+
/** Omitted => createAcpRunner(), disposed after the run. */
|
|
13
|
+
runner?: AgentRunner;
|
|
14
|
+
/** Approval policy for the recording script's declared meta.backends. */
|
|
15
|
+
allowScriptBackends?: ScriptBackendApproval;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Execute an isolation run with the SDK's ACP-defaulted runner and script-backend
|
|
19
|
+
* approval policy. Caller-supplied runners remain caller-owned.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runIsolation<T = unknown>(opts: RunIsolationSdkOptions): Promise<IsolationRunResult<T>>;
|
|
22
|
+
export { createReplayRunner };
|
|
23
|
+
export type { CheckpointCallContext, IsolationRunResult, IsolationTarget, ReplayCallReport, ReplayDivergenceEvent, ReplayObservation, ReplayReport, ReplayRunner, ReplayRunnerOptions, ResolvedIsolationTarget, RunIsolationOptions, WorkflowCallRecord, WorkflowRecordedError, };
|
|
24
|
+
//# sourceMappingURL=isolation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../src/isolation.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAMnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAyB,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAEzF,KAAK,WAAW,GAAG,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAClE,KAAK,oBAAoB,GAAG,MAAM,WAAW,CAAC;AAI9C,2GAA2G;AAC3G,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,GAAG,IAAI,CAEjG;AAED,MAAM,WAAW,sBACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,gBAAgB,CAAC;IAC9D,4DAA4D;IAC5D,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,qBAAqB,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAC5C,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CA0ChC;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC9B,YAAY,EACV,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,GACtB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createAcpRunner } from "@automatalabs/acp-agents";
|
|
2
|
+
import { createReplayRunner, createRunPersistence, parseWorkflowScript, runIsolation as runEngineIsolation, WorkflowError, WorkflowErrorCode, } from "@automatalabs/workflow-engine";
|
|
3
|
+
import { approveScriptBackends } from "./script-backends.js";
|
|
4
|
+
let defaultRunnerFactory = createAcpRunner;
|
|
5
|
+
/** Test-only injection point for the owned default runner. Deliberately absent from the package barrel. */
|
|
6
|
+
export function __setDefaultRunnerFactoryForTests(factory) {
|
|
7
|
+
defaultRunnerFactory = factory ?? createAcpRunner;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Execute an isolation run with the SDK's ACP-defaulted runner and script-backend
|
|
11
|
+
* approval policy. Caller-supplied runners remain caller-owned.
|
|
12
|
+
*/
|
|
13
|
+
export async function runIsolation(opts) {
|
|
14
|
+
let recording;
|
|
15
|
+
try {
|
|
16
|
+
const persistence = createRunPersistence(opts.cwd ?? process.cwd(), undefined, {
|
|
17
|
+
persistenceRoot: opts.persistenceRoot,
|
|
18
|
+
});
|
|
19
|
+
recording = persistence.load(opts.baselineRunId);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error instanceof WorkflowError)
|
|
23
|
+
throw error;
|
|
24
|
+
throw new WorkflowError(error instanceof Error ? error.message : String(error), WorkflowErrorCode.PERSISTENCE_ERROR, { recoverable: false });
|
|
25
|
+
}
|
|
26
|
+
let declared;
|
|
27
|
+
if (recording && typeof recording.script === "string") {
|
|
28
|
+
try {
|
|
29
|
+
declared = parseWorkflowScript(recording.script).meta.backends;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// The engine owns malformed-recording diagnosis and will reject it with its
|
|
33
|
+
// typed preflight error after reloading the same persisted bytes.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const scriptBackends = declared && Object.keys(declared).length > 0
|
|
37
|
+
? await approveScriptBackends(declared, opts.allowScriptBackends, "runIsolation")
|
|
38
|
+
: undefined;
|
|
39
|
+
const owned = opts.runner === undefined;
|
|
40
|
+
const runner = opts.runner ?? defaultRunnerFactory();
|
|
41
|
+
try {
|
|
42
|
+
const { allowScriptBackends: _approval, ...engineOptions } = opts;
|
|
43
|
+
return await runEngineIsolation({
|
|
44
|
+
...engineOptions,
|
|
45
|
+
runner,
|
|
46
|
+
...(scriptBackends === undefined ? {} : { scriptBackends }),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (owned)
|
|
51
|
+
await runner.dispose();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export { createReplayRunner };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WorkflowBackendConfig } from "@automatalabs/shared-types";
|
|
2
|
+
export type ScriptBackendApproval = boolean | ((backend: {
|
|
3
|
+
name: string;
|
|
4
|
+
} & WorkflowBackendConfig) => boolean | Promise<boolean>);
|
|
5
|
+
/** Resolve explicit host approval for script-declared command-spawning backends. */
|
|
6
|
+
export declare function approveScriptBackends(declared: Record<string, WorkflowBackendConfig>, approval: ScriptBackendApproval | undefined, helperName: "runDynamicWorkflow" | "runIsolation"): Promise<Record<string, WorkflowBackendConfig>>;
|
|
7
|
+
//# sourceMappingURL=script-backends.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-backends.d.ts","sourceRoot":"","sources":["../src/script-backends.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,MAAM,MAAM,qBAAqB,GAC7B,OAAO,GACP,CAAC,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,qBAAqB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAExF,oFAAoF;AACpF,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,EAC/C,QAAQ,EAAE,qBAAqB,GAAG,SAAS,EAC3C,UAAU,EAAE,oBAAoB,GAAG,cAAc,GAChD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAyBhD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { WorkflowError, WorkflowErrorCode } from "@automatalabs/workflow-engine";
|
|
2
|
+
/** Resolve explicit host approval for script-declared command-spawning backends. */
|
|
3
|
+
export async function approveScriptBackends(declared, approval, helperName) {
|
|
4
|
+
const names = Object.keys(declared).join(", ");
|
|
5
|
+
if (approval === undefined || approval === false) {
|
|
6
|
+
const guidance = helperName === "runDynamicWorkflow"
|
|
7
|
+
? "Pass allowScriptBackends: true (or a per-backend approval callback) to runDynamicWorkflow, or thread an approved registry yourself via exec.scriptBackends."
|
|
8
|
+
: "Pass allowScriptBackends: true (or a per-backend approval callback) to runIsolation.";
|
|
9
|
+
throw new WorkflowError(`script declares custom ACP backends (meta.backends: ${names}) — these spawn commands on this machine and require explicit approval. ` +
|
|
10
|
+
guidance, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false });
|
|
11
|
+
}
|
|
12
|
+
if (approval === true)
|
|
13
|
+
return declared;
|
|
14
|
+
for (const [name, config] of Object.entries(declared)) {
|
|
15
|
+
if (!(await approval({ name, ...config }))) {
|
|
16
|
+
throw new WorkflowError(`script backend "${name}" (command: ${config.command}) was declined by the allowScriptBackends callback — aborting the run`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return declared;
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type CustomBackendConfig, type ProbedConfigOptions } from "@automatalabs/acp-agents";
|
|
2
|
+
export interface ValidateProbeRunner {
|
|
3
|
+
probeConfigOptions(spec?: string, opts?: {
|
|
4
|
+
cwd?: string;
|
|
5
|
+
}): Promise<ProbedConfigOptions>;
|
|
6
|
+
dispose(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export type ValidateProbeFactory = (backends: Record<string, CustomBackendConfig> | undefined) => ValidateProbeRunner;
|
|
9
|
+
export declare function createValidateProbeRunner(backends: Record<string, CustomBackendConfig> | undefined): ValidateProbeRunner;
|
|
10
|
+
/** Package-internal hermetic test seam. Deliberately absent from the public index export. */
|
|
11
|
+
export declare function setValidateProbeFactoryForTests(factory: ValidateProbeFactory): () => void;
|
|
12
|
+
//# sourceMappingURL=validate-internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-internal.d.ts","sourceRoot":"","sources":["../src/validate-internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACzF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,SAAS,KACtD,mBAAmB,CAAC;AAIzB,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,SAAS,GACxD,mBAAmB,CAErB;AAED,6FAA6F;AAC7F,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAMzF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AcpAgentRunner, } from "@automatalabs/acp-agents";
|
|
2
|
+
let probeFactory = (backends) => new AcpAgentRunner({ backends });
|
|
3
|
+
export function createValidateProbeRunner(backends) {
|
|
4
|
+
return probeFactory(backends);
|
|
5
|
+
}
|
|
6
|
+
/** Package-internal hermetic test seam. Deliberately absent from the public index export. */
|
|
7
|
+
export function setValidateProbeFactoryForTests(factory) {
|
|
8
|
+
const previous = probeFactory;
|
|
9
|
+
probeFactory = factory;
|
|
10
|
+
return () => {
|
|
11
|
+
probeFactory = previous;
|
|
12
|
+
};
|
|
13
|
+
}
|
package/dist/validate.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SessionConfigOption } from "@automatalabs/acp-agents";
|
|
1
2
|
import type { WorkflowDir } from "@automatalabs/workflow-engine";
|
|
2
3
|
import type { WorkflowMeta } from "@automatalabs/shared-types";
|
|
3
4
|
export type MockAnswerJson = null | boolean | number | string | MockAnswerJson[] | {
|
|
@@ -64,13 +65,22 @@ export interface ValidatedAgentCall {
|
|
|
64
65
|
model?: string;
|
|
65
66
|
tier?: string;
|
|
66
67
|
mode?: string;
|
|
67
|
-
/**
|
|
68
|
-
|
|
68
|
+
/** The verbatim session config options authored for this call. */
|
|
69
|
+
configOptions?: Record<string, string | boolean>;
|
|
70
|
+
/** Which concrete backend the spec routes to: "claude" | "codex" | "opencode" | a custom
|
|
71
|
+
* backend name (suffixed " (script-declared)" when it comes from meta.backends). */
|
|
69
72
|
backend: string;
|
|
70
73
|
/** True when the call requested structured output. */
|
|
71
74
|
schema: boolean;
|
|
72
75
|
mockAnswer?: ValidatedMockAnswerUse;
|
|
73
76
|
}
|
|
77
|
+
export interface ValidateHarnessOptions {
|
|
78
|
+
backendId: string;
|
|
79
|
+
probed: boolean;
|
|
80
|
+
/** Present when probed=false: the harness's spawn/auth/session error. */
|
|
81
|
+
error?: string;
|
|
82
|
+
options?: SessionConfigOption[];
|
|
83
|
+
}
|
|
74
84
|
export interface ValidatedCheckpoint {
|
|
75
85
|
prompt: string;
|
|
76
86
|
kind: string;
|
|
@@ -78,9 +88,9 @@ export interface ValidatedCheckpoint {
|
|
|
78
88
|
reply: unknown;
|
|
79
89
|
}
|
|
80
90
|
export interface ValidateWorkflowReport {
|
|
81
|
-
/** True when
|
|
91
|
+
/** True when parse, dry run, and all checks against successfully probed catalogs pass. */
|
|
82
92
|
ok: boolean;
|
|
83
|
-
/** 0 = valid; 1 = parse/static failure; 2 = dry-run failure. */
|
|
93
|
+
/** 0 = valid; 1 = parse/static failure; 2 = dry-run or config-option failure. */
|
|
84
94
|
exitCode: 0 | 1 | 2;
|
|
85
95
|
parse: {
|
|
86
96
|
ok: boolean;
|
|
@@ -98,6 +108,8 @@ export interface ValidateWorkflowReport {
|
|
|
98
108
|
phasesVisited: string[];
|
|
99
109
|
logs: string[];
|
|
100
110
|
durationMs: number;
|
|
111
|
+
/** Fresh, per-run advertised config-option catalogs for every routed harness. */
|
|
112
|
+
harnessOptions?: ValidateHarnessOptions[];
|
|
101
113
|
/** The script's return value, composed from fabricated agent results. */
|
|
102
114
|
result?: unknown;
|
|
103
115
|
mockAnswers?: ValidatedMockAnswers;
|
|
@@ -114,8 +126,9 @@ export declare function fabricateFromSchema(schema: unknown, hint?: string, dept
|
|
|
114
126
|
* budget-guarded script paths deterministically. */
|
|
115
127
|
export declare const MOCK_TOKENS_PER_AGENT = 1000;
|
|
116
128
|
/**
|
|
117
|
-
* Validate a workflow script: parse it,
|
|
118
|
-
*
|
|
129
|
+
* Validate a workflow script: parse it, dry-run against a mock AgentRunner, then probe
|
|
130
|
+
* each routed harness's advertised config options. Never throws for an invalid script —
|
|
131
|
+
* read `report.ok` / `report.exitCode`.
|
|
119
132
|
*/
|
|
120
133
|
export declare function validateWorkflowScript(script: string, options?: ValidateWorkflowOptions): Promise<ValidateWorkflowReport>;
|
|
121
134
|
/** Render a ValidateWorkflowReport as the human-readable CLI output. */
|
package/dist/validate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
|
package/dist/validate.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
// Token-free validation for workflow scripts: a static parse (meta literal, syntax,
|
|
2
2
|
// determinism blocklist) followed by an optional DRY RUN — the script executes for real
|
|
3
3
|
// in the engine's deterministic realm, but every agent() call is served by an in-process
|
|
4
|
-
// mock AgentRunner that fabricates schema-conforming results.
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// mock AgentRunner that fabricates schema-conforming results. Afterward, each routed ACP harness
|
|
5
|
+
// is opened once without a prompt to read its advertised config options. No tokens are spent, a
|
|
6
|
+
// mock live confirm resolves checkpoints to their declared defaults, and run state is journaled
|
|
7
|
+
// nowhere (journaling off + a throwaway persistence root for the run lease).
|
|
7
8
|
//
|
|
8
9
|
// This is the programmatic core behind `agentprism-workflows validate` (see ./cli.ts).
|
|
9
10
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
10
11
|
import { tmpdir } from "node:os";
|
|
11
12
|
import { join } from "node:path";
|
|
12
13
|
import { openWorkflowDir, WorkflowError, WorkflowErrorCode, WorkflowManager, parseWorkflowScript, redactText, } from "@automatalabs/workflow-engine";
|
|
13
|
-
import { resolveBackendRegistry, selectBackend } from "@automatalabs/acp-agents";
|
|
14
|
+
import { registryWithRunBackends, resolveBackendRegistry, selectBackend, } from "@automatalabs/acp-agents";
|
|
14
15
|
import { Check, Errors } from "typebox/value";
|
|
16
|
+
import { createValidateProbeRunner } from "./validate-internal.js";
|
|
15
17
|
const MAX_MOCK_ANSWERS_BYTES = 256 * 1024;
|
|
16
18
|
const MAX_MOCK_ANSWER_RULES = 256;
|
|
17
19
|
const MAX_MOCK_ANSWER_GLOB_LENGTH = 256;
|
|
@@ -550,25 +552,126 @@ export function fabricateFromSchema(schema, hint = "value", depth = 0) {
|
|
|
550
552
|
/** Tokens the mock runner reports per agent call, so `--token-budget` exercises
|
|
551
553
|
* budget-guarded script paths deterministically. */
|
|
552
554
|
export const MOCK_TOKENS_PER_AGENT = 1000;
|
|
553
|
-
function
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
555
|
+
function routeBackend(model, tier, registry, hostRegistry, declared) {
|
|
556
|
+
const backendId = selectBackend({ model, tier }, registry).id;
|
|
557
|
+
const scriptDeclared = !hostRegistry.has(backendId) &&
|
|
558
|
+
Object.keys(declared ?? {}).some((name) => name.toLowerCase() === backendId.toLowerCase());
|
|
559
|
+
return {
|
|
560
|
+
backendId,
|
|
561
|
+
display: scriptDeclared ? `${backendId} (script-declared)` : backendId,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function registryOptions(registry) {
|
|
565
|
+
if (registry.size === 0)
|
|
566
|
+
return undefined;
|
|
567
|
+
return Object.fromEntries([...registry].map(([name, entry]) => {
|
|
568
|
+
const { name: _name, ...config } = entry;
|
|
569
|
+
return [name, config];
|
|
570
|
+
}));
|
|
571
|
+
}
|
|
572
|
+
async function probeHarnessConfigOptions(calls, cwd, registry, hostRegistry, declared, warnings) {
|
|
573
|
+
const backendIds = [
|
|
574
|
+
...new Set(calls.map((call) => routeBackend(call.model, call.tier, registry, hostRegistry, declared).backendId)),
|
|
575
|
+
].sort();
|
|
576
|
+
const harnessOptions = [];
|
|
577
|
+
const catalogs = new Map();
|
|
578
|
+
if (backendIds.length === 0)
|
|
579
|
+
return { harnessOptions, catalogs };
|
|
580
|
+
let runner;
|
|
581
|
+
try {
|
|
582
|
+
runner = createValidateProbeRunner(registryOptions(registry));
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
const reason = errorMessage(error);
|
|
586
|
+
for (const backendId of backendIds) {
|
|
587
|
+
warnings.push(`could not probe ${backendId} — configOptions on its calls are unverified: ${reason}`);
|
|
588
|
+
harnessOptions.push({ backendId, probed: false, error: reason });
|
|
589
|
+
}
|
|
590
|
+
return { harnessOptions, catalogs };
|
|
560
591
|
}
|
|
561
592
|
try {
|
|
562
|
-
const
|
|
563
|
-
|
|
593
|
+
for (const backendId of backendIds) {
|
|
594
|
+
try {
|
|
595
|
+
const result = await runner.probeConfigOptions(backendId, { cwd });
|
|
596
|
+
catalogs.set(backendId, result.options);
|
|
597
|
+
harnessOptions.push({
|
|
598
|
+
backendId: result.backendId,
|
|
599
|
+
probed: true,
|
|
600
|
+
options: result.options,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
const reason = errorMessage(error);
|
|
605
|
+
warnings.push(`could not probe ${backendId} — configOptions on its calls are unverified: ${reason}`);
|
|
606
|
+
harnessOptions.push({ backendId, probed: false, error: reason });
|
|
607
|
+
}
|
|
608
|
+
}
|
|
564
609
|
}
|
|
565
|
-
|
|
566
|
-
|
|
610
|
+
finally {
|
|
611
|
+
try {
|
|
612
|
+
await runner.dispose();
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
// Probe results are complete; process cleanup must not change validation semantics.
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return { harnessOptions, catalogs };
|
|
619
|
+
}
|
|
620
|
+
function errorMessage(error) {
|
|
621
|
+
return redactText(error instanceof Error ? error.message : String(error)).value;
|
|
622
|
+
}
|
|
623
|
+
function configOptionErrors(calls, catalogs, registry, hostRegistry, declared) {
|
|
624
|
+
const errors = [];
|
|
625
|
+
for (const call of calls) {
|
|
626
|
+
if (!call.configOptions || Object.keys(call.configOptions).length === 0)
|
|
627
|
+
continue;
|
|
628
|
+
const backendId = routeBackend(call.model, call.tier, registry, hostRegistry, declared).backendId;
|
|
629
|
+
const advertised = catalogs.get(backendId);
|
|
630
|
+
if (!advertised)
|
|
631
|
+
continue;
|
|
632
|
+
const optionIds = advertised.map((option) => option.id);
|
|
633
|
+
for (const [id, value] of Object.entries(call.configOptions)) {
|
|
634
|
+
const authored = displayValue(value);
|
|
635
|
+
if (id === "model") {
|
|
636
|
+
errors.push(`agent "${call.label}" configOptions option "model" authored value ${authored} is reserved; ` +
|
|
637
|
+
`advertised alternatives: use the call's model field; option ids ${displayAlternatives(optionIds)}`);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
const option = advertised.find((candidate) => candidate.id === id);
|
|
641
|
+
if (!option) {
|
|
642
|
+
errors.push(`agent "${call.label}" configOptions option ${JSON.stringify(id)} authored value ${authored} is unknown; ` +
|
|
643
|
+
`advertised alternatives: option ids ${displayAlternatives(optionIds)}`);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (option.type === "select") {
|
|
647
|
+
const choices = selectChoiceValues(option);
|
|
648
|
+
if (typeof value !== "string" || !choices.includes(value)) {
|
|
649
|
+
errors.push(`agent "${call.label}" configOptions option ${JSON.stringify(id)} authored value ${authored} is not an advertised select value; ` +
|
|
650
|
+
`advertised alternatives: ${displayAlternatives(choices)}`);
|
|
651
|
+
}
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
if (typeof value !== "boolean") {
|
|
655
|
+
errors.push(`agent "${call.label}" configOptions option ${JSON.stringify(id)} authored value ${authored} must be boolean; ` +
|
|
656
|
+
"advertised alternatives: true, false");
|
|
657
|
+
}
|
|
658
|
+
}
|
|
567
659
|
}
|
|
660
|
+
return errors;
|
|
661
|
+
}
|
|
662
|
+
function selectChoiceValues(option) {
|
|
663
|
+
return option.options.flatMap((entry) => ("options" in entry ? entry.options : [entry])).map((entry) => entry.value);
|
|
664
|
+
}
|
|
665
|
+
function displayValue(value) {
|
|
666
|
+
return JSON.stringify(value) ?? String(value);
|
|
667
|
+
}
|
|
668
|
+
function displayAlternatives(values) {
|
|
669
|
+
return values.length > 0 ? values.map((value) => JSON.stringify(value)).join(", ") : "(none advertised)";
|
|
568
670
|
}
|
|
569
671
|
/**
|
|
570
|
-
* Validate a workflow script: parse it,
|
|
571
|
-
*
|
|
672
|
+
* Validate a workflow script: parse it, dry-run against a mock AgentRunner, then probe
|
|
673
|
+
* each routed harness's advertised config options. Never throws for an invalid script —
|
|
674
|
+
* read `report.ok` / `report.exitCode`.
|
|
572
675
|
*/
|
|
573
676
|
export async function validateWorkflowScript(script, options = {}) {
|
|
574
677
|
const mockAnswerState = options.mockAnswers === undefined ? undefined : normalizeMockAnswers(options.mockAnswers);
|
|
@@ -585,7 +688,11 @@ export async function validateWorkflowScript(script, options = {}) {
|
|
|
585
688
|
warnings,
|
|
586
689
|
};
|
|
587
690
|
}
|
|
588
|
-
const declaredBackends = meta.backends && Object.keys(meta.backends).length > 0
|
|
691
|
+
const declaredBackends = meta.backends && Object.keys(meta.backends).length > 0
|
|
692
|
+
? meta.backends
|
|
693
|
+
: undefined;
|
|
694
|
+
const hostRegistry = resolveBackendRegistry();
|
|
695
|
+
const backendRegistry = registryWithRunBackends(hostRegistry, declaredBackends);
|
|
589
696
|
if (declaredBackends) {
|
|
590
697
|
warnings.push(`script declares custom backends (${Object.keys(declaredBackends).join(", ")}) — real runs must approve them ` +
|
|
591
698
|
`(allowScriptBackends / exec.scriptBackends / AGENTPRISM_ALLOW_SCRIPT_BACKENDS=1); the dry run treats them as approved`);
|
|
@@ -608,6 +715,7 @@ export async function validateWorkflowScript(script, options = {}) {
|
|
|
608
715
|
const metadata = {
|
|
609
716
|
tier: runOptions.tier,
|
|
610
717
|
mode: runOptions.mode,
|
|
718
|
+
configOptions: runOptions.configOptions,
|
|
611
719
|
schema: runOptions.schema !== undefined,
|
|
612
720
|
};
|
|
613
721
|
mockMeta.set(label, metadata);
|
|
@@ -615,8 +723,9 @@ export async function validateWorkflowScript(script, options = {}) {
|
|
|
615
723
|
if (pendingCall) {
|
|
616
724
|
pendingCall.tier = metadata.tier;
|
|
617
725
|
pendingCall.mode = metadata.mode;
|
|
726
|
+
pendingCall.configOptions = metadata.configOptions;
|
|
618
727
|
pendingCall.schema = metadata.schema;
|
|
619
|
-
pendingCall.backend =
|
|
728
|
+
pendingCall.backend = routeBackend(pendingCall.model, metadata.tier, backendRegistry, hostRegistry, declaredBackends).display;
|
|
620
729
|
}
|
|
621
730
|
const base = runOptions.schema === undefined ? undefined : fabricateFromSchema(runOptions.schema);
|
|
622
731
|
const reservation = mockAnswerState ? reserveMockAnswer(mockAnswerState, label) : undefined;
|
|
@@ -669,7 +778,8 @@ export async function validateWorkflowScript(script, options = {}) {
|
|
|
669
778
|
model: event.model,
|
|
670
779
|
tier: extra.tier,
|
|
671
780
|
mode: extra.mode,
|
|
672
|
-
|
|
781
|
+
configOptions: event.configOptions ?? extra.configOptions,
|
|
782
|
+
backend: routeBackend(event.model, extra.tier, backendRegistry, hostRegistry, declaredBackends).display,
|
|
673
783
|
schema: extra.schema,
|
|
674
784
|
};
|
|
675
785
|
agentCalls.push(call);
|
|
@@ -702,17 +812,18 @@ export async function validateWorkflowScript(script, options = {}) {
|
|
|
702
812
|
if (extra) {
|
|
703
813
|
call.tier = extra.tier;
|
|
704
814
|
call.mode = extra.mode;
|
|
815
|
+
call.configOptions = extra.configOptions;
|
|
705
816
|
call.schema = extra.schema;
|
|
706
|
-
call.backend =
|
|
817
|
+
call.backend = routeBackend(call.model, extra.tier, backendRegistry, hostRegistry, declaredBackends).display;
|
|
707
818
|
}
|
|
708
819
|
}
|
|
709
820
|
}
|
|
710
|
-
const
|
|
711
|
-
if (!
|
|
821
|
+
const runOk = run.status === "completed";
|
|
822
|
+
if (!runOk && flows === undefined && run.reason?.includes("must be the first statement") && /\bworkflow\s*\(/.test(script)) {
|
|
712
823
|
warnings.push('the failure looks like a nested workflow("<name>") call on a bare name — provide workflow dirs ' +
|
|
713
824
|
"(ValidateWorkflowOptions.workflows / --workflows-dir) so names resolve during the dry run");
|
|
714
825
|
}
|
|
715
|
-
if (
|
|
826
|
+
if (runOk) {
|
|
716
827
|
if (agentCalls.length === 0 && checkpoints.length === 0) {
|
|
717
828
|
warnings.push("the script completed without a single agent() or checkpoint() call");
|
|
718
829
|
}
|
|
@@ -733,6 +844,15 @@ export async function validateWorkflowScript(script, options = {}) {
|
|
|
733
844
|
const mockAnswers = mockAnswerState ? buildMockAnswersReport(mockAnswerState) : undefined;
|
|
734
845
|
if (mockAnswerState && mockAnswers)
|
|
735
846
|
appendMockAnswerWarnings(mockAnswerState, mockAnswers, warnings);
|
|
847
|
+
const probed = await probeHarnessConfigOptions(agentCalls, baseCwd, backendRegistry, hostRegistry, declaredBackends, warnings);
|
|
848
|
+
const optionErrors = configOptionErrors(agentCalls, probed.catalogs, backendRegistry, hostRegistry, declaredBackends);
|
|
849
|
+
const ok = runOk && optionErrors.length === 0;
|
|
850
|
+
const runReason = timedOut ? `dry run exceeded ${timeoutMs}ms and was aborted` : run.reason;
|
|
851
|
+
const reason = optionErrors.length === 0
|
|
852
|
+
? runReason
|
|
853
|
+
: [runReason, "configOptions validation failed:", ...optionErrors.map((error) => `- ${error}`)]
|
|
854
|
+
.filter(Boolean)
|
|
855
|
+
.join("\n");
|
|
736
856
|
return {
|
|
737
857
|
ok,
|
|
738
858
|
exitCode: ok ? 0 : 2,
|
|
@@ -740,13 +860,14 @@ export async function validateWorkflowScript(script, options = {}) {
|
|
|
740
860
|
dryRun: {
|
|
741
861
|
ok,
|
|
742
862
|
status: run.status,
|
|
743
|
-
reason
|
|
863
|
+
reason,
|
|
744
864
|
timedOut,
|
|
745
865
|
agentCalls,
|
|
746
866
|
checkpoints,
|
|
747
867
|
phasesVisited: run.phases ?? [],
|
|
748
868
|
logs: run.logs ?? [],
|
|
749
869
|
durationMs: run.durationMs,
|
|
870
|
+
harnessOptions: probed.harnessOptions,
|
|
750
871
|
result: run.result,
|
|
751
872
|
...(mockAnswers ? { mockAnswers } : {}),
|
|
752
873
|
},
|
|
@@ -803,6 +924,25 @@ export function formatValidateReport(report) {
|
|
|
803
924
|
.join(" ");
|
|
804
925
|
lines.push(` • ${call.label} ${bits}`);
|
|
805
926
|
}
|
|
927
|
+
lines.push(" advertised config options:");
|
|
928
|
+
for (const harness of dry.harnessOptions ?? []) {
|
|
929
|
+
if (!harness.probed) {
|
|
930
|
+
lines.push(` ${harness.backendId}: probe failed — ${harness.error ?? "unknown error"}`);
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
lines.push(` ${harness.backendId}:`);
|
|
934
|
+
lines.push(" id | type | current | choices");
|
|
935
|
+
if ((harness.options ?? []).length === 0) {
|
|
936
|
+
lines.push(" (none advertised)");
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
for (const option of harness.options ?? []) {
|
|
940
|
+
const choices = option.type === "select" ? displayAlternatives(selectChoiceValues(option)) : "true, false";
|
|
941
|
+
lines.push(` ${option.id} | ${option.type} | ${displayValue(option.currentValue)} | ${choices}`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if ((dry.harnessOptions ?? []).length === 0)
|
|
945
|
+
lines.push(" (no routed harnesses)");
|
|
806
946
|
for (const cp of dry.checkpoints) {
|
|
807
947
|
lines.push(` ◆ checkpoint [${cp.kind}] "${truncate(cp.prompt, 60)}" → ${JSON.stringify(cp.reply)}`);
|
|
808
948
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automatalabs/workflows",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"typebox": "1.3.2",
|
|
34
|
-
"@automatalabs/shared-types": "0.
|
|
35
|
-
"@automatalabs/workflow-engine": "0.
|
|
36
|
-
"@automatalabs/acp-agents": "0.
|
|
34
|
+
"@automatalabs/shared-types": "0.20.0",
|
|
35
|
+
"@automatalabs/workflow-engine": "0.21.0",
|
|
36
|
+
"@automatalabs/acp-agents": "0.26.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsc -b",
|