@travisliu/open-dynamic-workflow 0.3.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/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/README.md +312 -0
- package/dist/agents/antigravity-cli.d.ts +19 -0
- package/dist/agents/antigravity-cli.js +177 -0
- package/dist/agents/antigravity-cli.js.map +1 -0
- package/dist/agents/codex-exec.d.ts +12 -0
- package/dist/agents/codex-exec.js +283 -0
- package/dist/agents/codex-exec.js.map +1 -0
- package/dist/agents/execute-agent.d.ts +21 -0
- package/dist/agents/execute-agent.js +593 -0
- package/dist/agents/execute-agent.js.map +1 -0
- package/dist/agents/execution-types.d.ts +20 -0
- package/dist/agents/execution-types.js +2 -0
- package/dist/agents/execution-types.js.map +1 -0
- package/dist/agents/gemini-cli.d.ts +14 -0
- package/dist/agents/gemini-cli.js +176 -0
- package/dist/agents/gemini-cli.js.map +1 -0
- package/dist/agents/github-copilot-cli.d.ts +16 -0
- package/dist/agents/github-copilot-cli.js +256 -0
- package/dist/agents/github-copilot-cli.js.map +1 -0
- package/dist/agents/mock-adapter.d.ts +12 -0
- package/dist/agents/mock-adapter.js +66 -0
- package/dist/agents/mock-adapter.js.map +1 -0
- package/dist/agents/model-args.d.ts +2 -0
- package/dist/agents/model-args.js +15 -0
- package/dist/agents/model-args.js.map +1 -0
- package/dist/agents/opencode-cli.d.ts +22 -0
- package/dist/agents/opencode-cli.js +245 -0
- package/dist/agents/opencode-cli.js.map +1 -0
- package/dist/agents/pi-coding-agent.d.ts +31 -0
- package/dist/agents/pi-coding-agent.js +317 -0
- package/dist/agents/pi-coding-agent.js.map +1 -0
- package/dist/agents/process-runner.d.ts +2 -0
- package/dist/agents/process-runner.js +130 -0
- package/dist/agents/process-runner.js.map +1 -0
- package/dist/agents/provider-health.d.ts +3 -0
- package/dist/agents/provider-health.js +30 -0
- package/dist/agents/provider-health.js.map +1 -0
- package/dist/agents/registry.d.ts +12 -0
- package/dist/agents/registry.js +45 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/resolve-model.d.ts +11 -0
- package/dist/agents/resolve-model.js +30 -0
- package/dist/agents/resolve-model.js.map +1 -0
- package/dist/agents/types.d.ts +2 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/artifacts/call-cache.d.ts +117 -0
- package/dist/artifacts/call-cache.js +449 -0
- package/dist/artifacts/call-cache.js.map +1 -0
- package/dist/artifacts/logs.d.ts +13 -0
- package/dist/artifacts/logs.js +24 -0
- package/dist/artifacts/logs.js.map +1 -0
- package/dist/artifacts/manifest.d.ts +18 -0
- package/dist/artifacts/manifest.js +29 -0
- package/dist/artifacts/manifest.js.map +1 -0
- package/dist/artifacts/run-store.d.ts +22 -0
- package/dist/artifacts/run-store.js +148 -0
- package/dist/artifacts/run-store.js.map +1 -0
- package/dist/bin/open-dynamic-workflow.d.ts +2 -0
- package/dist/bin/open-dynamic-workflow.js +45 -0
- package/dist/bin/open-dynamic-workflow.js.map +1 -0
- package/dist/cli/args.d.ts +36 -0
- package/dist/cli/args.js +68 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +9 -0
- package/dist/cli/commands/doctor.js +109 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts +23 -0
- package/dist/cli/commands/init.js +147 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/list.d.ts +12 -0
- package/dist/cli/commands/list.js +65 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +5 -0
- package/dist/cli/commands/resume.js +81 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/run.d.ts +18 -0
- package/dist/cli/commands/run.js +318 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +14 -0
- package/dist/cli/commands/validate.js +66 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +169 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init/defaults.d.ts +12 -0
- package/dist/cli/init/defaults.js +54 -0
- package/dist/cli/init/defaults.js.map +1 -0
- package/dist/cli/init/planner.d.ts +10 -0
- package/dist/cli/init/planner.js +163 -0
- package/dist/cli/init/planner.js.map +1 -0
- package/dist/cli/init/prompts.d.ts +15 -0
- package/dist/cli/init/prompts.js +83 -0
- package/dist/cli/init/prompts.js.map +1 -0
- package/dist/cli/init/providers.d.ts +13 -0
- package/dist/cli/init/providers.js +100 -0
- package/dist/cli/init/providers.js.map +1 -0
- package/dist/cli/init/renderer.d.ts +8 -0
- package/dist/cli/init/renderer.js +84 -0
- package/dist/cli/init/renderer.js.map +1 -0
- package/dist/cli/init/smoke-test.d.ts +15 -0
- package/dist/cli/init/smoke-test.js +47 -0
- package/dist/cli/init/smoke-test.js.map +1 -0
- package/dist/cli/init/summary.d.ts +11 -0
- package/dist/cli/init/summary.js +65 -0
- package/dist/cli/init/summary.js.map +1 -0
- package/dist/cli/init/types.d.ts +81 -0
- package/dist/cli/init/types.js +2 -0
- package/dist/cli/init/types.js.map +1 -0
- package/dist/cli/init/writer.d.ts +3 -0
- package/dist/cli/init/writer.js +57 -0
- package/dist/cli/init/writer.js.map +1 -0
- package/dist/cli/package-info.d.ts +1 -0
- package/dist/cli/package-info.js +13 -0
- package/dist/cli/package-info.js.map +1 -0
- package/dist/cli/paths.d.ts +2 -0
- package/dist/cli/paths.js +8 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/print.d.ts +22 -0
- package/dist/cli/print.js +48 -0
- package/dist/cli/print.js.map +1 -0
- package/dist/config/defaults.d.ts +3 -0
- package/dist/config/defaults.js +116 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.js +72 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/merge.d.ts +10 -0
- package/dist/config/merge.js +58 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/schema.d.ts +2 -0
- package/dist/config/schema.js +236 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/types.d.ts +93 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/discovery/collect-files.d.ts +10 -0
- package/dist/discovery/collect-files.js +189 -0
- package/dist/discovery/collect-files.js.map +1 -0
- package/dist/discovery/definition-call.d.ts +10 -0
- package/dist/discovery/definition-call.js +37 -0
- package/dist/discovery/definition-call.js.map +1 -0
- package/dist/discovery/diagnostics.d.ts +17 -0
- package/dist/discovery/diagnostics.js +32 -0
- package/dist/discovery/diagnostics.js.map +1 -0
- package/dist/discovery/directories.d.ts +8 -0
- package/dist/discovery/directories.js +32 -0
- package/dist/discovery/directories.js.map +1 -0
- package/dist/discovery/duplicate-detector.d.ts +8 -0
- package/dist/discovery/duplicate-detector.js +40 -0
- package/dist/discovery/duplicate-detector.js.map +1 -0
- package/dist/discovery/extract-agent.d.ts +2 -0
- package/dist/discovery/extract-agent.js +166 -0
- package/dist/discovery/extract-agent.js.map +1 -0
- package/dist/discovery/extract-tool.d.ts +2 -0
- package/dist/discovery/extract-tool.js +188 -0
- package/dist/discovery/extract-tool.js.map +1 -0
- package/dist/discovery/extract-workflow.d.ts +2 -0
- package/dist/discovery/extract-workflow.js +180 -0
- package/dist/discovery/extract-workflow.js.map +1 -0
- package/dist/discovery/file-patterns.d.ts +3 -0
- package/dist/discovery/file-patterns.js +93 -0
- package/dist/discovery/file-patterns.js.map +1 -0
- package/dist/discovery/index.d.ts +7 -0
- package/dist/discovery/index.js +8 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/schema-summary.d.ts +13 -0
- package/dist/discovery/schema-summary.js +37 -0
- package/dist/discovery/schema-summary.js.map +1 -0
- package/dist/discovery/service.d.ts +4 -0
- package/dist/discovery/service.js +135 -0
- package/dist/discovery/service.js.map +1 -0
- package/dist/discovery/static-values.d.ts +13 -0
- package/dist/discovery/static-values.js +68 -0
- package/dist/discovery/static-values.js.map +1 -0
- package/dist/discovery/types.d.ts +95 -0
- package/dist/discovery/types.js +2 -0
- package/dist/discovery/types.js.map +1 -0
- package/dist/doctors/public.d.ts +15 -0
- package/dist/doctors/public.js +2 -0
- package/dist/doctors/public.js.map +1 -0
- package/dist/errors/codes.d.ts +53 -0
- package/dist/errors/codes.js +53 -0
- package/dist/errors/codes.js.map +1 -0
- package/dist/errors/exit-codes.d.ts +13 -0
- package/dist/errors/exit-codes.js +72 -0
- package/dist/errors/exit-codes.js.map +1 -0
- package/dist/errors/list-errors.d.ts +4 -0
- package/dist/errors/list-errors.js +11 -0
- package/dist/errors/list-errors.js.map +1 -0
- package/dist/errors/serialize.d.ts +2 -0
- package/dist/errors/serialize.js +38 -0
- package/dist/errors/serialize.js.map +1 -0
- package/dist/errors/types.d.ts +15 -0
- package/dist/errors/types.js +13 -0
- package/dist/errors/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestration/cancellation.d.ts +13 -0
- package/dist/orchestration/cancellation.js +41 -0
- package/dist/orchestration/cancellation.js.map +1 -0
- package/dist/orchestration/event-bus.d.ts +24 -0
- package/dist/orchestration/event-bus.js +50 -0
- package/dist/orchestration/event-bus.js.map +1 -0
- package/dist/orchestration/fail-fast.d.ts +8 -0
- package/dist/orchestration/fail-fast.js +21 -0
- package/dist/orchestration/fail-fast.js.map +1 -0
- package/dist/orchestration/scheduler.d.ts +37 -0
- package/dist/orchestration/scheduler.js +330 -0
- package/dist/orchestration/scheduler.js.map +1 -0
- package/dist/orchestration/tool-limiter.d.ts +10 -0
- package/dist/orchestration/tool-limiter.js +62 -0
- package/dist/orchestration/tool-limiter.js.map +1 -0
- package/dist/output/events.d.ts +290 -0
- package/dist/output/events.js +10 -0
- package/dist/output/events.js.map +1 -0
- package/dist/output/failed-artifacts.d.ts +5 -0
- package/dist/output/failed-artifacts.js +62 -0
- package/dist/output/failed-artifacts.js.map +1 -0
- package/dist/output/json-reporter.d.ts +13 -0
- package/dist/output/json-reporter.js +31 -0
- package/dist/output/json-reporter.js.map +1 -0
- package/dist/output/jsonl-reporter.d.ts +12 -0
- package/dist/output/jsonl-reporter.js +29 -0
- package/dist/output/jsonl-reporter.js.map +1 -0
- package/dist/output/list-json-reporter.d.ts +9 -0
- package/dist/output/list-json-reporter.js +22 -0
- package/dist/output/list-json-reporter.js.map +1 -0
- package/dist/output/list-jsonl-reporter.d.ts +9 -0
- package/dist/output/list-jsonl-reporter.js +43 -0
- package/dist/output/list-jsonl-reporter.js.map +1 -0
- package/dist/output/list-pretty-reporter.d.ts +13 -0
- package/dist/output/list-pretty-reporter.js +120 -0
- package/dist/output/list-pretty-reporter.js.map +1 -0
- package/dist/output/list-reporter.d.ts +14 -0
- package/dist/output/list-reporter.js +20 -0
- package/dist/output/list-reporter.js.map +1 -0
- package/dist/output/pretty-format.d.ts +19 -0
- package/dist/output/pretty-format.js +75 -0
- package/dist/output/pretty-format.js.map +1 -0
- package/dist/output/pretty-renderer.d.ts +2 -0
- package/dist/output/pretty-renderer.js +97 -0
- package/dist/output/pretty-renderer.js.map +1 -0
- package/dist/output/pretty-reporter.d.ts +14 -0
- package/dist/output/pretty-reporter.js +155 -0
- package/dist/output/pretty-reporter.js.map +1 -0
- package/dist/output/pretty-view-builder.d.ts +26 -0
- package/dist/output/pretty-view-builder.js +374 -0
- package/dist/output/pretty-view-builder.js.map +1 -0
- package/dist/output/pretty-view.d.ts +85 -0
- package/dist/output/pretty-view.js +6 -0
- package/dist/output/pretty-view.js.map +1 -0
- package/dist/output/reporter.d.ts +34 -0
- package/dist/output/reporter.js +24 -0
- package/dist/output/reporter.js.map +1 -0
- package/dist/output/verbose-formatter.d.ts +6 -0
- package/dist/output/verbose-formatter.js +137 -0
- package/dist/output/verbose-formatter.js.map +1 -0
- package/dist/pipeline/artifacts.d.ts +6 -0
- package/dist/pipeline/artifacts.js +44 -0
- package/dist/pipeline/artifacts.js.map +1 -0
- package/dist/pipeline/concurrency.d.ts +11 -0
- package/dist/pipeline/concurrency.js +60 -0
- package/dist/pipeline/concurrency.js.map +1 -0
- package/dist/pipeline/context.d.ts +15 -0
- package/dist/pipeline/context.js +17 -0
- package/dist/pipeline/context.js.map +1 -0
- package/dist/pipeline/events.d.ts +10 -0
- package/dist/pipeline/events.js +83 -0
- package/dist/pipeline/events.js.map +1 -0
- package/dist/pipeline/id.d.ts +10 -0
- package/dist/pipeline/id.js +61 -0
- package/dist/pipeline/id.js.map +1 -0
- package/dist/pipeline/item-streaming.d.ts +3 -0
- package/dist/pipeline/item-streaming.js +122 -0
- package/dist/pipeline/item-streaming.js.map +1 -0
- package/dist/pipeline/results.d.ts +10 -0
- package/dist/pipeline/results.js +88 -0
- package/dist/pipeline/results.js.map +1 -0
- package/dist/pipeline/run.d.ts +10 -0
- package/dist/pipeline/run.js +93 -0
- package/dist/pipeline/run.js.map +1 -0
- package/dist/pipeline/stage-barrier.d.ts +3 -0
- package/dist/pipeline/stage-barrier.js +135 -0
- package/dist/pipeline/stage-barrier.js.map +1 -0
- package/dist/pipeline/stage-runner.d.ts +13 -0
- package/dist/pipeline/stage-runner.js +185 -0
- package/dist/pipeline/stage-runner.js.map +1 -0
- package/dist/pipeline/summary.d.ts +12 -0
- package/dist/pipeline/summary.js +24 -0
- package/dist/pipeline/summary.js.map +1 -0
- package/dist/pipeline/types.d.ts +92 -0
- package/dist/pipeline/types.js +2 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/pipeline/validate.d.ts +11 -0
- package/dist/pipeline/validate.js +139 -0
- package/dist/pipeline/validate.js.map +1 -0
- package/dist/runtime/public.d.ts +3 -0
- package/dist/runtime/public.js +4 -0
- package/dist/runtime/public.js.map +1 -0
- package/dist/security/env.d.ts +38 -0
- package/dist/security/env.js +186 -0
- package/dist/security/env.js.map +1 -0
- package/dist/security/metadata.d.ts +6 -0
- package/dist/security/metadata.js +41 -0
- package/dist/security/metadata.js.map +1 -0
- package/dist/shared-agents/context.d.ts +2 -0
- package/dist/shared-agents/context.js +5 -0
- package/dist/shared-agents/context.js.map +1 -0
- package/dist/shared-agents/define-agent.d.ts +3 -0
- package/dist/shared-agents/define-agent.js +15 -0
- package/dist/shared-agents/define-agent.js.map +1 -0
- package/dist/shared-agents/execute.d.ts +24 -0
- package/dist/shared-agents/execute.js +63 -0
- package/dist/shared-agents/execute.js.map +1 -0
- package/dist/shared-agents/index.d.ts +5 -0
- package/dist/shared-agents/index.js +6 -0
- package/dist/shared-agents/index.js.map +1 -0
- package/dist/shared-agents/load.d.ts +8 -0
- package/dist/shared-agents/load.js +141 -0
- package/dist/shared-agents/load.js.map +1 -0
- package/dist/shared-agents/registry.d.ts +8 -0
- package/dist/shared-agents/registry.js +25 -0
- package/dist/shared-agents/registry.js.map +1 -0
- package/dist/shared-agents/render.d.ts +11 -0
- package/dist/shared-agents/render.js +31 -0
- package/dist/shared-agents/render.js.map +1 -0
- package/dist/shared-agents/resolver.d.ts +7 -0
- package/dist/shared-agents/resolver.js +22 -0
- package/dist/shared-agents/resolver.js.map +1 -0
- package/dist/shared-agents/types.d.ts +33 -0
- package/dist/shared-agents/types.js +2 -0
- package/dist/shared-agents/types.js.map +1 -0
- package/dist/shared-agents/validate.d.ts +7 -0
- package/dist/shared-agents/validate.js +223 -0
- package/dist/shared-agents/validate.js.map +1 -0
- package/dist/structured/extract-json.d.ts +11 -0
- package/dist/structured/extract-json.js +79 -0
- package/dist/structured/extract-json.js.map +1 -0
- package/dist/structured/normalize-agent-output.d.ts +18 -0
- package/dist/structured/normalize-agent-output.js +89 -0
- package/dist/structured/normalize-agent-output.js.map +1 -0
- package/dist/structured/structured-output.d.ts +14 -0
- package/dist/structured/structured-output.js +51 -0
- package/dist/structured/structured-output.js.map +1 -0
- package/dist/structured/validate-json.d.ts +13 -0
- package/dist/structured/validate-json.js +32 -0
- package/dist/structured/validate-json.js.map +1 -0
- package/dist/tools/artifacts.d.ts +8 -0
- package/dist/tools/artifacts.js +31 -0
- package/dist/tools/artifacts.js.map +1 -0
- package/dist/tools/define-tool.d.ts +10 -0
- package/dist/tools/define-tool.js +24 -0
- package/dist/tools/define-tool.js.map +1 -0
- package/dist/tools/executor-types.d.ts +40 -0
- package/dist/tools/executor-types.js +2 -0
- package/dist/tools/executor-types.js.map +1 -0
- package/dist/tools/executor.d.ts +23 -0
- package/dist/tools/executor.js +455 -0
- package/dist/tools/executor.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/load.d.ts +7 -0
- package/dist/tools/load.js +133 -0
- package/dist/tools/load.js.map +1 -0
- package/dist/tools/registry.d.ts +9 -0
- package/dist/tools/registry.js +97 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/serialization.d.ts +11 -0
- package/dist/tools/serialization.js +68 -0
- package/dist/tools/serialization.js.map +1 -0
- package/dist/tools/validate.d.ts +18 -0
- package/dist/tools/validate.js +55 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/types/agent.d.ts +136 -0
- package/dist/types/agent.js +2 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/artifacts.d.ts +88 -0
- package/dist/types/artifacts.js +2 -0
- package/dist/types/artifacts.js.map +1 -0
- package/dist/types/common.d.ts +15 -0
- package/dist/types/common.js +2 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/config.d.ts +119 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/errors.d.ts +21 -0
- package/dist/types/errors.js +50 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/events.d.ts +4 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/process.d.ts +20 -0
- package/dist/types/process.js +2 -0
- package/dist/types/process.js.map +1 -0
- package/dist/types/reporter.d.ts +1 -0
- package/dist/types/reporter.js +2 -0
- package/dist/types/reporter.js.map +1 -0
- package/dist/types/scheduler.d.ts +30 -0
- package/dist/types/scheduler.js +2 -0
- package/dist/types/scheduler.js.map +1 -0
- package/dist/types/tool.d.ts +129 -0
- package/dist/types/tool.js +2 -0
- package/dist/types/tool.js.map +1 -0
- package/dist/types/workflow.d.ts +127 -0
- package/dist/types/workflow.js +2 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/workflow/discovery.d.ts +15 -0
- package/dist/workflow/discovery.js +134 -0
- package/dist/workflow/discovery.js.map +1 -0
- package/dist/workflow/dsl.d.ts +13 -0
- package/dist/workflow/dsl.js +683 -0
- package/dist/workflow/dsl.js.map +1 -0
- package/dist/workflow/errors.d.ts +18 -0
- package/dist/workflow/errors.js +33 -0
- package/dist/workflow/errors.js.map +1 -0
- package/dist/workflow/invocation-artifacts.d.ts +42 -0
- package/dist/workflow/invocation-artifacts.js +114 -0
- package/dist/workflow/invocation-artifacts.js.map +1 -0
- package/dist/workflow/invocation-manager.d.ts +31 -0
- package/dist/workflow/invocation-manager.js +516 -0
- package/dist/workflow/invocation-manager.js.map +1 -0
- package/dist/workflow/invocation-types.d.ts +63 -0
- package/dist/workflow/invocation-types.js +15 -0
- package/dist/workflow/invocation-types.js.map +1 -0
- package/dist/workflow/json.d.ts +10 -0
- package/dist/workflow/json.js +117 -0
- package/dist/workflow/json.js.map +1 -0
- package/dist/workflow/load.d.ts +2 -0
- package/dist/workflow/load.js +18 -0
- package/dist/workflow/load.js.map +1 -0
- package/dist/workflow/parse.d.ts +2 -0
- package/dist/workflow/parse.js +135 -0
- package/dist/workflow/parse.js.map +1 -0
- package/dist/workflow/registry.d.ts +18 -0
- package/dist/workflow/registry.js +55 -0
- package/dist/workflow/registry.js.map +1 -0
- package/dist/workflow/resolve-target.d.ts +45 -0
- package/dist/workflow/resolve-target.js +162 -0
- package/dist/workflow/resolve-target.js.map +1 -0
- package/dist/workflow/runtime.d.ts +46 -0
- package/dist/workflow/runtime.js +398 -0
- package/dist/workflow/runtime.js.map +1 -0
- package/dist/workflow/sandbox.d.ts +14 -0
- package/dist/workflow/sandbox.js +79 -0
- package/dist/workflow/sandbox.js.map +1 -0
- package/dist/workflow/scope.d.ts +18 -0
- package/dist/workflow/scope.js +108 -0
- package/dist/workflow/scope.js.map +1 -0
- package/dist/workflow/types.d.ts +65 -0
- package/dist/workflow/types.js +2 -0
- package/dist/workflow/types.js.map +1 -0
- package/dist/workflow/validate.d.ts +28 -0
- package/dist/workflow/validate.js +1059 -0
- package/dist/workflow/validate.js.map +1 -0
- package/dist/workflow/workflow-call.d.ts +12 -0
- package/dist/workflow/workflow-call.js +61 -0
- package/dist/workflow/workflow-call.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,1059 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import AjvModule from "ajv";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { ErrorCode } from "../errors/codes.js";
|
|
5
|
+
import { OpenDynamicWorkflowError } from "../errors/types.js";
|
|
6
|
+
import { isPathLikeWorkflowName } from "./workflow-call.js";
|
|
7
|
+
const Ajv = AjvModule.default || AjvModule;
|
|
8
|
+
const ajv = new Ajv({ allErrors: true });
|
|
9
|
+
function isStaticValue(node) {
|
|
10
|
+
if (ts.isStringLiteral(node) || ts.isNumericLiteral(node) || node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword || node.kind === ts.SyntaxKind.NullKeyword) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(node.operand)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
17
|
+
return node.elements.every(isStaticValue);
|
|
18
|
+
}
|
|
19
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
20
|
+
return node.properties.every(prop => ts.isPropertyAssignment(prop) && isStaticValue(prop.initializer));
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
function parseStaticProperties(node) {
|
|
25
|
+
if (!node)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (ts.isStringLiteral(node)) {
|
|
28
|
+
return node.text;
|
|
29
|
+
}
|
|
30
|
+
if (ts.isNumericLiteral(node)) {
|
|
31
|
+
return Number(node.text);
|
|
32
|
+
}
|
|
33
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(node.operand)) {
|
|
34
|
+
return -Number(node.operand.text);
|
|
35
|
+
}
|
|
36
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
46
|
+
return node.elements.map(parseStaticProperties);
|
|
47
|
+
}
|
|
48
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
49
|
+
const obj = {};
|
|
50
|
+
for (const prop of node.properties) {
|
|
51
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
52
|
+
if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) {
|
|
53
|
+
const key = prop.name.text;
|
|
54
|
+
const val = parseStaticProperties(prop.initializer);
|
|
55
|
+
if (val !== undefined) {
|
|
56
|
+
obj[key] = val;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return obj;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
function getObjectLiteralProperty(node, name) {
|
|
66
|
+
for (const prop of node.properties) {
|
|
67
|
+
if (!ts.isPropertyAssignment(prop))
|
|
68
|
+
continue;
|
|
69
|
+
const propName = ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) ? prop.name.text : undefined;
|
|
70
|
+
if (propName === name)
|
|
71
|
+
return prop.initializer;
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
export function validateWorkflow(workflow, options) {
|
|
76
|
+
const issues = [];
|
|
77
|
+
const sourceFile = ts.createSourceFile(workflow.sourcePath, workflow.sourceText, ts.ScriptTarget.Latest, true);
|
|
78
|
+
// Find the exported default workflow function to get its context parameter name
|
|
79
|
+
const contextParameterNames = new Set(["ctx", "context"]);
|
|
80
|
+
for (const statement of sourceFile.statements) {
|
|
81
|
+
let workflowFn;
|
|
82
|
+
if (ts.isExportAssignment(statement)) {
|
|
83
|
+
const expr = statement.expression;
|
|
84
|
+
if (ts.isFunctionExpression(expr) || ts.isArrowFunction(expr)) {
|
|
85
|
+
workflowFn = expr;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (ts.isFunctionDeclaration(statement)) {
|
|
89
|
+
const isDefaultExport = statement.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
90
|
+
statement.modifiers?.some(m => m.kind === ts.SyntaxKind.DefaultKeyword);
|
|
91
|
+
if (isDefaultExport) {
|
|
92
|
+
workflowFn = statement;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (workflowFn && workflowFn.parameters.length > 0) {
|
|
96
|
+
const firstParam = workflowFn.parameters[0];
|
|
97
|
+
if (firstParam && ts.isIdentifier(firstParam.name)) {
|
|
98
|
+
contextParameterNames.add(firstParam.name.text);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function report(node, message, severity) {
|
|
103
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
104
|
+
issues.push({
|
|
105
|
+
code: "WORKFLOW_VALIDATION_ERROR",
|
|
106
|
+
message,
|
|
107
|
+
line: line + 1,
|
|
108
|
+
column: character + 1,
|
|
109
|
+
severity
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Validate metadata inputSchema
|
|
113
|
+
if (workflow.meta.inputSchema) {
|
|
114
|
+
try {
|
|
115
|
+
ajv.compile(workflow.meta.inputSchema);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
report(sourceFile, `Metadata 'inputSchema' is not a valid JSON Schema: ${err.message}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const knownSharedAgentIds = options.knownSharedAgentIds ?? (options.sharedAgentRegistry
|
|
122
|
+
? new Set(options.sharedAgentRegistry.list().map(entry => entry.id))
|
|
123
|
+
: undefined);
|
|
124
|
+
function validateSharedAgentId(idArg) {
|
|
125
|
+
if (!idArg) {
|
|
126
|
+
report(sourceFile, "Shared agent requires at least a shared agent ID.");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (ts.isStringLiteral(idArg)) {
|
|
130
|
+
const id = idArg.text;
|
|
131
|
+
if (id.startsWith(".") || id.startsWith("/") || id.includes("/") || id.includes("\\")) {
|
|
132
|
+
report(idArg, "Shared agent definition references must use a registry ID, not a path.");
|
|
133
|
+
}
|
|
134
|
+
else if (knownSharedAgentIds && !knownSharedAgentIds.has(id)) {
|
|
135
|
+
report(idArg, `Shared agent '${id}' was not found in the configured registry.`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (options.allowDynamicSharedAgentIds === false) {
|
|
139
|
+
report(idArg, "Shared agent ID must be a string literal.");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function validateSharedAgentInput(idArg, inputArg, isDefinitionForm = false) {
|
|
143
|
+
if (!options.sharedAgentRegistry || !idArg || !ts.isStringLiteral(idArg)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const id = idArg.text;
|
|
147
|
+
const entry = options.sharedAgentRegistry.get(id);
|
|
148
|
+
if (!entry || !entry.definition.inputSchema) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const schema = entry.definition.inputSchema;
|
|
152
|
+
let parsedInput = parseStaticProperties(inputArg);
|
|
153
|
+
if (parsedInput === undefined) {
|
|
154
|
+
if (!inputArg) {
|
|
155
|
+
try {
|
|
156
|
+
const validate = ajv.compile(schema);
|
|
157
|
+
const valid = validate({});
|
|
158
|
+
if (!valid && validate.errors) {
|
|
159
|
+
const hasRequired = validate.errors.some((e) => e.keyword === "required");
|
|
160
|
+
if (hasRequired) {
|
|
161
|
+
report(sourceFile, `Shared agent '${id}' requires input matching schema.`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) { }
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const validate = ajv.compile(schema);
|
|
171
|
+
const valid = validate(parsedInput);
|
|
172
|
+
if (!valid && validate.errors) {
|
|
173
|
+
let hasDynamicProps = false;
|
|
174
|
+
if (inputArg && ts.isObjectLiteralExpression(inputArg)) {
|
|
175
|
+
for (const prop of inputArg.properties) {
|
|
176
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
177
|
+
if (!isStaticValue(prop.initializer)) {
|
|
178
|
+
hasDynamicProps = true;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
hasDynamicProps = true;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const error of validate.errors) {
|
|
189
|
+
if (error.keyword === "required" && hasDynamicProps) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const path = error.instancePath ? ` at ${error.instancePath}` : "";
|
|
193
|
+
report(inputArg || sourceFile, `Shared agent '${id}' input validation failed: ${error.message}${path}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (err) { }
|
|
198
|
+
}
|
|
199
|
+
function validateInputAgainstSchema(name, schema, argsExpr) {
|
|
200
|
+
if (argsExpr === undefined) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const parsedArgs = parseStaticProperties(argsExpr);
|
|
204
|
+
if (parsedArgs === undefined) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const validate = ajv.compile(schema);
|
|
209
|
+
const valid = validate(parsedArgs);
|
|
210
|
+
if (!valid && validate.errors) {
|
|
211
|
+
let hasDynamicProps = false;
|
|
212
|
+
if (argsExpr && ts.isObjectLiteralExpression(argsExpr)) {
|
|
213
|
+
for (const prop of argsExpr.properties) {
|
|
214
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
215
|
+
if (!isStaticValue(prop.initializer)) {
|
|
216
|
+
hasDynamicProps = true;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
hasDynamicProps = true;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const error of validate.errors) {
|
|
227
|
+
if (error.keyword === "required" && hasDynamicProps) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const path = error.instancePath ? ` at ${error.instancePath}` : "";
|
|
231
|
+
report(argsExpr || sourceFile, `Workflow '${name}' input validation failed: ${error.message}${path}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (err) { }
|
|
236
|
+
}
|
|
237
|
+
function validateWorkflowCall(node, isContextForm, contextName = "workflow") {
|
|
238
|
+
const firstArg = node.arguments[0];
|
|
239
|
+
const callPrefix = isContextForm ? `${contextName}.workflow()` : "workflow()";
|
|
240
|
+
if (!firstArg) {
|
|
241
|
+
report(node, `${callPrefix} requires an object literal argument.`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (!ts.isObjectLiteralExpression(firstArg)) {
|
|
245
|
+
report(firstArg, `${callPrefix} argument must be an object literal.`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const allowedWorkflowCallKeys = new Set([
|
|
249
|
+
"name",
|
|
250
|
+
"args",
|
|
251
|
+
"failureMode",
|
|
252
|
+
"timeoutMs",
|
|
253
|
+
"concurrency",
|
|
254
|
+
"metadata"
|
|
255
|
+
]);
|
|
256
|
+
for (const prop of firstArg.properties) {
|
|
257
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
258
|
+
report(prop, `${callPrefix} does not support spread properties.`);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
262
|
+
report(prop, `${callPrefix} does not support shorthand or method properties.`);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (!ts.isIdentifier(prop.name) && !ts.isStringLiteral(prop.name)) {
|
|
266
|
+
report(prop.name, `${callPrefix} does not support computed property names.`);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const key = prop.name.text;
|
|
270
|
+
if (!allowedWorkflowCallKeys.has(key)) {
|
|
271
|
+
report(prop.name, `${callPrefix} contains unsupported key '${key}'.`);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const init = prop.initializer;
|
|
275
|
+
if (key === "failureMode") {
|
|
276
|
+
if (ts.isStringLiteral(init)) {
|
|
277
|
+
if (init.text !== "throw" && init.text !== "settled") {
|
|
278
|
+
report(init, `failureMode must be 'throw' or 'settled'.`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else if (key === "timeoutMs" || key === "concurrency") {
|
|
283
|
+
if (ts.isNumericLiteral(init)) {
|
|
284
|
+
const val = Number(init.text);
|
|
285
|
+
if (!Number.isInteger(val) || val <= 0) {
|
|
286
|
+
report(init, `${key} must be a positive integer.`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else if (isStaticValue(init)) {
|
|
290
|
+
report(init, `${key} must be a positive integer.`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
else if (key === "metadata") {
|
|
294
|
+
if (isStaticValue(init) && !ts.isObjectLiteralExpression(init)) {
|
|
295
|
+
report(init, "metadata must be an object.");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const nameExpr = getObjectLiteralProperty(firstArg, "name");
|
|
300
|
+
const argsExpr = getObjectLiteralProperty(firstArg, "args");
|
|
301
|
+
if (!nameExpr) {
|
|
302
|
+
report(firstArg, `${callPrefix} is missing required 'name' property.`);
|
|
303
|
+
}
|
|
304
|
+
else if (ts.isStringLiteral(nameExpr)) {
|
|
305
|
+
const name = nameExpr.text;
|
|
306
|
+
if (isPathLikeWorkflowName(name)) {
|
|
307
|
+
report(nameExpr, "Workflow names must not be path-like.");
|
|
308
|
+
}
|
|
309
|
+
else if (options.knownWorkflowNames && !options.knownWorkflowNames.has(name)) {
|
|
310
|
+
report(nameExpr, `Workflow '${name}' was not found in the registry.`);
|
|
311
|
+
}
|
|
312
|
+
else if (options.workflowInputSchemas) {
|
|
313
|
+
const schema = options.workflowInputSchemas.get(name);
|
|
314
|
+
if (schema) {
|
|
315
|
+
validateInputAgainstSchema(name, schema, argsExpr);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function validateToolCall(node, isContextForm, isForbiddenContext, contextName = "ctx") {
|
|
321
|
+
const firstArg = node.arguments[0];
|
|
322
|
+
const callPrefix = isContextForm ? `${contextName}.tool()` : "tool()";
|
|
323
|
+
if (isForbiddenContext) {
|
|
324
|
+
report(node, `${callPrefix} is not allowed in this context (parallel, pipeline stage, or shared agent).`);
|
|
325
|
+
}
|
|
326
|
+
if (!firstArg) {
|
|
327
|
+
report(node, `${callPrefix} requires an object literal argument.`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (!ts.isObjectLiteralExpression(firstArg)) {
|
|
331
|
+
report(firstArg, `${callPrefix} argument must be an object literal.`);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const allowedToolCallKeys = new Set([
|
|
335
|
+
"definition",
|
|
336
|
+
"args",
|
|
337
|
+
"id",
|
|
338
|
+
"label",
|
|
339
|
+
"timeoutMs",
|
|
340
|
+
"failureMode",
|
|
341
|
+
"metadata"
|
|
342
|
+
]);
|
|
343
|
+
for (const prop of firstArg.properties) {
|
|
344
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
345
|
+
report(prop, `${callPrefix} does not support spread properties.`);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
349
|
+
report(prop, `${callPrefix} does not support shorthand or method properties.`);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (!ts.isIdentifier(prop.name) && !ts.isStringLiteral(prop.name)) {
|
|
353
|
+
report(prop.name, `${callPrefix} does not support computed property names.`);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const key = prop.name.text;
|
|
357
|
+
if (!allowedToolCallKeys.has(key)) {
|
|
358
|
+
report(prop.name, `${callPrefix} contains unsupported key '${key}'.`);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const init = prop.initializer;
|
|
362
|
+
if (key === "failureMode") {
|
|
363
|
+
if (ts.isStringLiteral(init)) {
|
|
364
|
+
if (init.text !== "throw" && init.text !== "settled") {
|
|
365
|
+
report(init, `failureMode must be 'throw' or 'settled'.`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else if (key === "timeoutMs") {
|
|
370
|
+
if (ts.isNumericLiteral(init)) {
|
|
371
|
+
const val = Number(init.text);
|
|
372
|
+
if (!Number.isInteger(val) || val <= 0) {
|
|
373
|
+
report(init, `${key} must be a positive integer.`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else if (isStaticValue(init)) {
|
|
377
|
+
report(init, `${key} must be a positive integer.`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else if (key === "metadata") {
|
|
381
|
+
if (isStaticValue(init) && !ts.isObjectLiteralExpression(init)) {
|
|
382
|
+
report(init, "metadata must be an object.");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const definitionExpr = getObjectLiteralProperty(firstArg, "definition");
|
|
387
|
+
const argsExpr = getObjectLiteralProperty(firstArg, "args");
|
|
388
|
+
if (!definitionExpr) {
|
|
389
|
+
report(firstArg, `${callPrefix} is missing required 'definition' property.`);
|
|
390
|
+
}
|
|
391
|
+
else if (ts.isStringLiteral(definitionExpr)) {
|
|
392
|
+
const definition = definitionExpr.text;
|
|
393
|
+
if (definition.trim() === "") {
|
|
394
|
+
report(definitionExpr, "Tool definition must not be empty.");
|
|
395
|
+
}
|
|
396
|
+
else if (definition.includes("/") || definition.includes("\\")) {
|
|
397
|
+
report(definitionExpr, "Tool definition must be a registry ID, not a path.");
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
const knownToolIds = options.knownToolIds ?? (options.toolRegistry
|
|
401
|
+
? new Set(options.toolRegistry.list().map(t => t.definition.id))
|
|
402
|
+
: undefined);
|
|
403
|
+
if (knownToolIds && !knownToolIds.has(definition)) {
|
|
404
|
+
report(definitionExpr, `Tool '${definition}' was not found in the registry.`);
|
|
405
|
+
}
|
|
406
|
+
else if (options.toolRegistry) {
|
|
407
|
+
const toolDef = options.toolRegistry.get(definition);
|
|
408
|
+
if (toolDef && toolDef.definition.inputSchema) {
|
|
409
|
+
validateInputAgainstSchema(definition, toolDef.definition.inputSchema, argsExpr);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
report(definitionExpr, "Tool definition must be a string literal.");
|
|
416
|
+
}
|
|
417
|
+
if (!argsExpr) {
|
|
418
|
+
report(firstArg, `${callPrefix} is missing required 'args' property.`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function validateAgentCall(node, isContextForm, contextName = "ctx") {
|
|
422
|
+
const firstArg = node.arguments[0];
|
|
423
|
+
const callPrefix = isContextForm ? `${contextName}.agent()` : "agent()";
|
|
424
|
+
if (!firstArg) {
|
|
425
|
+
report(node, `${callPrefix} requires an object literal argument.`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (!ts.isObjectLiteralExpression(firstArg)) {
|
|
429
|
+
report(firstArg, `${callPrefix} argument must be an object literal.`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
let definitionProp;
|
|
433
|
+
let promptProp;
|
|
434
|
+
let hasSpread = false;
|
|
435
|
+
for (const prop of firstArg.properties) {
|
|
436
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
437
|
+
hasSpread = true;
|
|
438
|
+
}
|
|
439
|
+
else if (ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)) {
|
|
440
|
+
const propName = ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)
|
|
441
|
+
? (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) ? prop.name.text : prop.name.getText())
|
|
442
|
+
: "";
|
|
443
|
+
if (propName === "definition") {
|
|
444
|
+
definitionProp = prop;
|
|
445
|
+
}
|
|
446
|
+
else if (propName === "prompt") {
|
|
447
|
+
promptProp = prop;
|
|
448
|
+
}
|
|
449
|
+
if (propName === "permissions" && ts.isPropertyAssignment(prop)) {
|
|
450
|
+
const init = prop.initializer;
|
|
451
|
+
if (ts.isObjectLiteralExpression(init)) {
|
|
452
|
+
let hasMode = false;
|
|
453
|
+
let modeValue;
|
|
454
|
+
let hasDynamicProp = false;
|
|
455
|
+
const allowedKeys = ["mode"];
|
|
456
|
+
for (const innerProp of init.properties) {
|
|
457
|
+
if (ts.isPropertyAssignment(innerProp)) {
|
|
458
|
+
const innerName = ts.isIdentifier(innerProp.name) || ts.isStringLiteral(innerProp.name) ? innerProp.name.text : innerProp.name.getText();
|
|
459
|
+
if (!allowedKeys.includes(innerName)) {
|
|
460
|
+
report(innerProp, `${callPrefix} permissions contain unsupported key '${innerName}'.`);
|
|
461
|
+
}
|
|
462
|
+
if (innerName === "mode") {
|
|
463
|
+
hasMode = true;
|
|
464
|
+
const val = innerProp.initializer;
|
|
465
|
+
if (ts.isStringLiteral(val)) {
|
|
466
|
+
modeValue = val.text;
|
|
467
|
+
}
|
|
468
|
+
else if (ts.isNumericLiteral(val) ||
|
|
469
|
+
ts.isBigIntLiteral(val) ||
|
|
470
|
+
ts.isObjectLiteralExpression(val) ||
|
|
471
|
+
ts.isArrayLiteralExpression(val) ||
|
|
472
|
+
val.kind === ts.SyntaxKind.TrueKeyword ||
|
|
473
|
+
val.kind === ts.SyntaxKind.FalseKeyword ||
|
|
474
|
+
val.kind === ts.SyntaxKind.NullKeyword) {
|
|
475
|
+
report(val, `${callPrefix} permissions.mode must be a string literal.`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
else if (ts.isShorthandPropertyAssignment(innerProp)) {
|
|
480
|
+
const innerName = innerProp.name.text;
|
|
481
|
+
if (!allowedKeys.includes(innerName)) {
|
|
482
|
+
report(innerProp, `${callPrefix} permissions contain unsupported key '${innerName}'.`);
|
|
483
|
+
}
|
|
484
|
+
if (innerName === "mode") {
|
|
485
|
+
hasMode = true;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
hasDynamicProp = true;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (!hasMode && !hasDynamicProp) {
|
|
493
|
+
report(init, `${callPrefix} permissions must include a 'mode' property.`);
|
|
494
|
+
}
|
|
495
|
+
else if (hasMode && modeValue !== undefined && modeValue !== "dangerously-full-access") {
|
|
496
|
+
report(init, `${callPrefix} permissions.mode must be 'dangerously-full-access'.`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
else if (ts.isStringLiteral(init) ||
|
|
500
|
+
ts.isNumericLiteral(init) ||
|
|
501
|
+
ts.isBigIntLiteral(init) ||
|
|
502
|
+
ts.isArrayLiteralExpression(init) ||
|
|
503
|
+
init.kind === ts.SyntaxKind.TrueKeyword ||
|
|
504
|
+
init.kind === ts.SyntaxKind.FalseKeyword ||
|
|
505
|
+
init.kind === ts.SyntaxKind.NullKeyword) {
|
|
506
|
+
report(init, `${callPrefix} permissions must be an object literal.`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (definitionProp) {
|
|
512
|
+
const definitionArg = ts.isPropertyAssignment(definitionProp) ? definitionProp.initializer : undefined;
|
|
513
|
+
validateSharedAgentId(definitionArg);
|
|
514
|
+
validateSharedAgentInput(definitionArg, firstArg, true);
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
if (!promptProp && !hasSpread) {
|
|
518
|
+
report(firstArg, `${callPrefix} is missing required 'prompt' property.`);
|
|
519
|
+
}
|
|
520
|
+
else if (promptProp && ts.isPropertyAssignment(promptProp)) {
|
|
521
|
+
const init = promptProp.initializer;
|
|
522
|
+
if (ts.isStringLiteral(init)) {
|
|
523
|
+
if (init.text.trim() === "") {
|
|
524
|
+
report(init, `${callPrefix} prompt cannot be empty.`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
else if (ts.isNumericLiteral(init) ||
|
|
528
|
+
ts.isBigIntLiteral(init) ||
|
|
529
|
+
ts.isObjectLiteralExpression(init) ||
|
|
530
|
+
ts.isArrayLiteralExpression(init) ||
|
|
531
|
+
init.kind === ts.SyntaxKind.TrueKeyword ||
|
|
532
|
+
init.kind === ts.SyntaxKind.FalseKeyword ||
|
|
533
|
+
init.kind === ts.SyntaxKind.NullKeyword) {
|
|
534
|
+
report(init, `${callPrefix} prompt must be a string literal.`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function isToolOrCtxTool(node) {
|
|
540
|
+
// direct tool
|
|
541
|
+
if (ts.isIdentifier(node) && node.text === "tool")
|
|
542
|
+
return true;
|
|
543
|
+
// ctx.tool
|
|
544
|
+
if (ts.isPropertyAccessExpression(node) &&
|
|
545
|
+
ts.isIdentifier(node.expression) && contextParameterNames.has(node.expression.text) &&
|
|
546
|
+
node.name.text === "tool") {
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
// ctx["tool"]
|
|
550
|
+
if (ts.isElementAccessExpression(node) &&
|
|
551
|
+
ts.isIdentifier(node.expression) && contextParameterNames.has(node.expression.text) &&
|
|
552
|
+
ts.isStringLiteral(node.argumentExpression) && node.argumentExpression.text === "tool") {
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
// tool.bind, tool.call, tool.apply (PropertyAccess)
|
|
556
|
+
// OR tool.bind(null) (CallExpression)
|
|
557
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
558
|
+
const name = node.name.text;
|
|
559
|
+
if (name === "bind" || name === "call" || name === "apply") {
|
|
560
|
+
if (isToolOrCtxTool(node.expression))
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (ts.isCallExpression(node)) {
|
|
565
|
+
if (isToolOrCtxTool(node.expression))
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
function isLikelyWorkflowContext(node) {
|
|
571
|
+
if (ts.isIdentifier(node) && contextParameterNames.has(node.text))
|
|
572
|
+
return true;
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
function checkBindingForToolAlias(name, initializer) {
|
|
576
|
+
if (ts.isObjectBindingPattern(name)) {
|
|
577
|
+
for (const element of name.elements) {
|
|
578
|
+
const propName = element.propertyName ? (ts.isIdentifier(element.propertyName) ? element.propertyName.text : undefined) : (ts.isIdentifier(element.name) ? element.name.text : undefined);
|
|
579
|
+
if (propName === "tool") {
|
|
580
|
+
// If we have an initializer, check if it's the context.
|
|
581
|
+
// If no initializer (like in parameter), we assume it's aliasing if the parameter looks like a context.
|
|
582
|
+
if (!initializer || isLikelyWorkflowContext(initializer)) {
|
|
583
|
+
report(element, "Aliasing tool() is not allowed. Use it directly as tool() or ctx.tool().");
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (element.name && ts.isObjectBindingPattern(element.name)) {
|
|
587
|
+
checkBindingForToolAlias(element.name, initializer);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function visit(node, isForbiddenContext = false, functionDepth = 0) {
|
|
593
|
+
// Skip the metadata declaration statement (export const meta = { ... })
|
|
594
|
+
if (sourceFile.statements.length > 0 && node === sourceFile.statements[0]) {
|
|
595
|
+
if (ts.isVariableStatement(node) && node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
596
|
+
const firstDecl = node.declarationList.declarations[0];
|
|
597
|
+
if (firstDecl && ts.isIdentifier(firstDecl.name) && firstDecl.name.text === "meta") {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node)) {
|
|
603
|
+
report(node, "Arbitrary imports are not allowed.");
|
|
604
|
+
}
|
|
605
|
+
let nextForbiddenContext = isForbiddenContext;
|
|
606
|
+
let nextFunctionDepth = functionDepth;
|
|
607
|
+
if (ts.isVariableDeclaration(node)) {
|
|
608
|
+
const init = node.initializer;
|
|
609
|
+
if (init) {
|
|
610
|
+
if (isToolOrCtxTool(init)) {
|
|
611
|
+
report(node, "Aliasing tool() is not allowed. Use it directly as tool() or ctx.tool().");
|
|
612
|
+
}
|
|
613
|
+
checkBindingForToolAlias(node.name, init);
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
checkBindingForToolAlias(node.name);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (ts.isParameter(node)) {
|
|
620
|
+
checkBindingForToolAlias(node.name);
|
|
621
|
+
}
|
|
622
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
623
|
+
const rhs = node.right;
|
|
624
|
+
if (isToolOrCtxTool(rhs)) {
|
|
625
|
+
report(node, "Aliasing tool() is not allowed. Use it directly as tool() or ctx.tool().");
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node)) {
|
|
629
|
+
nextFunctionDepth++;
|
|
630
|
+
if (nextFunctionDepth > 1) {
|
|
631
|
+
nextForbiddenContext = true;
|
|
632
|
+
}
|
|
633
|
+
else if (nextFunctionDepth === 1) {
|
|
634
|
+
// Only the default exported function (the main workflow) is allowed to contain tools.
|
|
635
|
+
let isMainWorkflow = false;
|
|
636
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
637
|
+
isMainWorkflow = !!(node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
638
|
+
node.modifiers?.some(m => m.kind === ts.SyntaxKind.DefaultKeyword));
|
|
639
|
+
}
|
|
640
|
+
else if (ts.isFunctionExpression(node) || ts.isArrowFunction(node)) {
|
|
641
|
+
// Check if it's the expression of an export default
|
|
642
|
+
isMainWorkflow = ts.isExportAssignment(node.parent);
|
|
643
|
+
}
|
|
644
|
+
if (!isMainWorkflow) {
|
|
645
|
+
nextForbiddenContext = true;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (ts.isCallExpression(node)) {
|
|
650
|
+
const callee = node.expression;
|
|
651
|
+
// Reject tool being passed as an argument
|
|
652
|
+
for (const arg of node.arguments) {
|
|
653
|
+
if (isToolOrCtxTool(arg)) {
|
|
654
|
+
report(arg, "Aliasing tool() is not allowed. Use it directly as tool() or ctx.tool().");
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (ts.isIdentifier(callee)) {
|
|
658
|
+
const calleeText = callee.text;
|
|
659
|
+
if (calleeText === "require") {
|
|
660
|
+
report(node, "require() is not supported. Direct module access is not allowed.");
|
|
661
|
+
}
|
|
662
|
+
else if (calleeText === "shell") {
|
|
663
|
+
report(node, "shell() is not supported in the MVP.");
|
|
664
|
+
}
|
|
665
|
+
else if (calleeText === "pipeline") {
|
|
666
|
+
if (node.arguments.length < 2) {
|
|
667
|
+
report(node, "pipeline() requires at least 2 arguments: items and stages.");
|
|
668
|
+
}
|
|
669
|
+
else if (node.arguments.length > 3) {
|
|
670
|
+
report(node, "pipeline() accepts at most 3 arguments: items, stages, and options.");
|
|
671
|
+
}
|
|
672
|
+
const stagesArg = node.arguments[1];
|
|
673
|
+
if (stagesArg) {
|
|
674
|
+
if (ts.isArrayLiteralExpression(stagesArg)) {
|
|
675
|
+
const stageNamesSeen = new Set();
|
|
676
|
+
for (const element of stagesArg.elements) {
|
|
677
|
+
if (!ts.isObjectLiteralExpression(element)) {
|
|
678
|
+
report(element, "pipeline() stages must be named stage objects, not function shorthands. Recommend using { name: 'stageName', run: ... }");
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
let hasNameProp = false;
|
|
682
|
+
let nameValue;
|
|
683
|
+
for (const prop of element.properties) {
|
|
684
|
+
if (ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop) || ts.isSpreadAssignment(prop) || ts.isMethodDeclaration(prop)) {
|
|
685
|
+
const propName = ts.isPropertyAssignment(prop) || ts.isMethodDeclaration(prop)
|
|
686
|
+
? (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) ? prop.name.text : prop.name.getText())
|
|
687
|
+
: ts.isShorthandPropertyAssignment(prop) ? prop.name.text : "";
|
|
688
|
+
if (propName === "name") {
|
|
689
|
+
hasNameProp = true;
|
|
690
|
+
if (ts.isPropertyAssignment(prop) && ts.isStringLiteral(prop.initializer)) {
|
|
691
|
+
nameValue = prop.initializer.text;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (propName === "run") {
|
|
695
|
+
const runInit = ts.isPropertyAssignment(prop) ? prop.initializer : (ts.isMethodDeclaration(prop) ? prop : undefined);
|
|
696
|
+
if (runInit) {
|
|
697
|
+
visit(runInit, true, nextFunctionDepth);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (!hasNameProp) {
|
|
703
|
+
report(element, "pipeline() stage object is missing 'name' property.");
|
|
704
|
+
}
|
|
705
|
+
else if (nameValue !== undefined) {
|
|
706
|
+
if (stageNamesSeen.has(nameValue)) {
|
|
707
|
+
report(element, `pipeline() duplicate stage name detected: '${nameValue}'.`);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
stageNamesSeen.add(nameValue);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
else if (ts.isArrowFunction(stagesArg) || ts.isFunctionExpression(stagesArg)) {
|
|
717
|
+
report(stagesArg, "pipeline() stages must be named stage objects, not function shorthands. Recommend using { name: 'stageName', run: ... }");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const optionsArg = node.arguments[2];
|
|
721
|
+
if (optionsArg && ts.isObjectLiteralExpression(optionsArg)) {
|
|
722
|
+
const allowedOptionKeys = ["label", "strategy", "concurrency", "stageConcurrency", "preserveOrder", "failFast"];
|
|
723
|
+
for (const prop of optionsArg.properties) {
|
|
724
|
+
if (ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop) || ts.isSpreadAssignment(prop) || ts.isMethodDeclaration(prop)) {
|
|
725
|
+
const propName = ts.isPropertyAssignment(prop) || ts.isMethodDeclaration(prop)
|
|
726
|
+
? (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) ? prop.name.text : prop.name.getText())
|
|
727
|
+
: ts.isShorthandPropertyAssignment(prop) ? prop.name.text : "";
|
|
728
|
+
if (propName && !allowedOptionKeys.includes(propName)) {
|
|
729
|
+
report(prop, `pipeline() options contain unsupported key '${propName}'.`);
|
|
730
|
+
}
|
|
731
|
+
if (propName === "strategy" && ts.isPropertyAssignment(prop) && ts.isStringLiteral(prop.initializer)) {
|
|
732
|
+
const strategyVal = prop.initializer.text;
|
|
733
|
+
if (strategyVal !== "item-streaming" && strategyVal !== "stage-barrier") {
|
|
734
|
+
report(prop.initializer, `pipeline() options strategy must be 'item-streaming' or 'stage-barrier'.`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
// Manual recursion to handle forbidden context for stages
|
|
741
|
+
node.arguments.forEach((arg, idx) => {
|
|
742
|
+
if (idx === 1) {
|
|
743
|
+
// stagesArg and its children (the stage objects and their 'run' methods)
|
|
744
|
+
// need to recursively forbid tools.
|
|
745
|
+
visit(arg, true, nextFunctionDepth);
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
visit(arg, nextForbiddenContext, nextFunctionDepth);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
else if (calleeText === "parallel") {
|
|
754
|
+
nextForbiddenContext = true;
|
|
755
|
+
}
|
|
756
|
+
else if (calleeText === "defineAgent") {
|
|
757
|
+
const firstArg = node.arguments[0];
|
|
758
|
+
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
759
|
+
visit(firstArg, true, nextFunctionDepth);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
else if (calleeText === "agent") {
|
|
764
|
+
validateAgentCall(node, false);
|
|
765
|
+
}
|
|
766
|
+
else if (calleeText === "workflow") {
|
|
767
|
+
validateWorkflowCall(node, false);
|
|
768
|
+
}
|
|
769
|
+
else if (calleeText === "tool") {
|
|
770
|
+
validateToolCall(node, false, nextForbiddenContext);
|
|
771
|
+
}
|
|
772
|
+
else if (["read", "write"].includes(calleeText)) {
|
|
773
|
+
report(node, `${calleeText}() is not supported in the MVP.`);
|
|
774
|
+
}
|
|
775
|
+
else if (calleeText === "fetch") {
|
|
776
|
+
report(node, "Network APIs are not part of MVP workflow capabilities.");
|
|
777
|
+
}
|
|
778
|
+
else if (calleeText === "Function") {
|
|
779
|
+
report(node, "Dynamic function creation is not allowed.");
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else if (ts.isPropertyAccessExpression(callee)) {
|
|
783
|
+
const obj = callee.expression;
|
|
784
|
+
const prop = callee.name;
|
|
785
|
+
if (ts.isIdentifier(obj) && contextParameterNames.has(obj.text)) {
|
|
786
|
+
if (prop.text === "agent") {
|
|
787
|
+
validateAgentCall(node, true, obj.text);
|
|
788
|
+
}
|
|
789
|
+
else if (prop.text === "workflow") {
|
|
790
|
+
validateWorkflowCall(node, true, obj.text);
|
|
791
|
+
}
|
|
792
|
+
else if (prop.text === "tool") {
|
|
793
|
+
validateToolCall(node, true, nextForbiddenContext, obj.text);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
else if (ts.isElementAccessExpression(callee)) {
|
|
798
|
+
const obj = callee.expression;
|
|
799
|
+
const arg = callee.argumentExpression;
|
|
800
|
+
if (ts.isIdentifier(obj) && contextParameterNames.has(obj.text) && ts.isStringLiteral(arg)) {
|
|
801
|
+
const propName = arg.text;
|
|
802
|
+
if (["agent", "workflow", "tool"].includes(propName)) {
|
|
803
|
+
report(node, `Computed access forms like ${obj.text}["${propName}"]() are not allowed. Use direct property access like ${obj.text}.${propName}() instead.`);
|
|
804
|
+
if (propName === "agent") {
|
|
805
|
+
validateAgentCall(node, true, obj.text);
|
|
806
|
+
}
|
|
807
|
+
else if (propName === "workflow") {
|
|
808
|
+
validateWorkflowCall(node, true, obj.text);
|
|
809
|
+
}
|
|
810
|
+
else if (propName === "tool") {
|
|
811
|
+
validateToolCall(node, true, nextForbiddenContext, obj.text);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (callee.kind === ts.SyntaxKind.ImportKeyword) {
|
|
817
|
+
report(node, "Arbitrary imports are not allowed.");
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (ts.isNewExpression(node)) {
|
|
821
|
+
const expression = node.expression;
|
|
822
|
+
if (ts.isIdentifier(expression) && expression.text === "Date" && (!node.arguments || node.arguments.length === 0)) {
|
|
823
|
+
report(node, "Avoid new Date(): it prevents deterministic resume/cache behavior. Use tool() instead.", "warning");
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
827
|
+
const expr = node.expression;
|
|
828
|
+
const name = node.name;
|
|
829
|
+
if (name.text === "constructor") {
|
|
830
|
+
report(node, "Access to 'constructor' is not allowed.");
|
|
831
|
+
}
|
|
832
|
+
else if (name.text === "__proto__") {
|
|
833
|
+
report(node, "Access to '__proto__' is not allowed.");
|
|
834
|
+
}
|
|
835
|
+
else if (ts.isIdentifier(expr)) {
|
|
836
|
+
if (expr.text === "Date" && name.text === "now") {
|
|
837
|
+
report(node, "Avoid Date.now(): it prevents deterministic resume/cache behavior. Use tool() instead.", "warning");
|
|
838
|
+
}
|
|
839
|
+
else if (expr.text === "Math" && name.text === "random") {
|
|
840
|
+
report(node, "Avoid Math.random(): it prevents deterministic resume/cache behavior. Use tool() instead.", "warning");
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (ts.isElementAccessExpression(node)) {
|
|
845
|
+
const arg = node.argumentExpression;
|
|
846
|
+
if (ts.isStringLiteral(arg)) {
|
|
847
|
+
if (arg.text === "constructor") {
|
|
848
|
+
report(node, "Access to 'constructor' is not allowed.");
|
|
849
|
+
}
|
|
850
|
+
else if (arg.text === "__proto__") {
|
|
851
|
+
report(node, "Access to '__proto__' is not allowed.");
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (ts.isIdentifier(node)) {
|
|
856
|
+
const text = node.text;
|
|
857
|
+
const isPropertyName = ts.isPropertyAccessExpression(node.parent) && node.parent.name === node;
|
|
858
|
+
const isPropertyAssignmentName = ts.isPropertyAssignment(node.parent) && node.parent.name === node;
|
|
859
|
+
if (!isPropertyName && !isPropertyAssignmentName) {
|
|
860
|
+
if (text === "process") {
|
|
861
|
+
report(node, "Direct process access is not allowed.");
|
|
862
|
+
}
|
|
863
|
+
else if (text === "fs") {
|
|
864
|
+
report(node, "Direct module access is not allowed.");
|
|
865
|
+
}
|
|
866
|
+
else if (text === "child_process") {
|
|
867
|
+
report(node, "Shell/process spawning is not allowed.");
|
|
868
|
+
}
|
|
869
|
+
else if (text === "globalThis" || text === "global" || text === "window" || text === "self") {
|
|
870
|
+
report(node, "Global object access is not allowed.");
|
|
871
|
+
}
|
|
872
|
+
else if (text === "Function") {
|
|
873
|
+
report(node, "Dynamic function creation is not allowed.");
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
ts.forEachChild(node, (child) => visit(child, nextForbiddenContext, nextFunctionDepth));
|
|
878
|
+
}
|
|
879
|
+
visit(sourceFile);
|
|
880
|
+
return issues;
|
|
881
|
+
}
|
|
882
|
+
export function assertWorkflowValid(workflow, options) {
|
|
883
|
+
const issues = validateWorkflow(workflow, options);
|
|
884
|
+
const errors = issues.filter((issue) => issue.severity !== "warning");
|
|
885
|
+
const warnings = issues.filter((issue) => issue.severity === "warning");
|
|
886
|
+
if (warnings.length > 0) {
|
|
887
|
+
for (const warning of warnings) {
|
|
888
|
+
console.warn(`Warning: ${warning.message} (at line ${warning.line}, col ${warning.column})`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (errors.length > 0) {
|
|
892
|
+
const summary = errors.map((issue) => `${issue.message}`).join("\n");
|
|
893
|
+
throw new OpenDynamicWorkflowError(ErrorCode.WORKFLOW_VALIDATION_ERROR, `Workflow validation failed:\n${summary}`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
export function extractWorkflowDependencies(parsed) {
|
|
897
|
+
const dependencies = [];
|
|
898
|
+
const sourceFile = ts.createSourceFile(parsed.sourcePath, parsed.sourceText, ts.ScriptTarget.Latest, true);
|
|
899
|
+
// Find the exported default workflow function to get its context parameter name
|
|
900
|
+
const contextParameterNames = new Set(["ctx", "context"]);
|
|
901
|
+
for (const statement of sourceFile.statements) {
|
|
902
|
+
let workflowFn;
|
|
903
|
+
if (ts.isExportAssignment(statement)) {
|
|
904
|
+
const expr = statement.expression;
|
|
905
|
+
if (ts.isFunctionExpression(expr) || ts.isArrowFunction(expr)) {
|
|
906
|
+
workflowFn = expr;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
else if (ts.isFunctionDeclaration(statement)) {
|
|
910
|
+
const isDefaultExport = statement.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
911
|
+
statement.modifiers?.some(m => m.kind === ts.SyntaxKind.DefaultKeyword);
|
|
912
|
+
if (isDefaultExport) {
|
|
913
|
+
workflowFn = statement;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
if (workflowFn && workflowFn.parameters.length > 0) {
|
|
917
|
+
const firstParam = workflowFn.parameters[0];
|
|
918
|
+
if (firstParam && ts.isIdentifier(firstParam.name)) {
|
|
919
|
+
contextParameterNames.add(firstParam.name.text);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
function visit(node) {
|
|
924
|
+
if (ts.isCallExpression(node)) {
|
|
925
|
+
const callee = node.expression;
|
|
926
|
+
let isWorkflowCall = false;
|
|
927
|
+
if (ts.isIdentifier(callee) && callee.text === "workflow") {
|
|
928
|
+
isWorkflowCall = true;
|
|
929
|
+
}
|
|
930
|
+
else if (ts.isPropertyAccessExpression(callee)) {
|
|
931
|
+
const obj = callee.expression;
|
|
932
|
+
const prop = callee.name;
|
|
933
|
+
if (ts.isIdentifier(obj) && contextParameterNames.has(obj.text) && prop.text === "workflow") {
|
|
934
|
+
isWorkflowCall = true;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
if (isWorkflowCall && node.arguments.length > 0) {
|
|
938
|
+
const firstArg = node.arguments[0];
|
|
939
|
+
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
940
|
+
const nameExpr = getObjectLiteralProperty(firstArg, "name");
|
|
941
|
+
if (nameExpr && ts.isStringLiteral(nameExpr)) {
|
|
942
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(nameExpr.getStart());
|
|
943
|
+
dependencies.push({
|
|
944
|
+
name: nameExpr.text,
|
|
945
|
+
line: line + 1,
|
|
946
|
+
character: character + 1
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
ts.forEachChild(node, visit);
|
|
953
|
+
}
|
|
954
|
+
visit(sourceFile);
|
|
955
|
+
return dependencies;
|
|
956
|
+
}
|
|
957
|
+
export function validateRegistryDependencies(registry, options) {
|
|
958
|
+
const definitions = registry.list();
|
|
959
|
+
// 1. Extract dependencies
|
|
960
|
+
const dependencyMap = new Map();
|
|
961
|
+
for (const def of definitions) {
|
|
962
|
+
const deps = extractWorkflowDependencies(def.parsedWorkflow);
|
|
963
|
+
dependencyMap.set(def.name, deps);
|
|
964
|
+
}
|
|
965
|
+
const visiting = new Set();
|
|
966
|
+
const visited = new Set();
|
|
967
|
+
const validationCache = new Map(); // Map of workflow name -> validation errors (if any)
|
|
968
|
+
const knownWorkflowNames = registry.names();
|
|
969
|
+
const workflowInputSchemas = registry.inputSchemas();
|
|
970
|
+
// Find root workflow name if rootWorkflowPath is provided
|
|
971
|
+
const rootDef = options.rootWorkflowPath
|
|
972
|
+
? definitions.find(d => resolve(d.sourcePath) === resolve(options.rootWorkflowPath))
|
|
973
|
+
: undefined;
|
|
974
|
+
const rootName = rootDef?.name;
|
|
975
|
+
// DFS function for cycle detection and transitive validation
|
|
976
|
+
function check(currentName, stack) {
|
|
977
|
+
if (visiting.has(currentName)) {
|
|
978
|
+
const cycleStartIndex = stack.findIndex(item => item.name === currentName);
|
|
979
|
+
const cycleStack = stack.slice(cycleStartIndex);
|
|
980
|
+
const chainStr = cycleStack
|
|
981
|
+
.map(item => `${item.name} (${item.sourcePath}${item.line ? `:${item.line}:${item.character}` : ""})`)
|
|
982
|
+
.join(" -> ");
|
|
983
|
+
throw new OpenDynamicWorkflowError(ErrorCode.WORKFLOW_VALIDATION_ERROR, `Static recursion cycle detected: ${chainStr} -> ${currentName}`);
|
|
984
|
+
}
|
|
985
|
+
if (validationCache.has(currentName)) {
|
|
986
|
+
return validationCache.get(currentName);
|
|
987
|
+
}
|
|
988
|
+
if (visited.has(currentName)) {
|
|
989
|
+
return [];
|
|
990
|
+
}
|
|
991
|
+
const def = registry.get(currentName);
|
|
992
|
+
if (!def) {
|
|
993
|
+
return [`Workflow '${currentName}' was not found in the registry.`];
|
|
994
|
+
}
|
|
995
|
+
visiting.add(currentName);
|
|
996
|
+
// 2. Validate current workflow first (using standard validation)
|
|
997
|
+
const issues = validateWorkflow(def.parsedWorkflow, {
|
|
998
|
+
allowImports: false,
|
|
999
|
+
sharedAgentRegistry: options.sharedAgentRegistry,
|
|
1000
|
+
knownWorkflowNames,
|
|
1001
|
+
workflowInputSchemas,
|
|
1002
|
+
allowDynamicSharedAgentIds: options.allowDynamicSharedAgentIds,
|
|
1003
|
+
toolRegistry: options.toolRegistry
|
|
1004
|
+
});
|
|
1005
|
+
const localErrors = issues.filter(issue => issue.severity !== "warning").map(issue => issue.message);
|
|
1006
|
+
// Log warnings for checked workflows. To avoid duplicate warnings for root workflow (which was
|
|
1007
|
+
// validated standalone in discovery phase), we skip root if rootName is set.
|
|
1008
|
+
if (currentName !== rootName) {
|
|
1009
|
+
const warnings = issues.filter(issue => issue.severity === "warning");
|
|
1010
|
+
if (warnings.length > 0) {
|
|
1011
|
+
for (const warning of warnings) {
|
|
1012
|
+
console.warn(`Warning: ${warning.message} (at line ${warning.line}, col ${warning.column})`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
// 3. Recurse to check dependencies
|
|
1017
|
+
const deps = dependencyMap.get(currentName) || [];
|
|
1018
|
+
const childErrors = [];
|
|
1019
|
+
for (const dep of deps) {
|
|
1020
|
+
const errors = check(dep.name, [
|
|
1021
|
+
...stack,
|
|
1022
|
+
{
|
|
1023
|
+
name: currentName,
|
|
1024
|
+
sourcePath: def.sourcePath,
|
|
1025
|
+
line: dep.line,
|
|
1026
|
+
character: dep.character
|
|
1027
|
+
}
|
|
1028
|
+
]);
|
|
1029
|
+
for (const err of errors) {
|
|
1030
|
+
childErrors.push(`${dep.name} (${def.sourcePath}:${dep.line}:${dep.character}) -> ${err}`);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
visiting.delete(currentName);
|
|
1034
|
+
visited.add(currentName);
|
|
1035
|
+
const allErrors = [...localErrors, ...childErrors];
|
|
1036
|
+
validationCache.set(currentName, allErrors);
|
|
1037
|
+
return allErrors;
|
|
1038
|
+
}
|
|
1039
|
+
// Run validation only starting from root workflow if rootDef is found, otherwise fallback to all definitions.
|
|
1040
|
+
const allRegistryErrors = [];
|
|
1041
|
+
if (rootDef) {
|
|
1042
|
+
const errors = check(rootDef.name, []);
|
|
1043
|
+
if (errors.length > 0) {
|
|
1044
|
+
allRegistryErrors.push(`Workflow '${rootDef.name}' validation failed:\n` + errors.map(e => ` - ${e}`).join("\n"));
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
else {
|
|
1048
|
+
for (const def of definitions) {
|
|
1049
|
+
const errors = check(def.name, []);
|
|
1050
|
+
if (errors.length > 0) {
|
|
1051
|
+
allRegistryErrors.push(`Workflow '${def.name}' validation failed:\n` + errors.map(e => ` - ${e}`).join("\n"));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (allRegistryErrors.length > 0) {
|
|
1056
|
+
throw new OpenDynamicWorkflowError(ErrorCode.WORKFLOW_VALIDATION_ERROR, allRegistryErrors.join("\n\n"));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
//# sourceMappingURL=validate.js.map
|