agent-relay 3.2.22 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/dist/index.cjs +6564 -2100
- package/dist/src/cli/bootstrap.d.ts.map +1 -1
- package/dist/src/cli/bootstrap.js +2 -0
- package/dist/src/cli/bootstrap.js.map +1 -1
- package/dist/src/cli/commands/agent-management.d.ts.map +1 -1
- package/dist/src/cli/commands/agent-management.js +14 -4
- package/dist/src/cli/commands/agent-management.js.map +1 -1
- package/dist/src/cli/commands/core.d.ts +2 -6
- package/dist/src/cli/commands/core.d.ts.map +1 -1
- package/dist/src/cli/commands/core.js +31 -12
- package/dist/src/cli/commands/core.js.map +1 -1
- package/dist/src/cli/commands/messaging.d.ts.map +1 -1
- package/dist/src/cli/commands/messaging.js +10 -3
- package/dist/src/cli/commands/messaging.js.map +1 -1
- package/dist/src/cli/commands/monitoring.d.ts +2 -2
- package/dist/src/cli/commands/monitoring.d.ts.map +1 -1
- package/dist/src/cli/commands/monitoring.js +15 -6
- package/dist/src/cli/commands/monitoring.js.map +1 -1
- package/dist/src/cli/commands/on/dotfiles.d.ts +35 -0
- package/dist/src/cli/commands/on/dotfiles.d.ts.map +1 -0
- package/dist/src/cli/commands/on/dotfiles.js +157 -0
- package/dist/src/cli/commands/on/dotfiles.js.map +1 -0
- package/dist/src/cli/commands/on/prereqs.d.ts +15 -0
- package/dist/src/cli/commands/on/prereqs.d.ts.map +1 -0
- package/dist/src/cli/commands/on/prereqs.js +103 -0
- package/dist/src/cli/commands/on/prereqs.js.map +1 -0
- package/dist/src/cli/commands/on/provision.d.ts +22 -0
- package/dist/src/cli/commands/on/provision.d.ts.map +1 -0
- package/dist/src/cli/commands/on/provision.js +157 -0
- package/dist/src/cli/commands/on/provision.js.map +1 -0
- package/dist/src/cli/commands/on/relayfile-binary.d.ts +2 -0
- package/dist/src/cli/commands/on/relayfile-binary.d.ts.map +1 -0
- package/dist/src/cli/commands/on/relayfile-binary.js +208 -0
- package/dist/src/cli/commands/on/relayfile-binary.js.map +1 -0
- package/dist/src/cli/commands/on/scan.d.ts +8 -0
- package/dist/src/cli/commands/on/scan.d.ts.map +1 -0
- package/dist/src/cli/commands/on/scan.js +59 -0
- package/dist/src/cli/commands/on/scan.js.map +1 -0
- package/dist/src/cli/commands/on/services.d.ts +17 -0
- package/dist/src/cli/commands/on/services.d.ts.map +1 -0
- package/dist/src/cli/commands/on/services.js +328 -0
- package/dist/src/cli/commands/on/services.js.map +1 -0
- package/dist/src/cli/commands/on/start.d.ts +61 -0
- package/dist/src/cli/commands/on/start.d.ts.map +1 -0
- package/dist/src/cli/commands/on/start.js +1107 -0
- package/dist/src/cli/commands/on/start.js.map +1 -0
- package/dist/src/cli/commands/on/stop.d.ts +4 -0
- package/dist/src/cli/commands/on/stop.d.ts.map +1 -0
- package/dist/src/cli/commands/on/stop.js +11 -0
- package/dist/src/cli/commands/on/stop.js.map +1 -0
- package/dist/src/cli/commands/on/token.d.ts +8 -0
- package/dist/src/cli/commands/on/token.d.ts.map +1 -0
- package/dist/src/cli/commands/on/token.js +26 -0
- package/dist/src/cli/commands/on/token.js.map +1 -0
- package/dist/src/cli/commands/on/workspace.d.ts +4 -0
- package/dist/src/cli/commands/on/workspace.d.ts.map +1 -0
- package/dist/src/cli/commands/on/workspace.js +245 -0
- package/dist/src/cli/commands/on/workspace.js.map +1 -0
- package/dist/src/cli/commands/on.d.ts +10 -0
- package/dist/src/cli/commands/on.d.ts.map +1 -0
- package/dist/src/cli/commands/on.js +52 -0
- package/dist/src/cli/commands/on.js.map +1 -0
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +10 -21
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/lib/bridge.js +1 -1
- package/dist/src/cli/lib/bridge.js.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.d.ts +14 -4
- package/dist/src/cli/lib/broker-lifecycle.d.ts.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.js +82 -120
- package/dist/src/cli/lib/broker-lifecycle.js.map +1 -1
- package/dist/src/cli/lib/client-factory.d.ts +4 -4
- package/dist/src/cli/lib/client-factory.d.ts.map +1 -1
- package/dist/src/cli/lib/client-factory.js +14 -11
- package/dist/src/cli/lib/client-factory.js.map +1 -1
- package/dist/src/cli/lib/core-maintenance.d.ts.map +1 -1
- package/dist/src/cli/lib/core-maintenance.js +11 -22
- package/dist/src/cli/lib/core-maintenance.js.map +1 -1
- package/dist/src/cost/pricing.d.ts +18 -0
- package/dist/src/cost/pricing.d.ts.map +1 -0
- package/dist/src/cost/pricing.js +111 -0
- package/dist/src/cost/pricing.js.map +1 -0
- package/dist/src/cost/tracker.d.ts +13 -0
- package/dist/src/cost/tracker.d.ts.map +1 -0
- package/dist/src/cost/tracker.js +152 -0
- package/dist/src/cost/tracker.js.map +1 -0
- package/dist/src/cost/types.d.ts +23 -0
- package/dist/src/cost/types.d.ts.map +1 -0
- package/dist/src/cost/types.js +2 -0
- package/dist/src/cost/types.js.map +1 -0
- package/package.json +15 -12
- package/packages/acp-bridge/package.json +2 -2
- package/packages/brand/package.json +1 -1
- package/packages/cloud/package.json +3 -3
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/sdk/README.md +10 -3
- package/packages/sdk/dist/broker-path.d.ts +3 -2
- package/packages/sdk/dist/broker-path.d.ts.map +1 -1
- package/packages/sdk/dist/broker-path.js +119 -32
- package/packages/sdk/dist/broker-path.js.map +1 -1
- package/packages/sdk/dist/client.d.ts +119 -197
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +354 -823
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/examples/example.js +2 -5
- package/packages/sdk/dist/examples/example.js.map +1 -1
- package/packages/sdk/dist/index.d.ts +3 -1
- package/packages/sdk/dist/index.d.ts.map +1 -1
- package/packages/sdk/dist/index.js +3 -1
- package/packages/sdk/dist/index.js.map +1 -1
- package/packages/sdk/dist/relay-adapter.d.ts +9 -26
- package/packages/sdk/dist/relay-adapter.d.ts.map +1 -1
- package/packages/sdk/dist/relay-adapter.js +75 -47
- package/packages/sdk/dist/relay-adapter.js.map +1 -1
- package/packages/sdk/dist/relay.d.ts +26 -6
- package/packages/sdk/dist/relay.d.ts.map +1 -1
- package/packages/sdk/dist/relay.js +213 -43
- package/packages/sdk/dist/relay.js.map +1 -1
- package/packages/sdk/dist/transport.d.ts +58 -0
- package/packages/sdk/dist/transport.d.ts.map +1 -0
- package/packages/sdk/dist/transport.js +184 -0
- package/packages/sdk/dist/transport.js.map +1 -0
- package/packages/sdk/dist/types.d.ts +69 -0
- package/packages/sdk/dist/types.d.ts.map +1 -0
- package/packages/sdk/dist/types.js +5 -0
- package/packages/sdk/dist/types.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.js +117 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/run-summary-table.test.js +4 -3
- package/packages/sdk/dist/workflows/__tests__/run-summary-table.test.js.map +1 -1
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.js +378 -0
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.js +145 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.js +170 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.js.map +1 -0
- package/packages/sdk/dist/workflows/builder.d.ts +3 -2
- package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/builder.js +1 -3
- package/packages/sdk/dist/workflows/builder.js.map +1 -1
- package/packages/sdk/dist/workflows/channel-messenger.d.ts +28 -0
- package/packages/sdk/dist/workflows/channel-messenger.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/channel-messenger.js +255 -0
- package/packages/sdk/dist/workflows/channel-messenger.js.map +1 -0
- package/packages/sdk/dist/workflows/index.d.ts +7 -0
- package/packages/sdk/dist/workflows/index.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/index.js +7 -0
- package/packages/sdk/dist/workflows/index.js.map +1 -1
- package/packages/sdk/dist/workflows/process-spawner.d.ts +35 -0
- package/packages/sdk/dist/workflows/process-spawner.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/process-spawner.js +141 -0
- package/packages/sdk/dist/workflows/process-spawner.js.map +1 -0
- package/packages/sdk/dist/workflows/run.d.ts +2 -1
- package/packages/sdk/dist/workflows/run.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/run.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts +6 -6
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +443 -719
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/step-executor.d.ts +95 -0
- package/packages/sdk/dist/workflows/step-executor.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/step-executor.js +393 -0
- package/packages/sdk/dist/workflows/step-executor.js.map +1 -0
- package/packages/sdk/dist/workflows/template-resolver.d.ts +33 -0
- package/packages/sdk/dist/workflows/template-resolver.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/template-resolver.js +144 -0
- package/packages/sdk/dist/workflows/template-resolver.js.map +1 -0
- package/packages/sdk/dist/workflows/validator.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/validator.js +17 -2
- package/packages/sdk/dist/workflows/validator.js.map +1 -1
- package/packages/sdk/dist/workflows/verification.d.ts +33 -0
- package/packages/sdk/dist/workflows/verification.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/verification.js +122 -0
- package/packages/sdk/dist/workflows/verification.js.map +1 -0
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/__tests__/unit.test.ts +100 -1
- package/packages/sdk/src/broker-path.ts +136 -30
- package/packages/sdk/src/client.ts +453 -1069
- package/packages/sdk/src/examples/example.ts +2 -5
- package/packages/sdk/src/index.ts +9 -1
- package/packages/sdk/src/relay-adapter.ts +75 -55
- package/packages/sdk/src/relay.ts +262 -55
- package/packages/sdk/src/transport.ts +216 -0
- package/packages/sdk/src/types.ts +75 -0
- package/packages/sdk/src/workflows/__tests__/channel-messenger.test.ts +137 -0
- package/packages/sdk/src/workflows/__tests__/run-summary-table.test.ts +4 -3
- package/packages/sdk/src/workflows/__tests__/step-executor.test.ts +444 -0
- package/packages/sdk/src/workflows/__tests__/template-resolver.test.ts +162 -0
- package/packages/sdk/src/workflows/__tests__/verification.test.ts +229 -0
- package/packages/sdk/src/workflows/builder.ts +6 -6
- package/packages/sdk/src/workflows/channel-messenger.ts +314 -0
- package/packages/sdk/src/workflows/index.ts +12 -0
- package/packages/sdk/src/workflows/process-spawner.ts +201 -0
- package/packages/sdk/src/workflows/run.ts +2 -1
- package/packages/sdk/src/workflows/runner.ts +636 -951
- package/packages/sdk/src/workflows/step-executor.ts +579 -0
- package/packages/sdk/src/workflows/template-resolver.ts +180 -0
- package/packages/sdk/src/workflows/validator.ts +20 -2
- package/packages/sdk/src/workflows/verification.ts +184 -0
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/sdk-py/src/agent_relay/__init__.py +0 -8
- package/packages/sdk-py/src/agent_relay/client.py +329 -522
- package/packages/sdk-py/src/agent_relay/protocol.py +2 -96
- package/packages/sdk-py/src/agent_relay/relay.py +1 -4
- package/packages/sdk-py/tests/test_wait_for_api_url.py +92 -0
- package/packages/sdk-py/uv.lock +5388 -0
- package/packages/telemetry/dist/client.d.ts.map +1 -1
- package/packages/telemetry/dist/client.js +1 -1
- package/packages/telemetry/dist/client.js.map +1 -1
- package/packages/telemetry/package.json +1 -1
- package/packages/telemetry/src/client.ts +3 -10
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/scripts/postinstall.js +121 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { RelayYamlConfig } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Escape a string for safe inclusion in a shell command passed to `sh -c`.
|
|
5
|
+
* Wraps the value in single quotes and escapes any embedded single quotes.
|
|
6
|
+
*/
|
|
7
|
+
export function shellEscape(value: string): string {
|
|
8
|
+
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const TEMPLATE_VARIABLE_PATTERN = /\{\{([\w][\w.\-]*)\}\}/g;
|
|
12
|
+
const STEP_OUTPUT_TEMPLATE_PATTERN = /\{\{(steps\.[\w\-]+\.output)\}\}/g;
|
|
13
|
+
const STEP_OUTPUT_REF_PATTERN = /^steps\.([\w\-]+)\.output$/;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Variable context for template resolution.
|
|
17
|
+
* Values are typed as `unknown` to accommodate dynamic step-output contexts;
|
|
18
|
+
* only scalar values (string | number | boolean) are interpolated — complex
|
|
19
|
+
* objects are coerced via String(). Shell-bound templates are escaped by
|
|
20
|
+
* {@link resolveTemplateForShell}.
|
|
21
|
+
*/
|
|
22
|
+
export interface VariableContext {
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function resolveVariables(config: RelayYamlConfig, vars: VariableContext): RelayYamlConfig {
|
|
27
|
+
const resolved = structuredClone(config);
|
|
28
|
+
|
|
29
|
+
for (const agent of resolved.agents) {
|
|
30
|
+
if (agent.task) {
|
|
31
|
+
agent.task = resolveTemplate(agent.task, vars);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (resolved.workflows) {
|
|
36
|
+
for (const workflow of resolved.workflows) {
|
|
37
|
+
for (const step of workflow.steps) {
|
|
38
|
+
if (step.task) {
|
|
39
|
+
step.task = resolveTemplate(step.task, vars);
|
|
40
|
+
}
|
|
41
|
+
if (step.command) {
|
|
42
|
+
step.command = resolveTemplateForShell(step.command, vars);
|
|
43
|
+
}
|
|
44
|
+
if (step.params && typeof step.params === 'object') {
|
|
45
|
+
for (const key of Object.keys(step.params)) {
|
|
46
|
+
const value = (step.params as Record<string, unknown>)[key];
|
|
47
|
+
if (typeof value === 'string') {
|
|
48
|
+
(step.params as Record<string, string>)[key] = resolveTemplate(value, vars);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return resolved;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function resolveTemplate(template: string, context: VariableContext): string {
|
|
60
|
+
return template.replace(TEMPLATE_VARIABLE_PATTERN, (match, key: string) => {
|
|
61
|
+
if (key.startsWith('steps.')) {
|
|
62
|
+
return match;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const value = resolveDotPath(key, context);
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
throw new Error(`Unresolved variable: {{${key}}}`);
|
|
68
|
+
}
|
|
69
|
+
return String(value);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Like resolveTemplate but shell-escapes interpolated values.
|
|
75
|
+
* Use this when the result will be passed to `sh -c` to prevent injection.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveTemplateForShell(template: string, context: VariableContext): string {
|
|
78
|
+
return template.replace(TEMPLATE_VARIABLE_PATTERN, (match, key: string) => {
|
|
79
|
+
if (key.startsWith('steps.')) {
|
|
80
|
+
return match;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const value = resolveDotPath(key, context);
|
|
84
|
+
if (value === undefined) {
|
|
85
|
+
throw new Error(`Unresolved variable: {{${key}}}`);
|
|
86
|
+
}
|
|
87
|
+
return shellEscape(String(value));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function resolveDotPath(key: string, context: VariableContext): string | number | boolean | undefined {
|
|
92
|
+
if (!key.includes('.')) {
|
|
93
|
+
return toTemplateScalar(context[key]);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const parts = key.split('.');
|
|
97
|
+
let current: unknown = context;
|
|
98
|
+
for (const part of parts) {
|
|
99
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
current = (current as Record<string, unknown>)[part];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return toTemplateScalar(current);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function resolveStepOutputRef(ref: string, stepOutputs: Map<string, string>): string {
|
|
109
|
+
const normalizedRef = ref.startsWith('{{') && ref.endsWith('}}') ? ref.slice(2, -2).trim() : ref;
|
|
110
|
+
const match = STEP_OUTPUT_REF_PATTERN.exec(normalizedRef);
|
|
111
|
+
if (!match) {
|
|
112
|
+
throw new Error(`Invalid step output reference: ${ref}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const stepOutput = stepOutputs.get(match[1]);
|
|
116
|
+
if (stepOutput === undefined) {
|
|
117
|
+
throw new Error(`Unresolved step output reference: ${ref}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return stepOutput;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function interpolateStepTask(template: string, context: VariableContext): string {
|
|
124
|
+
const stepOutputs = buildStepOutputMap(context);
|
|
125
|
+
return template.replace(STEP_OUTPUT_TEMPLATE_PATTERN, (match, ref: string) => {
|
|
126
|
+
try {
|
|
127
|
+
return resolveStepOutputRef(ref, stepOutputs);
|
|
128
|
+
} catch {
|
|
129
|
+
return match;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function buildStepOutputMap(context: VariableContext): Map<string, string> {
|
|
135
|
+
const stepOutputs = new Map<string, string>();
|
|
136
|
+
const steps = context.steps;
|
|
137
|
+
|
|
138
|
+
if (!steps || typeof steps !== 'object') {
|
|
139
|
+
return stepOutputs;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const [stepName, stepState] of Object.entries(steps as Record<string, unknown>)) {
|
|
143
|
+
if (!stepState || typeof stepState !== 'object') {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const output = toTemplateScalar((stepState as Record<string, unknown>).output);
|
|
148
|
+
if (output !== undefined) {
|
|
149
|
+
stepOutputs.set(stepName, String(output));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return stepOutputs;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function toTemplateScalar(value: unknown): string | number | boolean | undefined {
|
|
157
|
+
if (value === undefined || value === null) return undefined;
|
|
158
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
return String(value);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export class TemplateResolver {
|
|
165
|
+
resolveVariables(config: RelayYamlConfig, vars: VariableContext): RelayYamlConfig {
|
|
166
|
+
return resolveVariables(config, vars);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
interpolate(template: string, vars: VariableContext): string {
|
|
170
|
+
return resolveTemplate(template, vars);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
resolveDotPath(key: string, vars: VariableContext): string | number | boolean | undefined {
|
|
174
|
+
return resolveDotPath(key, vars);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
interpolateStepTask(template: string, context: VariableContext): string {
|
|
178
|
+
return interpolateStepTask(template, context);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RelayYamlConfig, AgentDefinition, WorkflowStep } from './types.js';
|
|
2
|
+
import { CodexModels } from '@agent-relay/config';
|
|
2
3
|
|
|
3
4
|
export interface ValidationIssue {
|
|
4
5
|
severity: 'error' | 'warning' | 'info';
|
|
@@ -117,7 +118,24 @@ export function validateWorkflow(config: RelayYamlConfig): ValidationIssue[] {
|
|
|
117
118
|
});
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
// Check 4:
|
|
121
|
+
// Check 4: codex-spark cannot be used in non-interactive mode
|
|
122
|
+
const CODEX_SPARK_MODELS: string[] = [CodexModels.GPT_5_3_CODEX_SPARK];
|
|
123
|
+
if (
|
|
124
|
+
def.interactive === false &&
|
|
125
|
+
def.cli === 'codex' &&
|
|
126
|
+
def.constraints?.model &&
|
|
127
|
+
(CODEX_SPARK_MODELS.includes(def.constraints.model) || /codex-spark/i.test(def.constraints.model))
|
|
128
|
+
) {
|
|
129
|
+
issues.push({
|
|
130
|
+
severity: 'error',
|
|
131
|
+
code: 'CODEX_SPARK_NON_INTERACTIVE',
|
|
132
|
+
message: `Agent "${step.agent}" uses codex-spark model in non-interactive mode. Codex Spark does not support non-interactive (headless) execution.`,
|
|
133
|
+
fix: `Switch to a different model (e.g. gpt-5.3-codex) or set the agent to interactive mode.`,
|
|
134
|
+
location: `step:${step.name}`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check 5: non-interactive agent that references relay messaging tools in task
|
|
121
139
|
if (
|
|
122
140
|
def.interactive === false &&
|
|
123
141
|
(task.includes('mcp__relaycast__message_dm_send') || task.includes('mcp__relaycast__message_post') || task.includes('mcp__relaycast__message_inbox_check'))
|
|
@@ -132,7 +150,7 @@ export function validateWorkflow(config: RelayYamlConfig): ValidationIssue[] {
|
|
|
132
150
|
}
|
|
133
151
|
}
|
|
134
152
|
|
|
135
|
-
// Check
|
|
153
|
+
// Check 6: maxConcurrency vs interactive agent count
|
|
136
154
|
const interactiveSteps = (workflow.steps ?? []).filter((s) => {
|
|
137
155
|
if (s.type === 'deterministic') return false;
|
|
138
156
|
const def = agentMap.get(s.agent ?? '');
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
CompletionEvidenceSignal,
|
|
6
|
+
CompletionEvidenceToolSideEffect,
|
|
7
|
+
VerificationCheck,
|
|
8
|
+
WorkflowStepCompletionReason,
|
|
9
|
+
} from './types.js';
|
|
10
|
+
|
|
11
|
+
export type { VerificationCheck } from './types.js';
|
|
12
|
+
|
|
13
|
+
export interface VerificationResult {
|
|
14
|
+
passed: boolean;
|
|
15
|
+
completionReason?: WorkflowStepCompletionReason;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface VerificationOptions {
|
|
20
|
+
allowFailure?: boolean;
|
|
21
|
+
completionMarkerFound?: boolean;
|
|
22
|
+
cwd?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class WorkflowCompletionError extends Error {
|
|
26
|
+
completionReason?: WorkflowStepCompletionReason;
|
|
27
|
+
|
|
28
|
+
constructor(message: string, completionReason?: WorkflowStepCompletionReason) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'WorkflowCompletionError';
|
|
31
|
+
this.completionReason = completionReason;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface VerificationSideEffects {
|
|
36
|
+
recordStepToolSideEffect?: (
|
|
37
|
+
stepName: string,
|
|
38
|
+
effect: Omit<CompletionEvidenceToolSideEffect, 'observedAt'> & { observedAt?: string }
|
|
39
|
+
) => void;
|
|
40
|
+
getOrCreateStepEvidenceRecord?: (stepName: string) => {
|
|
41
|
+
evidence: { coordinationSignals: CompletionEvidenceSignal[] };
|
|
42
|
+
};
|
|
43
|
+
log?: (message: string) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function runVerification(
|
|
47
|
+
check: VerificationCheck,
|
|
48
|
+
output: string,
|
|
49
|
+
stepName: string,
|
|
50
|
+
injectedTaskText?: string,
|
|
51
|
+
options: VerificationOptions = {},
|
|
52
|
+
sideEffects: VerificationSideEffects = {}
|
|
53
|
+
): VerificationResult {
|
|
54
|
+
const cwd = options.cwd ?? process.cwd();
|
|
55
|
+
|
|
56
|
+
const fail = (message: string): VerificationResult => {
|
|
57
|
+
const observedAt = new Date().toISOString();
|
|
58
|
+
sideEffects.recordStepToolSideEffect?.(stepName, {
|
|
59
|
+
type: 'verification_observed',
|
|
60
|
+
detail: message,
|
|
61
|
+
observedAt,
|
|
62
|
+
raw: { passed: false, type: check.type, value: check.value },
|
|
63
|
+
});
|
|
64
|
+
sideEffects.getOrCreateStepEvidenceRecord?.(stepName).evidence.coordinationSignals.push({
|
|
65
|
+
kind: 'verification_failed',
|
|
66
|
+
source: 'verification',
|
|
67
|
+
text: message,
|
|
68
|
+
observedAt,
|
|
69
|
+
value: check.value,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (options.allowFailure) {
|
|
73
|
+
return {
|
|
74
|
+
passed: false,
|
|
75
|
+
completionReason: 'failed_verification',
|
|
76
|
+
error: message,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
throw new WorkflowCompletionError(message, 'failed_verification');
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
switch (check.type) {
|
|
84
|
+
case 'output_contains': {
|
|
85
|
+
const token = check.value;
|
|
86
|
+
if (!checkOutputContains(output, token, injectedTaskText)) {
|
|
87
|
+
return fail(`Verification failed for "${stepName}": output does not contain "${token}"`);
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case 'exit_code':
|
|
93
|
+
if (!checkExitCode(check.value)) {
|
|
94
|
+
return fail(`Verification failed for "${stepName}": exit code did not match "${check.value}"`);
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
|
|
98
|
+
case 'file_exists':
|
|
99
|
+
if (!checkFileExists(check.value, cwd)) {
|
|
100
|
+
return fail(`Verification failed for "${stepName}": file "${check.value}" does not exist`);
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
|
|
104
|
+
case 'custom':
|
|
105
|
+
return { passed: false };
|
|
106
|
+
|
|
107
|
+
default:
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (options.completionMarkerFound === false) {
|
|
112
|
+
sideEffects.log?.(
|
|
113
|
+
`[${stepName}] Verification passed without legacy STEP_COMPLETE marker; allowing completion`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const observedAt = new Date().toISOString();
|
|
118
|
+
const successMessage =
|
|
119
|
+
options.completionMarkerFound === false
|
|
120
|
+
? 'Verification passed without legacy STEP_COMPLETE marker'
|
|
121
|
+
: 'Verification passed';
|
|
122
|
+
sideEffects.recordStepToolSideEffect?.(stepName, {
|
|
123
|
+
type: 'verification_observed',
|
|
124
|
+
detail: successMessage,
|
|
125
|
+
observedAt,
|
|
126
|
+
raw: { passed: true, type: check.type, value: check.value },
|
|
127
|
+
});
|
|
128
|
+
sideEffects.getOrCreateStepEvidenceRecord?.(stepName).evidence.coordinationSignals.push({
|
|
129
|
+
kind: 'verification_passed',
|
|
130
|
+
source: 'verification',
|
|
131
|
+
text: successMessage,
|
|
132
|
+
observedAt,
|
|
133
|
+
value: check.value,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
passed: true,
|
|
138
|
+
completionReason: 'completed_verified',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function stripInjectedTaskEcho(output: string, injectedTaskText?: string): string {
|
|
143
|
+
if (!injectedTaskText) {
|
|
144
|
+
return output;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const candidates = [
|
|
148
|
+
injectedTaskText,
|
|
149
|
+
injectedTaskText.replace(/\r\n/g, '\n'),
|
|
150
|
+
injectedTaskText.replace(/\n/g, '\r\n'),
|
|
151
|
+
].filter((candidate, index, all) => candidate.length > 0 && all.indexOf(candidate) === index);
|
|
152
|
+
|
|
153
|
+
for (const candidate of candidates) {
|
|
154
|
+
const start = output.indexOf(candidate);
|
|
155
|
+
if (start !== -1) {
|
|
156
|
+
return output.slice(0, start) + output.slice(start + candidate.length);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return output;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function checkExitCode(_expectedExitCode: string): boolean {
|
|
164
|
+
// Existing runner semantics treat process success as established before this
|
|
165
|
+
// verification hook runs, so this check is currently an unconditional pass.
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function checkOutputContains(output: string, token: string, injectedTaskText?: string): boolean {
|
|
170
|
+
if (!token) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return stripInjectedTaskEcho(output, injectedTaskText).includes(token);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function checkFileExists(filePath: string, cwd = process.cwd()): boolean {
|
|
177
|
+
const normalizedCwd = path.resolve(cwd);
|
|
178
|
+
const resolved = path.resolve(normalizedCwd, filePath);
|
|
179
|
+
// Prevent path traversal outside the working directory
|
|
180
|
+
if (!resolved.startsWith(normalizedCwd + path.sep) && resolved !== normalizedCwd) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
return existsSync(resolved);
|
|
184
|
+
}
|
|
@@ -13,13 +13,9 @@ except ImportError:
|
|
|
13
13
|
from .models import Models
|
|
14
14
|
from .client import AgentRelayClient, AgentRelayProtocolError, AgentRelayProcessError
|
|
15
15
|
from .protocol import (
|
|
16
|
-
PROTOCOL_VERSION,
|
|
17
16
|
AgentRuntime,
|
|
18
|
-
AgentSpec,
|
|
19
17
|
BrokerEvent,
|
|
20
18
|
MessageInjectionMode,
|
|
21
|
-
ProtocolEnvelope,
|
|
22
|
-
RestartPolicy as ProtocolRestartPolicy,
|
|
23
19
|
)
|
|
24
20
|
|
|
25
21
|
# ── Secondary API: Workflow builder (backward compatibility) ──────────────────
|
|
@@ -89,13 +85,9 @@ __all__ = [
|
|
|
89
85
|
"AgentRelayClient",
|
|
90
86
|
"AgentRelayProtocolError",
|
|
91
87
|
"AgentRelayProcessError",
|
|
92
|
-
"PROTOCOL_VERSION",
|
|
93
88
|
"AgentRuntime",
|
|
94
|
-
"AgentSpec",
|
|
95
89
|
"BrokerEvent",
|
|
96
90
|
"MessageInjectionMode",
|
|
97
|
-
"ProtocolEnvelope",
|
|
98
|
-
"ProtocolRestartPolicy",
|
|
99
91
|
# Workflow builder (backward compat)
|
|
100
92
|
"workflow",
|
|
101
93
|
"WorkflowBuilder",
|