@the-open-engine/zeroshot 6.38.1 → 6.39.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/cli/event-copy.js +8 -7
- package/cli/event-copy.ts +12 -0
- package/cli/index.js +7 -31
- package/cli/json-export.js +61 -64
- package/cli/json-export.ts +112 -0
- package/cli/message-formatter-utils.js +68 -56
- package/cli/message-formatter-utils.ts +121 -0
- package/cli/message-formatters-normal.js +124 -259
- package/cli/message-formatters-normal.ts +250 -0
- package/cli/message-formatters-watch.js +81 -84
- package/cli/message-formatters-watch.ts +189 -0
- package/lib/cluster/generated/protocol-schema.cjs +1 -1
- package/lib/cluster/generated/protocol-schema.mjs +1 -1
- package/lib/cluster/generated/protocol.d.cts +8 -0
- package/lib/cluster/generated/protocol.d.mts +8 -0
- package/lib/cluster/generated/protocol.d.ts +8 -0
- package/lib/clusters-registry.d.ts +23 -0
- package/lib/clusters-registry.js +17 -29
- package/lib/completion.d.ts +12 -0
- package/lib/completion.js +84 -94
- package/lib/compose-utils.d.ts +32 -0
- package/lib/compose-utils.js +57 -72
- package/lib/detached-startup.d.ts +83 -0
- package/lib/detached-startup.js +231 -302
- package/lib/docker-config.d.ts +43 -0
- package/lib/docker-config.js +150 -325
- package/lib/docker-env-auth.d.ts +17 -0
- package/lib/docker-env-auth.js +75 -0
- package/lib/git-remote-utils.d.ts +48 -0
- package/lib/git-remote-utils.js +147 -213
- package/lib/id-detector.d.ts +15 -0
- package/lib/id-detector.js +37 -41
- package/lib/path-check.d.ts +30 -0
- package/lib/path-check.js +42 -45
- package/lib/process-liveness.d.ts +16 -0
- package/lib/process-liveness.js +19 -12
- package/lib/provider-credential-path.d.ts +16 -0
- package/lib/provider-credential-path.js +31 -29
- package/lib/provider-defaults.d.ts +30 -0
- package/lib/provider-defaults.js +47 -42
- package/lib/provider-detection.d.ts +11 -0
- package/lib/provider-detection.js +91 -54
- package/lib/provider-names.d.ts +38 -0
- package/lib/provider-names.js +56 -64
- package/lib/repo-settings.d.ts +28 -0
- package/lib/repo-settings.js +83 -59
- package/lib/run-mode.d.ts +21 -0
- package/lib/run-mode.js +21 -20
- package/lib/run-plan.d.ts +28 -0
- package/lib/run-plan.js +17 -16
- package/lib/settings/claude-auth.d.ts +35 -0
- package/lib/settings/claude-auth.js +30 -47
- package/lib/settings-defaults.d.ts +20 -0
- package/lib/settings-defaults.js +149 -0
- package/lib/settings-error.d.ts +7 -0
- package/lib/settings-error.js +8 -0
- package/lib/settings-issue-providers.d.ts +10 -0
- package/lib/settings-issue-providers.js +12 -0
- package/lib/settings-models.d.ts +22 -0
- package/lib/settings-models.js +93 -0
- package/lib/settings-storage.d.ts +23 -0
- package/lib/settings-storage.js +205 -0
- package/lib/settings-validation.d.ts +7 -0
- package/lib/settings-validation.js +182 -0
- package/lib/settings.d.ts +29 -0
- package/lib/settings.js +41 -736
- package/lib/setup-apply-types.d.ts +66 -0
- package/lib/setup-apply-types.js +2 -0
- package/lib/setup-apply.d.ts +39 -0
- package/lib/setup-apply.js +267 -264
- package/lib/setup-journal.d.ts +41 -0
- package/lib/setup-journal.js +119 -76
- package/lib/setup-plan-types.d.ts +102 -0
- package/lib/setup-plan-types.js +2 -0
- package/lib/setup-plan.d.ts +30 -0
- package/lib/setup-plan.js +300 -324
- package/lib/setup-undo.d.ts +44 -0
- package/lib/setup-undo.js +61 -74
- package/lib/start-cluster-config.d.ts +26 -0
- package/lib/start-cluster-config.js +56 -0
- package/lib/start-cluster-environment.d.ts +40 -0
- package/lib/start-cluster-environment.js +134 -0
- package/lib/start-cluster-run-options.d.ts +53 -0
- package/lib/start-cluster-run-options.js +147 -0
- package/lib/start-cluster.d.ts +64 -0
- package/lib/start-cluster.js +98 -454
- package/lib/stream-json-parser.d.ts +17 -0
- package/lib/stream-json-parser.js +51 -52
- package/npm-shrinkwrap.json +2 -2
- package/package.json +12 -6
- package/scripts/postinstall.js +27 -0
- package/scripts/rust-distribution.js +23 -14
- package/src/agent/agent-command-proofs-context.js +35 -45
- package/src/agent/agent-command-proofs-context.ts +87 -0
- package/src/agent/agent-config-settings.js +19 -0
- package/src/agent/agent-config-settings.ts +41 -0
- package/src/agent/agent-config.js +122 -231
- package/src/agent/agent-config.ts +285 -0
- package/src/agent/agent-context-builder.js +38 -273
- package/src/agent/agent-context-builder.ts +51 -0
- package/src/agent/agent-context-environment-sections.js +105 -0
- package/src/agent/agent-context-environment-sections.ts +134 -0
- package/src/agent/agent-context-packs.js +165 -0
- package/src/agent/agent-context-packs.ts +296 -0
- package/src/agent/agent-context-prompt-sections.js +100 -0
- package/src/agent/agent-context-prompt-sections.ts +148 -0
- package/src/agent/agent-context-sections.js +25 -334
- package/src/agent/agent-context-sections.ts +36 -0
- package/src/agent/agent-context-sources.js +96 -149
- package/src/agent/agent-context-sources.ts +253 -0
- package/src/agent/agent-context-types.js +2 -0
- package/src/agent/agent-context-types.ts +132 -0
- package/src/agent/agent-context-validation-sections.js +73 -0
- package/src/agent/agent-context-validation-sections.ts +129 -0
- package/src/agent/agent-input-injector.js +99 -118
- package/src/agent/agent-input-injector.ts +195 -0
- package/src/agent/agent-liveness-poll.js +74 -78
- package/src/agent/agent-liveness-poll.ts +163 -0
- package/src/agent/agent-quality-gate-schema.js +77 -86
- package/src/agent/agent-quality-gate-schema.ts +135 -0
- package/src/agent/agent-quality-gates-context.js +44 -50
- package/src/agent/agent-quality-gates-context.ts +91 -0
- package/src/agent/agent-stuck-detector.js +171 -239
- package/src/agent/agent-stuck-detector.ts +288 -0
- package/src/agent/agent-trigger-evaluator.js +29 -46
- package/src/agent/agent-trigger-evaluator.ts +98 -0
- package/src/agent/context-metrics.js +110 -141
- package/src/agent/context-metrics.ts +261 -0
- package/src/agent/context-pack-builder.js +72 -358
- package/src/agent/context-pack-builder.ts +105 -0
- package/src/agent/context-pack-core.js +70 -0
- package/src/agent/context-pack-core.ts +96 -0
- package/src/agent/context-pack-selection.js +119 -0
- package/src/agent/context-pack-selection.ts +187 -0
- package/src/agent/context-pack-truncation.js +139 -0
- package/src/agent/context-pack-truncation.ts +208 -0
- package/src/agent/context-pack-types.js +2 -0
- package/src/agent/context-pack-types.ts +98 -0
- package/src/agent/context-replay-policy.js +30 -39
- package/src/agent/context-replay-policy.ts +71 -0
- package/src/agent/critical-agent-policy.js +6 -10
- package/src/agent/critical-agent-policy.ts +20 -0
- package/src/agent/guidance-queue.js +74 -87
- package/src/agent/guidance-queue.ts +166 -0
- package/src/agent/output-extraction-error-detail.js +45 -0
- package/src/agent/output-extraction-error-detail.ts +49 -0
- package/src/agent/output-extraction-failures.js +125 -0
- package/src/agent/output-extraction-failures.ts +149 -0
- package/src/agent/output-extraction-json.js +234 -0
- package/src/agent/output-extraction-json.ts +235 -0
- package/src/agent/output-extraction-pi.js +124 -0
- package/src/agent/output-extraction-pi.ts +137 -0
- package/src/agent/output-extraction-types.js +2 -0
- package/src/agent/output-extraction-types.ts +49 -0
- package/src/agent/output-extraction.js +17 -617
- package/src/agent/output-extraction.ts +30 -0
- package/src/agent/output-reformatter-errors.js +83 -0
- package/src/agent/output-reformatter-errors.ts +106 -0
- package/src/agent/output-reformatter-types.js +2 -0
- package/src/agent/output-reformatter-types.ts +86 -0
- package/src/agent/output-reformatter.js +140 -185
- package/src/agent/output-reformatter.ts +238 -0
- package/src/agent/pi-terminal-lifecycle-types.js +2 -0
- package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
- package/src/agent/pi-terminal-lifecycle.js +186 -206
- package/src/agent/pi-terminal-lifecycle.ts +297 -0
- package/src/agent/provider-control-plane.js +22 -21
- package/src/agent/provider-control-plane.ts +52 -0
- package/src/agent/provider-terminal-failure-types.js +2 -0
- package/src/agent/provider-terminal-failure-types.ts +60 -0
- package/src/agent/provider-terminal-failure.js +194 -191
- package/src/agent/provider-terminal-failure.ts +262 -0
- package/src/agent/rate-limit-backoff.js +31 -74
- package/src/agent/rate-limit-backoff.ts +71 -0
- package/src/agent/schema-utils.js +85 -125
- package/src/agent/schema-utils.ts +148 -0
- package/src/agent/structured-output-error.js +34 -33
- package/src/agent/structured-output-error.ts +105 -0
- package/src/agent/validation-platform.js +27 -27
- package/src/agent/validation-platform.ts +49 -0
- package/src/attach/attach-client.js +232 -405
- package/src/attach/attach-client.ts +300 -0
- package/src/attach/attach-server-cleanup.js +43 -0
- package/src/attach/attach-server-cleanup.ts +39 -0
- package/src/attach/attach-server-clients.js +117 -0
- package/src/attach/attach-server-clients.ts +122 -0
- package/src/attach/attach-server-events.js +38 -0
- package/src/attach/attach-server-events.ts +36 -0
- package/src/attach/attach-server-pty.js +45 -0
- package/src/attach/attach-server-pty.ts +64 -0
- package/src/attach/attach-server-runtime.js +73 -0
- package/src/attach/attach-server-runtime.ts +62 -0
- package/src/attach/attach-server-socket.js +28 -0
- package/src/attach/attach-server-socket.ts +23 -0
- package/src/attach/attach-server-types.js +2 -0
- package/src/attach/attach-server-types.ts +82 -0
- package/src/attach/attach-server.js +106 -523
- package/src/attach/attach-server.ts +158 -0
- package/src/attach/index.js +18 -35
- package/src/attach/index.ts +18 -0
- package/src/attach/protocol.js +95 -184
- package/src/attach/protocol.ts +181 -0
- package/src/attach/ring-buffer.js +76 -102
- package/src/attach/ring-buffer.ts +109 -0
- package/src/attach/send-input.js +64 -75
- package/src/attach/send-input.ts +94 -0
- package/src/attach/socket-discovery.js +164 -218
- package/src/attach/socket-discovery.ts +230 -0
- package/src/attach/socket-paths.js +42 -61
- package/src/attach/socket-paths.ts +81 -0
- package/src/claude-credentials.js +51 -54
- package/src/claude-credentials.ts +89 -0
- package/src/cluster/generated/protocol-schema.ts +1 -1
- package/src/cluster/generated/protocol.ts +2 -1
- package/src/command-proofs.js +110 -141
- package/src/command-proofs.ts +216 -0
- package/src/config-router.js +81 -73
- package/src/config-router.ts +131 -0
- package/src/copy-worker.js +53 -29
- package/src/copy-worker.ts +75 -0
- package/src/darwin-keychain-boundary.js +80 -111
- package/src/darwin-keychain-boundary.ts +148 -0
- package/src/guidance-topics.js +4 -5
- package/src/guidance-topics.ts +10 -0
- package/src/input-helpers.js +33 -54
- package/src/input-helpers.ts +58 -0
- package/src/ledger-sequence.js +45 -48
- package/src/ledger-sequence.ts +63 -0
- package/src/legacy-lib/clusters-registry.ts +40 -0
- package/src/legacy-lib/completion.ts +148 -0
- package/src/legacy-lib/compose-utils.ts +102 -0
- package/src/legacy-lib/detached-startup.ts +449 -0
- package/src/legacy-lib/docker-config.ts +282 -0
- package/src/legacy-lib/docker-env-auth.ts +116 -0
- package/src/legacy-lib/git-remote-utils.ts +244 -0
- package/src/legacy-lib/id-detector.ts +57 -0
- package/src/legacy-lib/js-yaml.d.ts +3 -0
- package/src/legacy-lib/omelette.d.ts +15 -0
- package/src/legacy-lib/path-check.ts +89 -0
- package/src/legacy-lib/process-liveness.ts +36 -0
- package/src/legacy-lib/provider-credential-path.ts +57 -0
- package/src/legacy-lib/provider-defaults.ts +114 -0
- package/src/legacy-lib/provider-detection.ts +67 -0
- package/src/legacy-lib/provider-names.ts +126 -0
- package/src/legacy-lib/repo-settings.ts +88 -0
- package/src/legacy-lib/run-mode.ts +58 -0
- package/src/legacy-lib/run-plan.ts +49 -0
- package/src/legacy-lib/settings/claude-auth.ts +80 -0
- package/src/legacy-lib/settings-defaults.ts +212 -0
- package/src/legacy-lib/settings-error.ts +8 -0
- package/src/legacy-lib/settings-issue-providers.ts +18 -0
- package/src/legacy-lib/settings-models.ts +124 -0
- package/src/legacy-lib/settings-storage.ts +277 -0
- package/src/legacy-lib/settings-validation.ts +220 -0
- package/src/legacy-lib/settings.ts +87 -0
- package/src/legacy-lib/setup-apply-types.ts +70 -0
- package/src/legacy-lib/setup-apply.ts +456 -0
- package/src/legacy-lib/setup-journal.ts +185 -0
- package/src/legacy-lib/setup-plan-types.ts +96 -0
- package/src/legacy-lib/setup-plan.ts +500 -0
- package/src/legacy-lib/setup-undo.ts +142 -0
- package/src/legacy-lib/start-cluster-config.ts +124 -0
- package/src/legacy-lib/start-cluster-environment.ts +165 -0
- package/src/legacy-lib/start-cluster-run-options.ts +279 -0
- package/src/legacy-lib/start-cluster.ts +237 -0
- package/src/legacy-lib/stream-json-parser.ts +88 -0
- package/src/message-buffer.js +55 -71
- package/src/message-buffer.ts +107 -0
- package/src/message-bus-bridge.js +92 -133
- package/src/message-bus-bridge.ts +149 -0
- package/src/name-generator-shared.js +220 -0
- package/src/name-generator-shared.ts +231 -0
- package/src/name-generator.js +8 -217
- package/src/name-generator.ts +27 -0
- package/src/omp-blob-root.js +83 -64
- package/src/omp-blob-root.ts +116 -0
- package/src/omp-config-overlay.js +65 -38
- package/src/omp-config-overlay.ts +110 -0
- package/src/omp-execution-fingerprint.js +38 -43
- package/src/omp-execution-fingerprint.ts +92 -0
- package/src/omp-session-limits.js +10 -15
- package/src/omp-session-limits.ts +53 -0
- package/src/omp-session-partition.js +2 -2
- package/src/omp-session-verifier.js +1 -1
- package/src/orchestrator.js +19 -4
- package/src/providers/anthropic/index.js +3 -1
- package/src/providers/anthropic/index.ts +13 -0
- package/src/providers/capabilities.js +19 -27
- package/src/providers/capabilities.ts +44 -0
- package/src/providers/google/index.js +3 -1
- package/src/providers/google/index.ts +13 -0
- package/src/providers/openai/index.js +3 -1
- package/src/providers/openai/index.ts +13 -0
- package/src/providers/opencode/index.js +3 -1
- package/src/providers/opencode/index.ts +13 -0
- package/src/quality-gates.js +67 -127
- package/src/quality-gates.ts +149 -0
- package/src/repo-settings-access.js +40 -0
- package/src/repo-settings-access.ts +64 -0
- package/src/schemas/sub-cluster.js +127 -183
- package/src/schemas/sub-cluster.ts +238 -0
- package/src/state-snapshot-normalization.js +224 -0
- package/src/state-snapshot-normalization.ts +222 -0
- package/src/state-snapshot.js +192 -359
- package/src/state-snapshot.ts +291 -0
- package/src/state-snapshotter.js +118 -130
- package/src/state-snapshotter.ts +167 -0
- package/src/sub-cluster-wrapper.js +4 -13
- package/src/task-run-model-args.js +87 -124
- package/src/task-run-model-args.ts +204 -0
- package/src/task-runner.js +5 -25
- package/src/task-runner.ts +28 -0
- package/src/task-startup-error.js +73 -58
- package/src/task-startup-error.ts +126 -0
- package/src/template-validation/consensus-gate-contracts.js +2 -0
- package/src/template-validation/consensus-gate-contracts.ts +70 -0
- package/src/template-validation/consensus-gate-scenarios.js +76 -0
- package/src/template-validation/consensus-gate-scenarios.ts +130 -0
- package/src/template-validation/consensus-gate-stage.js +34 -0
- package/src/template-validation/consensus-gate-stage.ts +56 -0
- package/src/template-validation/random-topology-contracts.js +2 -0
- package/src/template-validation/random-topology-contracts.ts +54 -0
- package/src/template-validation/random-topology-dispatch.js +25 -0
- package/src/template-validation/random-topology-dispatch.ts +33 -0
- package/src/template-validation/random-topology-message-loop.js +53 -0
- package/src/template-validation/random-topology-message-loop.ts +73 -0
- package/src/template-validation/random-topology-operations.js +79 -0
- package/src/template-validation/random-topology-operations.ts +101 -0
- package/src/template-validation/random-topology-runtime.js +25 -0
- package/src/template-validation/random-topology-runtime.ts +43 -0
- package/src/template-validation/random-topology-scenario.js +52 -0
- package/src/template-validation/random-topology-scenario.ts +57 -0
- package/src/template-validation/random-topology-schema.js +159 -0
- package/src/template-validation/random-topology-schema.ts +190 -0
- package/src/template-validation/random-topology-state.js +28 -0
- package/src/template-validation/random-topology-state.ts +30 -0
- package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
- package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
- package/src/template-validation/random-topology-trigger.js +75 -0
- package/src/template-validation/random-topology-trigger.ts +88 -0
- package/src/template-validation/report-formatter.js +40 -46
- package/src/template-validation/report-formatter.ts +101 -0
- package/src/template-validation/simulate-consensus-gates.js +111 -302
- package/src/template-validation/simulate-consensus-gates.ts +196 -0
- package/src/template-validation/simulate-random-topology.js +26 -519
- package/src/template-validation/simulate-random-topology.ts +57 -0
- package/src/template-validation/simulate-two-stage-validation.js +41 -265
- package/src/template-validation/simulate-two-stage-validation.ts +55 -0
- package/src/template-validation/simulation-agent-runtime.js +25 -0
- package/src/template-validation/simulation-agent-runtime.ts +40 -0
- package/src/template-validation/simulation-agent.js +29 -0
- package/src/template-validation/simulation-agent.ts +73 -0
- package/src/template-validation/simulation-runtime.js +34 -0
- package/src/template-validation/simulation-runtime.ts +72 -0
- package/src/template-validation/two-stage-contracts.js +2 -0
- package/src/template-validation/two-stage-contracts.ts +41 -0
- package/src/template-validation/two-stage-inputs.js +21 -0
- package/src/template-validation/two-stage-inputs.ts +29 -0
- package/src/template-validation/two-stage-results.js +30 -0
- package/src/template-validation/two-stage-results.ts +40 -0
- package/src/template-validation/two-stage-scenario.js +86 -0
- package/src/template-validation/two-stage-scenario.ts +120 -0
- package/src/worktree-claude-config.js +182 -220
- package/src/worktree-claude-config.ts +272 -0
- package/src/worktree-tooling-env.js +103 -125
- package/src/worktree-tooling-env.ts +142 -0
- package/task-lib/completion.js +2 -3
- package/task-lib/completion.ts +9 -0
- package/task-lib/config.js +1 -4
- package/task-lib/config.ts +11 -0
- package/task-lib/name-generator.js +4 -226
- package/task-lib/name-generator.ts +11 -0
- package/task-lib/omp-session-cleanup.js +1 -1
- package/task-lib/omp-storage-root.js +8 -12
- package/task-lib/omp-storage-root.ts +62 -0
- package/task-lib/process-termination.js +143 -166
- package/task-lib/process-termination.ts +268 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
interface RepoSettingsResult {
|
|
2
|
+
settings?: unknown;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
interface RepoSettingsModule {
|
|
6
|
+
readRepoSettings(startDir: string): RepoSettingsResult;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface PropertySelectionOptions {
|
|
10
|
+
skipUndefined?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isRepoSettingsModule(value: unknown): value is RepoSettingsModule {
|
|
14
|
+
return (
|
|
15
|
+
typeof value === 'object' &&
|
|
16
|
+
value !== null &&
|
|
17
|
+
'readRepoSettings' in value &&
|
|
18
|
+
typeof value.readRepoSettings === 'function'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const repoSettingsModule: unknown = require('../lib/repo-settings');
|
|
23
|
+
if (!isRepoSettingsModule(repoSettingsModule)) {
|
|
24
|
+
throw new TypeError('repo-settings must export readRepoSettings');
|
|
25
|
+
}
|
|
26
|
+
const readRepoSettings = repoSettingsModule.readRepoSettings;
|
|
27
|
+
|
|
28
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
29
|
+
return typeof value === 'object' && value !== null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function propertyValue(value: unknown, key: string): unknown {
|
|
33
|
+
return isRecord(value) ? value[key] : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function selectOwnProperty(
|
|
37
|
+
key: string,
|
|
38
|
+
candidates: readonly unknown[],
|
|
39
|
+
options: PropertySelectionOptions = {}
|
|
40
|
+
): unknown {
|
|
41
|
+
for (const candidate of candidates) {
|
|
42
|
+
if (!Object.prototype.hasOwnProperty.call(candidate || {}, key)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const value = propertyValue(candidate, key);
|
|
47
|
+
if (options.skipUndefined && value === undefined) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readRepoSettingsValue(startDir: string): unknown {
|
|
56
|
+
return readRepoSettings(startDir).settings || {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export = {
|
|
60
|
+
isRecord,
|
|
61
|
+
propertyValue,
|
|
62
|
+
readRepoSettingsValue,
|
|
63
|
+
selectOwnProperty,
|
|
64
|
+
};
|
|
@@ -1,208 +1,152 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Defines the structure for nested clusters where an agent can be replaced
|
|
5
|
-
* with an entire cluster configuration, enabling recursive composition.
|
|
6
|
-
*
|
|
7
|
-
* Example:
|
|
8
|
-
* {
|
|
9
|
-
* "id": "implementation-cluster",
|
|
10
|
-
* "type": "subcluster",
|
|
11
|
-
* "config": {
|
|
12
|
-
* "agents": [
|
|
13
|
-
* { "id": "worker", "role": "implementation", ... },
|
|
14
|
-
* { "id": "validator", "role": "validator", ... }
|
|
15
|
-
* ]
|
|
16
|
-
* },
|
|
17
|
-
* "triggers": [{ "topic": "PLAN_READY" }],
|
|
18
|
-
* "hooks": {
|
|
19
|
-
* "onComplete": {
|
|
20
|
-
* "action": "publish_message",
|
|
21
|
-
* "config": { "topic": "IMPLEMENTATION_COMPLETE" }
|
|
22
|
-
* }
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Validate sub-cluster agent configuration
|
|
29
|
-
* @param {Object} agentConfig - Agent config with type: 'subcluster'
|
|
30
|
-
* @param {Number} depth - Current nesting depth (for recursion limit)
|
|
31
|
-
* @returns {{ valid: boolean, errors: string[], warnings: string[] }}
|
|
32
|
-
*/
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Schema validation and defaults for recursively composed sub-clusters. */
|
|
33
3
|
const MAX_SUBCLUSTER_DEPTH = 5;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!validateSubClusterConfig(agentConfig, depth, errors, warnings)) {
|
|
53
|
-
return buildValidationResult(errors, warnings);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Validate triggers (sub-cluster must have triggers to activate)
|
|
57
|
-
validateSubClusterTriggers(agentConfig, errors);
|
|
58
|
-
|
|
59
|
-
// Validate hooks structure
|
|
60
|
-
validateSubClusterHooks(agentConfig, errors);
|
|
61
|
-
|
|
62
|
-
// Check for context bridging configuration
|
|
63
|
-
validateContextStrategy(agentConfig, errors);
|
|
64
|
-
|
|
65
|
-
return buildValidationResult(errors, warnings);
|
|
4
|
+
function isUnknownRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null;
|
|
6
|
+
}
|
|
7
|
+
function isConfigValidatorModule(value) {
|
|
8
|
+
return (typeof value === 'object' &&
|
|
9
|
+
value !== null &&
|
|
10
|
+
'validateConfig' in value &&
|
|
11
|
+
typeof value.validateConfig === 'function');
|
|
12
|
+
}
|
|
13
|
+
function loadConfigValidator() {
|
|
14
|
+
// Keep this lazy: config-validator loads this schema while validating agents.
|
|
15
|
+
const configValidator = require('../config-validator');
|
|
16
|
+
if (!isConfigValidatorModule(configValidator)) {
|
|
17
|
+
throw new TypeError('config-validator must export validateConfig');
|
|
18
|
+
}
|
|
19
|
+
return configValidator;
|
|
66
20
|
}
|
|
67
|
-
|
|
68
21
|
function buildValidationResult(errors, warnings) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
22
|
+
return {
|
|
23
|
+
valid: errors.length === 0,
|
|
24
|
+
errors,
|
|
25
|
+
warnings,
|
|
26
|
+
};
|
|
74
27
|
}
|
|
75
|
-
|
|
76
28
|
function validateSubClusterConfig(agentConfig, depth, errors, warnings) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!childValidation.valid) {
|
|
97
|
-
errors.push(...childValidation.errors.map((e) => `Sub-cluster '${agentConfig.id}': ${e}`));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
warnings.push(...childValidation.warnings.map((w) => `Sub-cluster '${agentConfig.id}': ${w}`));
|
|
101
|
-
return true;
|
|
29
|
+
if (!agentConfig.config) {
|
|
30
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' missing config field`);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (!agentConfig.config.agents || !Array.isArray(agentConfig.config.agents)) {
|
|
34
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' config.agents must be an array`);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (agentConfig.config.agents.length === 0) {
|
|
38
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' config.agents cannot be empty`);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const childValidation = loadConfigValidator().validateConfig(agentConfig.config, depth + 1);
|
|
42
|
+
if (!childValidation.valid) {
|
|
43
|
+
errors.push(...childValidation.errors.map((error) => `Sub-cluster '${String(agentConfig.id)}': ${error}`));
|
|
44
|
+
}
|
|
45
|
+
warnings.push(...childValidation.warnings.map((warning) => `Sub-cluster '${String(agentConfig.id)}': ${warning}`));
|
|
46
|
+
return true;
|
|
102
47
|
}
|
|
103
|
-
|
|
104
48
|
function validateSubClusterTriggers(agentConfig, errors) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
49
|
+
if (!agentConfig.triggers || agentConfig.triggers.length === 0) {
|
|
50
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' must have triggers to activate`);
|
|
51
|
+
}
|
|
108
52
|
}
|
|
109
|
-
|
|
110
53
|
function validateSubClusterHooks(agentConfig, errors) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
54
|
+
if (!agentConfig.hooks?.onComplete) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const hook = agentConfig.hooks.onComplete;
|
|
58
|
+
if (!hook.action) {
|
|
59
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' onComplete hook missing action`);
|
|
60
|
+
}
|
|
61
|
+
if (hook.action === 'publish_message' && !hook.config?.topic) {
|
|
62
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' onComplete hook missing config.topic`);
|
|
63
|
+
}
|
|
122
64
|
}
|
|
123
|
-
|
|
124
65
|
function validateContextStrategy(agentConfig, errors) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (!Array.isArray(parentTopics)) {
|
|
131
|
-
errors.push(`Sub-cluster '${agentConfig.id}' contextStrategy.parentTopics must be an array`);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Validate each parent topic entry
|
|
136
|
-
for (const entry of parentTopics) {
|
|
137
|
-
if (typeof entry === 'string') {
|
|
138
|
-
continue;
|
|
66
|
+
const parentTopics = agentConfig.contextStrategy?.parentTopics;
|
|
67
|
+
if (!parentTopics) {
|
|
68
|
+
return;
|
|
139
69
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
`Sub-cluster '${agentConfig.id}' parentTopics must contain strings or objects, got ${typeof entry}`
|
|
144
|
-
);
|
|
145
|
-
continue;
|
|
70
|
+
if (!Array.isArray(parentTopics)) {
|
|
71
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' contextStrategy.parentTopics must be an array`);
|
|
72
|
+
return;
|
|
146
73
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
74
|
+
const allowedStrategies = ['latest', 'all', 'oldest'];
|
|
75
|
+
for (const entry of parentTopics) {
|
|
76
|
+
if (typeof entry === 'string') {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!isUnknownRecord(entry)) {
|
|
80
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' parentTopics must contain strings or objects, got ${typeof entry}`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (typeof entry.topic !== 'string') {
|
|
84
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' parentTopics entry must include a string topic`);
|
|
85
|
+
}
|
|
86
|
+
if (entry.strategy && !allowedStrategies.includes(entry.strategy)) {
|
|
87
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' parentTopics entry has invalid strategy '${String(entry.strategy)}'`);
|
|
88
|
+
}
|
|
89
|
+
if (entry.amount !== undefined &&
|
|
90
|
+
(typeof entry.amount !== 'number' || !Number.isFinite(entry.amount))) {
|
|
91
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' parentTopics entry amount must be a number`);
|
|
92
|
+
}
|
|
93
|
+
if (entry.limit !== undefined &&
|
|
94
|
+
(typeof entry.limit !== 'number' || !Number.isFinite(entry.limit))) {
|
|
95
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' parentTopics entry limit must be a number`);
|
|
96
|
+
}
|
|
150
97
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
98
|
+
}
|
|
99
|
+
function validateSubCluster(agentConfig, depth = 0) {
|
|
100
|
+
const errors = [];
|
|
101
|
+
const warnings = [];
|
|
102
|
+
if (depth > MAX_SUBCLUSTER_DEPTH) {
|
|
103
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' exceeds max nesting depth (${MAX_SUBCLUSTER_DEPTH})`);
|
|
104
|
+
return buildValidationResult(errors, warnings);
|
|
156
105
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
errors.push(`Sub-cluster '${agentConfig.id}' parentTopics entry amount must be a number`);
|
|
106
|
+
if (agentConfig.type !== 'subcluster') {
|
|
107
|
+
errors.push(`Agent '${String(agentConfig.id)}' must have type: 'subcluster'`);
|
|
160
108
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
errors.push(`Sub-cluster '${agentConfig.id}' parentTopics entry limit must be a number`);
|
|
109
|
+
if (!validateSubClusterConfig(agentConfig, depth, errors, warnings)) {
|
|
110
|
+
return buildValidationResult(errors, warnings);
|
|
164
111
|
}
|
|
165
|
-
|
|
112
|
+
validateSubClusterTriggers(agentConfig, errors);
|
|
113
|
+
validateSubClusterHooks(agentConfig, errors);
|
|
114
|
+
validateContextStrategy(agentConfig, errors);
|
|
115
|
+
return buildValidationResult(errors, warnings);
|
|
166
116
|
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Get default sub-cluster template
|
|
170
|
-
* @returns {Object} Default sub-cluster configuration
|
|
171
|
-
*/
|
|
172
117
|
function getDefaultSubCluster() {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
118
|
+
return {
|
|
119
|
+
id: 'example-subcluster',
|
|
120
|
+
type: 'subcluster',
|
|
121
|
+
role: 'orchestrator',
|
|
122
|
+
config: {
|
|
123
|
+
agents: [
|
|
124
|
+
{
|
|
125
|
+
id: 'worker',
|
|
126
|
+
role: 'implementation',
|
|
127
|
+
triggers: [{ topic: 'PARENT_TRIGGER' }],
|
|
128
|
+
hooks: {
|
|
129
|
+
onComplete: {
|
|
130
|
+
action: 'publish_message',
|
|
131
|
+
config: { topic: 'WORK_COMPLETE' },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
triggers: [{ topic: 'START_WORK' }],
|
|
138
|
+
hooks: {
|
|
184
139
|
onComplete: {
|
|
185
|
-
|
|
186
|
-
|
|
140
|
+
action: 'publish_message',
|
|
141
|
+
config: { topic: 'SUBCLUSTER_COMPLETE' },
|
|
187
142
|
},
|
|
188
|
-
},
|
|
189
143
|
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
onComplete: {
|
|
195
|
-
action: 'publish_message',
|
|
196
|
-
config: { topic: 'SUBCLUSTER_COMPLETE' },
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
contextStrategy: {
|
|
200
|
-
parentTopics: ['ISSUE_OPENED', 'PLAN_READY'],
|
|
201
|
-
},
|
|
202
|
-
};
|
|
144
|
+
contextStrategy: {
|
|
145
|
+
parentTopics: ['ISSUE_OPENED', 'PLAN_READY'],
|
|
146
|
+
},
|
|
147
|
+
};
|
|
203
148
|
}
|
|
204
|
-
|
|
205
149
|
module.exports = {
|
|
206
|
-
|
|
207
|
-
|
|
150
|
+
validateSubCluster,
|
|
151
|
+
getDefaultSubCluster,
|
|
208
152
|
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/** Schema validation and defaults for recursively composed sub-clusters. */
|
|
2
|
+
|
|
3
|
+
type UnknownRecord = Record<string, unknown>;
|
|
4
|
+
|
|
5
|
+
interface ValidationResult {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
errors: string[];
|
|
8
|
+
warnings: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ConfigValidatorModule {
|
|
12
|
+
validateConfig(config: UnknownRecord, depth: number): ValidationResult;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface CompletionHook {
|
|
16
|
+
action?: unknown;
|
|
17
|
+
config?: { topic?: unknown };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SubClusterAgentConfig {
|
|
21
|
+
id?: unknown;
|
|
22
|
+
type?: unknown;
|
|
23
|
+
config?: UnknownRecord;
|
|
24
|
+
triggers?: unknown[];
|
|
25
|
+
hooks?: { onComplete?: CompletionHook };
|
|
26
|
+
contextStrategy?: { parentTopics?: unknown };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const MAX_SUBCLUSTER_DEPTH = 5;
|
|
30
|
+
|
|
31
|
+
function isUnknownRecord(value: unknown): value is UnknownRecord {
|
|
32
|
+
return typeof value === 'object' && value !== null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isConfigValidatorModule(value: unknown): value is ConfigValidatorModule {
|
|
36
|
+
return (
|
|
37
|
+
typeof value === 'object' &&
|
|
38
|
+
value !== null &&
|
|
39
|
+
'validateConfig' in value &&
|
|
40
|
+
typeof value.validateConfig === 'function'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function loadConfigValidator(): ConfigValidatorModule {
|
|
45
|
+
// Keep this lazy: config-validator loads this schema while validating agents.
|
|
46
|
+
const configValidator: unknown = require('../config-validator');
|
|
47
|
+
if (!isConfigValidatorModule(configValidator)) {
|
|
48
|
+
throw new TypeError('config-validator must export validateConfig');
|
|
49
|
+
}
|
|
50
|
+
return configValidator;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function buildValidationResult(errors: string[], warnings: string[]): ValidationResult {
|
|
54
|
+
return {
|
|
55
|
+
valid: errors.length === 0,
|
|
56
|
+
errors,
|
|
57
|
+
warnings,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validateSubClusterConfig(
|
|
62
|
+
agentConfig: SubClusterAgentConfig,
|
|
63
|
+
depth: number,
|
|
64
|
+
errors: string[],
|
|
65
|
+
warnings: string[]
|
|
66
|
+
): boolean {
|
|
67
|
+
if (!agentConfig.config) {
|
|
68
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' missing config field`);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!agentConfig.config.agents || !Array.isArray(agentConfig.config.agents)) {
|
|
73
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' config.agents must be an array`);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (agentConfig.config.agents.length === 0) {
|
|
78
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' config.agents cannot be empty`);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const childValidation = loadConfigValidator().validateConfig(agentConfig.config, depth + 1);
|
|
83
|
+
|
|
84
|
+
if (!childValidation.valid) {
|
|
85
|
+
errors.push(
|
|
86
|
+
...childValidation.errors.map((error) => `Sub-cluster '${String(agentConfig.id)}': ${error}`)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
warnings.push(
|
|
91
|
+
...childValidation.warnings.map(
|
|
92
|
+
(warning) => `Sub-cluster '${String(agentConfig.id)}': ${warning}`
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function validateSubClusterTriggers(agentConfig: SubClusterAgentConfig, errors: string[]): void {
|
|
99
|
+
if (!agentConfig.triggers || agentConfig.triggers.length === 0) {
|
|
100
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' must have triggers to activate`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function validateSubClusterHooks(agentConfig: SubClusterAgentConfig, errors: string[]): void {
|
|
105
|
+
if (!agentConfig.hooks?.onComplete) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const hook = agentConfig.hooks.onComplete;
|
|
110
|
+
if (!hook.action) {
|
|
111
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' onComplete hook missing action`);
|
|
112
|
+
}
|
|
113
|
+
if (hook.action === 'publish_message' && !hook.config?.topic) {
|
|
114
|
+
errors.push(`Sub-cluster '${String(agentConfig.id)}' onComplete hook missing config.topic`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function validateContextStrategy(agentConfig: SubClusterAgentConfig, errors: string[]): void {
|
|
119
|
+
const parentTopics = agentConfig.contextStrategy?.parentTopics;
|
|
120
|
+
if (!parentTopics) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!Array.isArray(parentTopics)) {
|
|
125
|
+
errors.push(
|
|
126
|
+
`Sub-cluster '${String(agentConfig.id)}' contextStrategy.parentTopics must be an array`
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const allowedStrategies: unknown[] = ['latest', 'all', 'oldest'];
|
|
132
|
+
for (const entry of parentTopics) {
|
|
133
|
+
if (typeof entry === 'string') {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!isUnknownRecord(entry)) {
|
|
138
|
+
errors.push(
|
|
139
|
+
`Sub-cluster '${String(agentConfig.id)}' parentTopics must contain strings or objects, got ${typeof entry}`
|
|
140
|
+
);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (typeof entry.topic !== 'string') {
|
|
145
|
+
errors.push(
|
|
146
|
+
`Sub-cluster '${String(agentConfig.id)}' parentTopics entry must include a string topic`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (entry.strategy && !allowedStrategies.includes(entry.strategy)) {
|
|
151
|
+
errors.push(
|
|
152
|
+
`Sub-cluster '${String(agentConfig.id)}' parentTopics entry has invalid strategy '${String(entry.strategy)}'`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
entry.amount !== undefined &&
|
|
158
|
+
(typeof entry.amount !== 'number' || !Number.isFinite(entry.amount))
|
|
159
|
+
) {
|
|
160
|
+
errors.push(
|
|
161
|
+
`Sub-cluster '${String(agentConfig.id)}' parentTopics entry amount must be a number`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (
|
|
166
|
+
entry.limit !== undefined &&
|
|
167
|
+
(typeof entry.limit !== 'number' || !Number.isFinite(entry.limit))
|
|
168
|
+
) {
|
|
169
|
+
errors.push(
|
|
170
|
+
`Sub-cluster '${String(agentConfig.id)}' parentTopics entry limit must be a number`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function validateSubCluster(agentConfig: SubClusterAgentConfig, depth = 0): ValidationResult {
|
|
177
|
+
const errors: string[] = [];
|
|
178
|
+
const warnings: string[] = [];
|
|
179
|
+
|
|
180
|
+
if (depth > MAX_SUBCLUSTER_DEPTH) {
|
|
181
|
+
errors.push(
|
|
182
|
+
`Sub-cluster '${String(agentConfig.id)}' exceeds max nesting depth (${MAX_SUBCLUSTER_DEPTH})`
|
|
183
|
+
);
|
|
184
|
+
return buildValidationResult(errors, warnings);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (agentConfig.type !== 'subcluster') {
|
|
188
|
+
errors.push(`Agent '${String(agentConfig.id)}' must have type: 'subcluster'`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!validateSubClusterConfig(agentConfig, depth, errors, warnings)) {
|
|
192
|
+
return buildValidationResult(errors, warnings);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
validateSubClusterTriggers(agentConfig, errors);
|
|
196
|
+
validateSubClusterHooks(agentConfig, errors);
|
|
197
|
+
validateContextStrategy(agentConfig, errors);
|
|
198
|
+
|
|
199
|
+
return buildValidationResult(errors, warnings);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getDefaultSubCluster(): UnknownRecord {
|
|
203
|
+
return {
|
|
204
|
+
id: 'example-subcluster',
|
|
205
|
+
type: 'subcluster',
|
|
206
|
+
role: 'orchestrator',
|
|
207
|
+
config: {
|
|
208
|
+
agents: [
|
|
209
|
+
{
|
|
210
|
+
id: 'worker',
|
|
211
|
+
role: 'implementation',
|
|
212
|
+
triggers: [{ topic: 'PARENT_TRIGGER' }],
|
|
213
|
+
hooks: {
|
|
214
|
+
onComplete: {
|
|
215
|
+
action: 'publish_message',
|
|
216
|
+
config: { topic: 'WORK_COMPLETE' },
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
triggers: [{ topic: 'START_WORK' }],
|
|
223
|
+
hooks: {
|
|
224
|
+
onComplete: {
|
|
225
|
+
action: 'publish_message',
|
|
226
|
+
config: { topic: 'SUBCLUSTER_COMPLETE' },
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
contextStrategy: {
|
|
230
|
+
parentTopics: ['ISSUE_OPENED', 'PLAN_READY'],
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export = {
|
|
236
|
+
validateSubCluster,
|
|
237
|
+
getDefaultSubCluster,
|
|
238
|
+
};
|