agent-relay 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +5 -5
  2. package/bin/agent-relay-broker-darwin-arm64 +0 -0
  3. package/bin/agent-relay-broker-darwin-x64 +0 -0
  4. package/bin/agent-relay-broker-linux-arm64 +0 -0
  5. package/bin/agent-relay-broker-linux-x64 +0 -0
  6. package/dist/index.cjs +1980 -1024
  7. package/dist/src/cli/commands/core.d.ts.map +1 -1
  8. package/dist/src/cli/commands/core.js +4 -3
  9. package/dist/src/cli/commands/core.js.map +1 -1
  10. package/dist/src/cli/commands/on/relayfile-binary.d.ts +2 -0
  11. package/dist/src/cli/commands/on/relayfile-binary.d.ts.map +1 -0
  12. package/dist/src/cli/commands/on/relayfile-binary.js +208 -0
  13. package/dist/src/cli/commands/on/relayfile-binary.js.map +1 -0
  14. package/dist/src/cli/commands/on/start.d.ts.map +1 -1
  15. package/dist/src/cli/commands/on/start.js +62 -26
  16. package/dist/src/cli/commands/on/start.js.map +1 -1
  17. package/dist/src/cli/commands/on/workspace.d.ts.map +1 -1
  18. package/dist/src/cli/commands/on/workspace.js +4 -0
  19. package/dist/src/cli/commands/on/workspace.js.map +1 -1
  20. package/dist/src/cli/commands/on.js +1 -1
  21. package/dist/src/cli/commands/on.js.map +1 -1
  22. package/dist/src/cli/lib/broker-lifecycle.d.ts.map +1 -1
  23. package/dist/src/cli/lib/broker-lifecycle.js +15 -8
  24. package/dist/src/cli/lib/broker-lifecycle.js.map +1 -1
  25. package/dist/src/cli/lib/client-factory.d.ts +2 -2
  26. package/dist/src/cli/lib/client-factory.d.ts.map +1 -1
  27. package/dist/src/cli/lib/client-factory.js.map +1 -1
  28. package/dist/src/cost/pricing.d.ts +18 -0
  29. package/dist/src/cost/pricing.d.ts.map +1 -0
  30. package/dist/src/cost/pricing.js +111 -0
  31. package/dist/src/cost/pricing.js.map +1 -0
  32. package/dist/src/cost/tracker.d.ts +13 -0
  33. package/dist/src/cost/tracker.d.ts.map +1 -0
  34. package/dist/src/cost/tracker.js +152 -0
  35. package/dist/src/cost/tracker.js.map +1 -0
  36. package/dist/src/cost/types.d.ts +23 -0
  37. package/dist/src/cost/types.d.ts.map +1 -0
  38. package/dist/src/cost/types.js +2 -0
  39. package/dist/src/cost/types.js.map +1 -0
  40. package/package.json +10 -10
  41. package/packages/acp-bridge/package.json +2 -2
  42. package/packages/brand/package.json +1 -1
  43. package/packages/cloud/package.json +3 -3
  44. package/packages/config/package.json +1 -1
  45. package/packages/hooks/package.json +4 -4
  46. package/packages/memory/package.json +2 -2
  47. package/packages/openclaw/package.json +2 -2
  48. package/packages/policy/package.json +2 -2
  49. package/packages/sdk/dist/broker-path.d.ts +3 -2
  50. package/packages/sdk/dist/broker-path.d.ts.map +1 -1
  51. package/packages/sdk/dist/broker-path.js +119 -32
  52. package/packages/sdk/dist/broker-path.js.map +1 -1
  53. package/packages/sdk/dist/client.d.ts +12 -2
  54. package/packages/sdk/dist/client.d.ts.map +1 -1
  55. package/packages/sdk/dist/client.js +20 -1
  56. package/packages/sdk/dist/client.js.map +1 -1
  57. package/packages/sdk/dist/index.d.ts +1 -1
  58. package/packages/sdk/dist/index.d.ts.map +1 -1
  59. package/packages/sdk/dist/index.js.map +1 -1
  60. package/packages/sdk/dist/relay.d.ts +2 -1
  61. package/packages/sdk/dist/relay.d.ts.map +1 -1
  62. package/packages/sdk/dist/relay.js +1 -1
  63. package/packages/sdk/dist/relay.js.map +1 -1
  64. package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.d.ts +2 -0
  65. package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.d.ts.map +1 -0
  66. package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.js +117 -0
  67. package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.js.map +1 -0
  68. package/packages/sdk/dist/workflows/__tests__/run-summary-table.test.js +4 -3
  69. package/packages/sdk/dist/workflows/__tests__/run-summary-table.test.js.map +1 -1
  70. package/packages/sdk/dist/workflows/__tests__/step-executor.test.d.ts +2 -0
  71. package/packages/sdk/dist/workflows/__tests__/step-executor.test.d.ts.map +1 -0
  72. package/packages/sdk/dist/workflows/__tests__/step-executor.test.js +378 -0
  73. package/packages/sdk/dist/workflows/__tests__/step-executor.test.js.map +1 -0
  74. package/packages/sdk/dist/workflows/__tests__/template-resolver.test.d.ts +2 -0
  75. package/packages/sdk/dist/workflows/__tests__/template-resolver.test.d.ts.map +1 -0
  76. package/packages/sdk/dist/workflows/__tests__/template-resolver.test.js +145 -0
  77. package/packages/sdk/dist/workflows/__tests__/template-resolver.test.js.map +1 -0
  78. package/packages/sdk/dist/workflows/__tests__/verification.test.d.ts +2 -0
  79. package/packages/sdk/dist/workflows/__tests__/verification.test.d.ts.map +1 -0
  80. package/packages/sdk/dist/workflows/__tests__/verification.test.js +170 -0
  81. package/packages/sdk/dist/workflows/__tests__/verification.test.js.map +1 -0
  82. package/packages/sdk/dist/workflows/builder.d.ts +3 -2
  83. package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
  84. package/packages/sdk/dist/workflows/builder.js +1 -3
  85. package/packages/sdk/dist/workflows/builder.js.map +1 -1
  86. package/packages/sdk/dist/workflows/channel-messenger.d.ts +28 -0
  87. package/packages/sdk/dist/workflows/channel-messenger.d.ts.map +1 -0
  88. package/packages/sdk/dist/workflows/channel-messenger.js +255 -0
  89. package/packages/sdk/dist/workflows/channel-messenger.js.map +1 -0
  90. package/packages/sdk/dist/workflows/index.d.ts +7 -0
  91. package/packages/sdk/dist/workflows/index.d.ts.map +1 -1
  92. package/packages/sdk/dist/workflows/index.js +7 -0
  93. package/packages/sdk/dist/workflows/index.js.map +1 -1
  94. package/packages/sdk/dist/workflows/process-spawner.d.ts +35 -0
  95. package/packages/sdk/dist/workflows/process-spawner.d.ts.map +1 -0
  96. package/packages/sdk/dist/workflows/process-spawner.js +141 -0
  97. package/packages/sdk/dist/workflows/process-spawner.js.map +1 -0
  98. package/packages/sdk/dist/workflows/run.d.ts +2 -1
  99. package/packages/sdk/dist/workflows/run.d.ts.map +1 -1
  100. package/packages/sdk/dist/workflows/run.js.map +1 -1
  101. package/packages/sdk/dist/workflows/runner.d.ts +6 -6
  102. package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
  103. package/packages/sdk/dist/workflows/runner.js +443 -719
  104. package/packages/sdk/dist/workflows/runner.js.map +1 -1
  105. package/packages/sdk/dist/workflows/step-executor.d.ts +95 -0
  106. package/packages/sdk/dist/workflows/step-executor.d.ts.map +1 -0
  107. package/packages/sdk/dist/workflows/step-executor.js +393 -0
  108. package/packages/sdk/dist/workflows/step-executor.js.map +1 -0
  109. package/packages/sdk/dist/workflows/template-resolver.d.ts +33 -0
  110. package/packages/sdk/dist/workflows/template-resolver.d.ts.map +1 -0
  111. package/packages/sdk/dist/workflows/template-resolver.js +144 -0
  112. package/packages/sdk/dist/workflows/template-resolver.js.map +1 -0
  113. package/packages/sdk/dist/workflows/verification.d.ts +33 -0
  114. package/packages/sdk/dist/workflows/verification.d.ts.map +1 -0
  115. package/packages/sdk/dist/workflows/verification.js +122 -0
  116. package/packages/sdk/dist/workflows/verification.js.map +1 -0
  117. package/packages/sdk/package.json +2 -2
  118. package/packages/sdk/src/broker-path.ts +136 -30
  119. package/packages/sdk/src/client.ts +37 -3
  120. package/packages/sdk/src/index.ts +1 -0
  121. package/packages/sdk/src/relay.ts +6 -2
  122. package/packages/sdk/src/workflows/__tests__/channel-messenger.test.ts +137 -0
  123. package/packages/sdk/src/workflows/__tests__/run-summary-table.test.ts +4 -3
  124. package/packages/sdk/src/workflows/__tests__/step-executor.test.ts +444 -0
  125. package/packages/sdk/src/workflows/__tests__/template-resolver.test.ts +162 -0
  126. package/packages/sdk/src/workflows/__tests__/verification.test.ts +229 -0
  127. package/packages/sdk/src/workflows/builder.ts +6 -6
  128. package/packages/sdk/src/workflows/channel-messenger.ts +314 -0
  129. package/packages/sdk/src/workflows/index.ts +12 -0
  130. package/packages/sdk/src/workflows/process-spawner.ts +201 -0
  131. package/packages/sdk/src/workflows/run.ts +2 -1
  132. package/packages/sdk/src/workflows/runner.ts +636 -951
  133. package/packages/sdk/src/workflows/step-executor.ts +579 -0
  134. package/packages/sdk/src/workflows/template-resolver.ts +180 -0
  135. package/packages/sdk/src/workflows/verification.ts +184 -0
  136. package/packages/sdk-py/pyproject.toml +1 -1
  137. package/packages/telemetry/package.json +1 -1
  138. package/packages/trajectory/package.json +2 -2
  139. package/packages/user-directory/package.json +2 -2
  140. package/packages/utils/package.json +2 -2
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Escape a string for safe inclusion in a shell command passed to `sh -c`.
3
+ * Wraps the value in single quotes and escapes any embedded single quotes.
4
+ */
5
+ export function shellEscape(value) {
6
+ return "'" + value.replace(/'/g, "'\\''") + "'";
7
+ }
8
+ const TEMPLATE_VARIABLE_PATTERN = /\{\{([\w][\w.\-]*)\}\}/g;
9
+ const STEP_OUTPUT_TEMPLATE_PATTERN = /\{\{(steps\.[\w\-]+\.output)\}\}/g;
10
+ const STEP_OUTPUT_REF_PATTERN = /^steps\.([\w\-]+)\.output$/;
11
+ export function resolveVariables(config, vars) {
12
+ const resolved = structuredClone(config);
13
+ for (const agent of resolved.agents) {
14
+ if (agent.task) {
15
+ agent.task = resolveTemplate(agent.task, vars);
16
+ }
17
+ }
18
+ if (resolved.workflows) {
19
+ for (const workflow of resolved.workflows) {
20
+ for (const step of workflow.steps) {
21
+ if (step.task) {
22
+ step.task = resolveTemplate(step.task, vars);
23
+ }
24
+ if (step.command) {
25
+ step.command = resolveTemplateForShell(step.command, vars);
26
+ }
27
+ if (step.params && typeof step.params === 'object') {
28
+ for (const key of Object.keys(step.params)) {
29
+ const value = step.params[key];
30
+ if (typeof value === 'string') {
31
+ step.params[key] = resolveTemplate(value, vars);
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ return resolved;
39
+ }
40
+ export function resolveTemplate(template, context) {
41
+ return template.replace(TEMPLATE_VARIABLE_PATTERN, (match, key) => {
42
+ if (key.startsWith('steps.')) {
43
+ return match;
44
+ }
45
+ const value = resolveDotPath(key, context);
46
+ if (value === undefined) {
47
+ throw new Error(`Unresolved variable: {{${key}}}`);
48
+ }
49
+ return String(value);
50
+ });
51
+ }
52
+ /**
53
+ * Like resolveTemplate but shell-escapes interpolated values.
54
+ * Use this when the result will be passed to `sh -c` to prevent injection.
55
+ */
56
+ export function resolveTemplateForShell(template, context) {
57
+ return template.replace(TEMPLATE_VARIABLE_PATTERN, (match, key) => {
58
+ if (key.startsWith('steps.')) {
59
+ return match;
60
+ }
61
+ const value = resolveDotPath(key, context);
62
+ if (value === undefined) {
63
+ throw new Error(`Unresolved variable: {{${key}}}`);
64
+ }
65
+ return shellEscape(String(value));
66
+ });
67
+ }
68
+ export function resolveDotPath(key, context) {
69
+ if (!key.includes('.')) {
70
+ return toTemplateScalar(context[key]);
71
+ }
72
+ const parts = key.split('.');
73
+ let current = context;
74
+ for (const part of parts) {
75
+ if (current === null || current === undefined || typeof current !== 'object') {
76
+ return undefined;
77
+ }
78
+ current = current[part];
79
+ }
80
+ return toTemplateScalar(current);
81
+ }
82
+ export function resolveStepOutputRef(ref, stepOutputs) {
83
+ const normalizedRef = ref.startsWith('{{') && ref.endsWith('}}') ? ref.slice(2, -2).trim() : ref;
84
+ const match = STEP_OUTPUT_REF_PATTERN.exec(normalizedRef);
85
+ if (!match) {
86
+ throw new Error(`Invalid step output reference: ${ref}`);
87
+ }
88
+ const stepOutput = stepOutputs.get(match[1]);
89
+ if (stepOutput === undefined) {
90
+ throw new Error(`Unresolved step output reference: ${ref}`);
91
+ }
92
+ return stepOutput;
93
+ }
94
+ export function interpolateStepTask(template, context) {
95
+ const stepOutputs = buildStepOutputMap(context);
96
+ return template.replace(STEP_OUTPUT_TEMPLATE_PATTERN, (match, ref) => {
97
+ try {
98
+ return resolveStepOutputRef(ref, stepOutputs);
99
+ }
100
+ catch {
101
+ return match;
102
+ }
103
+ });
104
+ }
105
+ function buildStepOutputMap(context) {
106
+ const stepOutputs = new Map();
107
+ const steps = context.steps;
108
+ if (!steps || typeof steps !== 'object') {
109
+ return stepOutputs;
110
+ }
111
+ for (const [stepName, stepState] of Object.entries(steps)) {
112
+ if (!stepState || typeof stepState !== 'object') {
113
+ continue;
114
+ }
115
+ const output = toTemplateScalar(stepState.output);
116
+ if (output !== undefined) {
117
+ stepOutputs.set(stepName, String(output));
118
+ }
119
+ }
120
+ return stepOutputs;
121
+ }
122
+ function toTemplateScalar(value) {
123
+ if (value === undefined || value === null)
124
+ return undefined;
125
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
126
+ return value;
127
+ }
128
+ return String(value);
129
+ }
130
+ export class TemplateResolver {
131
+ resolveVariables(config, vars) {
132
+ return resolveVariables(config, vars);
133
+ }
134
+ interpolate(template, vars) {
135
+ return resolveTemplate(template, vars);
136
+ }
137
+ resolveDotPath(key, vars) {
138
+ return resolveDotPath(key, vars);
139
+ }
140
+ interpolateStepTask(template, context) {
141
+ return interpolateStepTask(template, context);
142
+ }
143
+ }
144
+ //# sourceMappingURL=template-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-resolver.js","sourceRoot":"","sources":["../../src/workflows/template-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { CompletionEvidenceSignal, CompletionEvidenceToolSideEffect, VerificationCheck, WorkflowStepCompletionReason } from './types.js';
2
+ export type { VerificationCheck } from './types.js';
3
+ export interface VerificationResult {
4
+ passed: boolean;
5
+ completionReason?: WorkflowStepCompletionReason;
6
+ error?: string;
7
+ }
8
+ export interface VerificationOptions {
9
+ allowFailure?: boolean;
10
+ completionMarkerFound?: boolean;
11
+ cwd?: string;
12
+ }
13
+ export declare class WorkflowCompletionError extends Error {
14
+ completionReason?: WorkflowStepCompletionReason;
15
+ constructor(message: string, completionReason?: WorkflowStepCompletionReason);
16
+ }
17
+ export interface VerificationSideEffects {
18
+ recordStepToolSideEffect?: (stepName: string, effect: Omit<CompletionEvidenceToolSideEffect, 'observedAt'> & {
19
+ observedAt?: string;
20
+ }) => void;
21
+ getOrCreateStepEvidenceRecord?: (stepName: string) => {
22
+ evidence: {
23
+ coordinationSignals: CompletionEvidenceSignal[];
24
+ };
25
+ };
26
+ log?: (message: string) => void;
27
+ }
28
+ export declare function runVerification(check: VerificationCheck, output: string, stepName: string, injectedTaskText?: string, options?: VerificationOptions, sideEffects?: VerificationSideEffects): VerificationResult;
29
+ export declare function stripInjectedTaskEcho(output: string, injectedTaskText?: string): string;
30
+ export declare function checkExitCode(_expectedExitCode: string): boolean;
31
+ export declare function checkOutputContains(output: string, token: string, injectedTaskText?: string): boolean;
32
+ export declare function checkFileExists(filePath: string, cwd?: string): boolean;
33
+ //# sourceMappingURL=verification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../src/workflows/verification.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,wBAAwB,EACxB,gCAAgC,EAChC,iBAAiB,EACjB,4BAA4B,EAC7B,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,4BAA4B,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,gBAAgB,CAAC,EAAE,4BAA4B,CAAC;gBAEpC,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,4BAA4B;CAK7E;AAED,MAAM,WAAW,uBAAuB;IACtC,wBAAwB,CAAC,EAAE,CACzB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,CAAC,gCAAgC,EAAE,YAAY,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KACnF,IAAI,CAAC;IACV,6BAA6B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK;QACpD,QAAQ,EAAE;YAAE,mBAAmB,EAAE,wBAAwB,EAAE,CAAA;SAAE,CAAC;KAC/D,CAAC;IACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,MAAM,EACzB,OAAO,GAAE,mBAAwB,EACjC,WAAW,GAAE,uBAA4B,GACxC,kBAAkB,CAuFpB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAmBvF;AAED,wBAAgB,aAAa,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAIhE;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAKrG;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAgB,GAAG,OAAO,CAQ9E"}
@@ -0,0 +1,122 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ export class WorkflowCompletionError extends Error {
4
+ completionReason;
5
+ constructor(message, completionReason) {
6
+ super(message);
7
+ this.name = 'WorkflowCompletionError';
8
+ this.completionReason = completionReason;
9
+ }
10
+ }
11
+ export function runVerification(check, output, stepName, injectedTaskText, options = {}, sideEffects = {}) {
12
+ const cwd = options.cwd ?? process.cwd();
13
+ const fail = (message) => {
14
+ const observedAt = new Date().toISOString();
15
+ sideEffects.recordStepToolSideEffect?.(stepName, {
16
+ type: 'verification_observed',
17
+ detail: message,
18
+ observedAt,
19
+ raw: { passed: false, type: check.type, value: check.value },
20
+ });
21
+ sideEffects.getOrCreateStepEvidenceRecord?.(stepName).evidence.coordinationSignals.push({
22
+ kind: 'verification_failed',
23
+ source: 'verification',
24
+ text: message,
25
+ observedAt,
26
+ value: check.value,
27
+ });
28
+ if (options.allowFailure) {
29
+ return {
30
+ passed: false,
31
+ completionReason: 'failed_verification',
32
+ error: message,
33
+ };
34
+ }
35
+ throw new WorkflowCompletionError(message, 'failed_verification');
36
+ };
37
+ switch (check.type) {
38
+ case 'output_contains': {
39
+ const token = check.value;
40
+ if (!checkOutputContains(output, token, injectedTaskText)) {
41
+ return fail(`Verification failed for "${stepName}": output does not contain "${token}"`);
42
+ }
43
+ break;
44
+ }
45
+ case 'exit_code':
46
+ if (!checkExitCode(check.value)) {
47
+ return fail(`Verification failed for "${stepName}": exit code did not match "${check.value}"`);
48
+ }
49
+ break;
50
+ case 'file_exists':
51
+ if (!checkFileExists(check.value, cwd)) {
52
+ return fail(`Verification failed for "${stepName}": file "${check.value}" does not exist`);
53
+ }
54
+ break;
55
+ case 'custom':
56
+ return { passed: false };
57
+ default:
58
+ break;
59
+ }
60
+ if (options.completionMarkerFound === false) {
61
+ sideEffects.log?.(`[${stepName}] Verification passed without legacy STEP_COMPLETE marker; allowing completion`);
62
+ }
63
+ const observedAt = new Date().toISOString();
64
+ const successMessage = options.completionMarkerFound === false
65
+ ? 'Verification passed without legacy STEP_COMPLETE marker'
66
+ : 'Verification passed';
67
+ sideEffects.recordStepToolSideEffect?.(stepName, {
68
+ type: 'verification_observed',
69
+ detail: successMessage,
70
+ observedAt,
71
+ raw: { passed: true, type: check.type, value: check.value },
72
+ });
73
+ sideEffects.getOrCreateStepEvidenceRecord?.(stepName).evidence.coordinationSignals.push({
74
+ kind: 'verification_passed',
75
+ source: 'verification',
76
+ text: successMessage,
77
+ observedAt,
78
+ value: check.value,
79
+ });
80
+ return {
81
+ passed: true,
82
+ completionReason: 'completed_verified',
83
+ };
84
+ }
85
+ export function stripInjectedTaskEcho(output, injectedTaskText) {
86
+ if (!injectedTaskText) {
87
+ return output;
88
+ }
89
+ const candidates = [
90
+ injectedTaskText,
91
+ injectedTaskText.replace(/\r\n/g, '\n'),
92
+ injectedTaskText.replace(/\n/g, '\r\n'),
93
+ ].filter((candidate, index, all) => candidate.length > 0 && all.indexOf(candidate) === index);
94
+ for (const candidate of candidates) {
95
+ const start = output.indexOf(candidate);
96
+ if (start !== -1) {
97
+ return output.slice(0, start) + output.slice(start + candidate.length);
98
+ }
99
+ }
100
+ return output;
101
+ }
102
+ export function checkExitCode(_expectedExitCode) {
103
+ // Existing runner semantics treat process success as established before this
104
+ // verification hook runs, so this check is currently an unconditional pass.
105
+ return true;
106
+ }
107
+ export function checkOutputContains(output, token, injectedTaskText) {
108
+ if (!token) {
109
+ return false;
110
+ }
111
+ return stripInjectedTaskEcho(output, injectedTaskText).includes(token);
112
+ }
113
+ export function checkFileExists(filePath, cwd = process.cwd()) {
114
+ const normalizedCwd = path.resolve(cwd);
115
+ const resolved = path.resolve(normalizedCwd, filePath);
116
+ // Prevent path traversal outside the working directory
117
+ if (!resolved.startsWith(normalizedCwd + path.sep) && resolved !== normalizedCwd) {
118
+ return false;
119
+ }
120
+ return existsSync(resolved);
121
+ }
122
+ //# sourceMappingURL=verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/workflows/verification.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-relay/sdk",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -112,7 +112,7 @@
112
112
  "typescript": "^5.7.3"
113
113
  },
114
114
  "dependencies": {
115
- "@agent-relay/config": "4.0.0",
115
+ "@agent-relay/config": "4.0.2",
116
116
  "@relaycast/sdk": "^1.1.0",
117
117
  "@sinclair/typebox": "^0.34.48",
118
118
  "chalk": "^4.1.2",
@@ -7,64 +7,170 @@
7
7
  */
8
8
 
9
9
  import { existsSync } from 'node:fs';
10
- import { join, dirname } from 'node:path';
11
- import { execSync } from 'node:child_process';
10
+ import { join, dirname, resolve } from 'node:path';
11
+ import { execFileSync } from 'node:child_process';
12
12
  import { createRequire } from 'node:module';
13
13
  import { fileURLToPath } from 'node:url';
14
14
 
15
15
  const BROKER_NAME = 'agent-relay-broker';
16
16
 
17
+ function addUniquePath(paths: string[], candidate: string | null | undefined): void {
18
+ if (!candidate || paths.includes(candidate)) {
19
+ return;
20
+ }
21
+ paths.push(candidate);
22
+ }
23
+
24
+ function getImportMetaUrl(): string | null {
25
+ try {
26
+ return import.meta.url;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ function getCurrentModuleDir(): string | null {
33
+ if (typeof __dirname === 'string' && __dirname) {
34
+ return __dirname;
35
+ }
36
+ if (typeof __filename === 'string' && __filename) {
37
+ return dirname(__filename);
38
+ }
39
+
40
+ const importMetaUrl = getImportMetaUrl();
41
+ if (!importMetaUrl) {
42
+ return null;
43
+ }
44
+
45
+ try {
46
+ return dirname(fileURLToPath(importMetaUrl));
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ function getCurrentModuleReference(): string | null {
53
+ if (typeof __filename === 'string' && __filename) {
54
+ return __filename;
55
+ }
56
+ if (typeof __dirname === 'string' && __dirname) {
57
+ return join(__dirname, 'broker-path.js');
58
+ }
59
+
60
+ return getImportMetaUrl();
61
+ }
62
+
63
+ function getSdkBinDirs(): string[] {
64
+ const binDirs: string[] = [];
65
+
66
+ const currentModuleDir = getCurrentModuleDir();
67
+ if (currentModuleDir) {
68
+ addUniquePath(binDirs, resolve(currentModuleDir, '..', 'bin'));
69
+ }
70
+
71
+ const currentModuleReference = getCurrentModuleReference();
72
+ if (currentModuleReference) {
73
+ try {
74
+ const sdkEntry = createRequire(currentModuleReference).resolve('@agent-relay/sdk');
75
+ addUniquePath(binDirs, resolve(dirname(sdkEntry), '..', 'bin'));
76
+ } catch {
77
+ // Continue with other resolution strategies.
78
+ }
79
+ }
80
+
81
+ const importMetaUrl = getImportMetaUrl();
82
+ if (importMetaUrl) {
83
+ try {
84
+ addUniquePath(binDirs, resolve(dirname(fileURLToPath(importMetaUrl)), '..', 'bin'));
85
+ } catch {
86
+ // Continue with other resolution strategies.
87
+ }
88
+ }
89
+
90
+ return binDirs;
91
+ }
92
+
93
+ function getDevelopmentBinaryPaths(ext: string, binDirs: string[]): string[] {
94
+ const binaryPaths: string[] = [];
95
+ const repoRoots = new Set<string>();
96
+
97
+ const addRepoRoot = (candidate: string | null | undefined): void => {
98
+ if (!candidate) {
99
+ return;
100
+ }
101
+
102
+ const repoRoot = resolve(candidate);
103
+ if (repoRoots.has(repoRoot)) {
104
+ return;
105
+ }
106
+ repoRoots.add(repoRoot);
107
+
108
+ addUniquePath(binaryPaths, join(repoRoot, 'target', 'release', `${BROKER_NAME}${ext}`));
109
+ addUniquePath(binaryPaths, join(repoRoot, 'target', 'debug', `${BROKER_NAME}${ext}`));
110
+ };
111
+
112
+ addRepoRoot(process.cwd());
113
+
114
+ const currentModuleDir = getCurrentModuleDir();
115
+ if (currentModuleDir) {
116
+ addRepoRoot(resolve(currentModuleDir, '..', '..', '..'));
117
+ }
118
+
119
+ for (const binDir of binDirs) {
120
+ addRepoRoot(resolve(binDir, '..', '..', '..'));
121
+ }
122
+
123
+ return binaryPaths;
124
+ }
125
+
17
126
  /**
18
127
  * Resolve the agent-relay-broker binary path.
19
128
  *
20
129
  * Search order:
21
- * 1. SDK's bin/ directory (resolved via createRequire or import.meta.url)
130
+ * 1. SDK's bin/ directory (resolved via CJS globals, createRequire, or import.meta.url)
22
131
  * 2. Platform-specific name (agent-relay-broker-{platform}-{arch}) in bin/
23
- * 3. PATH lookup via `which` / `where`
132
+ * 3. Common Cargo development paths (target/release and target/debug)
133
+ * 4. PATH lookup via `which` / `where`
24
134
  *
25
135
  * @returns Absolute path to the broker binary, or null if not found
26
136
  */
27
137
  export function getBrokerBinaryPath(): string | null {
28
- let binDir: string | null = null;
29
- try {
30
- // Use createRequire for ESM-compatible require.resolve
31
- const esmRequire = createRequire(import.meta.url);
32
- const sdkEntry = esmRequire.resolve('@agent-relay/sdk');
33
- binDir = join(dirname(sdkEntry), '..', 'bin');
34
- } catch {
35
- try {
36
- // Fallback: derive from import.meta.url
37
- binDir = join(dirname(dirname(fileURLToPath(import.meta.url))), 'bin');
38
- } catch {
39
- // Neither method worked
40
- }
41
- }
42
- if (!binDir) return null;
43
-
44
138
  const ext = process.platform === 'win32' ? '.exe' : '';
139
+ const binDirs = getSdkBinDirs();
140
+ const platformSpecific = `${BROKER_NAME}-${process.platform}-${process.arch}${ext}`;
45
141
 
46
142
  // 1. Exact name in bin/
47
- const exactPath = join(binDir, `${BROKER_NAME}${ext}`);
48
- if (existsSync(exactPath)) {
49
- return exactPath;
143
+ for (const binDir of binDirs) {
144
+ const exactPath = join(binDir, `${BROKER_NAME}${ext}`);
145
+ if (existsSync(exactPath)) {
146
+ return exactPath;
147
+ }
50
148
  }
51
149
 
52
150
  // 2. Platform-specific name in bin/
53
- const platformSpecific = `${BROKER_NAME}-${process.platform}-${process.arch}${ext}`;
54
- const platformPath = join(binDir, platformSpecific);
55
- if (existsSync(platformPath)) {
56
- return platformPath;
151
+ for (const binDir of binDirs) {
152
+ const platformPath = join(binDir, platformSpecific);
153
+ if (existsSync(platformPath)) {
154
+ return platformPath;
155
+ }
156
+ }
157
+
158
+ // 3. Common development paths for local Cargo builds.
159
+ for (const developmentPath of getDevelopmentBinaryPaths(ext, binDirs)) {
160
+ if (existsSync(developmentPath)) {
161
+ return developmentPath;
162
+ }
57
163
  }
58
164
 
59
- // 3. PATH lookup
165
+ // 4. PATH lookup
60
166
  try {
61
167
  const cmd = process.platform === 'win32' ? 'where' : 'which';
62
- const result = execSync(`${cmd} ${BROKER_NAME}`, {
168
+ const result = execFileSync(cmd, [BROKER_NAME], {
63
169
  encoding: 'utf-8',
64
170
  stdio: ['pipe', 'pipe', 'pipe'],
65
171
  }).trim();
66
172
  if (result) {
67
- return result.split('\n')[0].trim();
173
+ return result.split(/\r?\n/u)[0].trim();
68
174
  }
69
175
  } catch {
70
176
  // Not found on PATH
@@ -41,11 +41,22 @@ export interface AgentRelayClientOptions {
41
41
  requestTimeoutMs?: number;
42
42
  }
43
43
 
44
+ export interface AgentRelayBrokerInitArgs {
45
+ /** Optional HTTP API port for dashboard proxy (0 = disabled). */
46
+ apiPort?: number;
47
+ /** Bind address for the HTTP API. Defaults to 127.0.0.1 in the broker. */
48
+ apiBind?: string;
49
+ /** Enable persistence for broker state under the working directory. */
50
+ persist?: boolean;
51
+ /** Override the directory used for broker state files. */
52
+ stateDir?: string;
53
+ }
54
+
44
55
  export interface AgentRelaySpawnOptions {
45
56
  /** Path to the agent-relay-broker binary. Auto-resolved if omitted. */
46
57
  binaryPath?: string;
47
- /** Extra args passed to `broker init` (e.g. ['--persist']). */
48
- binaryArgs?: string[];
58
+ /** Structured options mapped to the broker's Rust `init` CLI flags. */
59
+ binaryArgs?: AgentRelayBrokerInitArgs;
49
60
  /** Broker name. Defaults to cwd basename. */
50
61
  brokerName?: string;
51
62
  /** Default channels for spawned agents. */
@@ -92,6 +103,29 @@ function isProcessRunning(pid: number): boolean {
92
103
  }
93
104
  }
94
105
 
106
+ function buildBrokerInitArgs(args?: AgentRelayBrokerInitArgs): string[] {
107
+ if (!args) {
108
+ return [];
109
+ }
110
+
111
+ const cliArgs: string[] = [];
112
+
113
+ if (args.persist) {
114
+ cliArgs.push('--persist');
115
+ }
116
+ if (args.apiPort !== undefined) {
117
+ cliArgs.push('--api-port', String(args.apiPort));
118
+ }
119
+ if (args.apiBind !== undefined) {
120
+ cliArgs.push('--api-bind', args.apiBind);
121
+ }
122
+ if (args.stateDir !== undefined) {
123
+ cliArgs.push('--state-dir', args.stateDir);
124
+ }
125
+
126
+ return cliArgs;
127
+ }
128
+
95
129
  // ── Client ─────────────────────────────────────────────────────────────
96
130
 
97
131
  export class AgentRelayClient {
@@ -172,7 +206,7 @@ export class AgentRelayClient {
172
206
  const brokerName = options?.brokerName ?? (path.basename(cwd) || 'project');
173
207
  const channels = options?.channels ?? ['general'];
174
208
  const timeoutMs = options?.startupTimeoutMs ?? 15_000;
175
- const userArgs = options?.binaryArgs ?? [];
209
+ const userArgs = buildBrokerInitArgs(options?.binaryArgs);
176
210
 
177
211
  const apiKey = `br_${randomBytes(16).toString('hex')}`;
178
212
 
@@ -3,6 +3,7 @@ export * from './types.js';
3
3
  export { BrokerTransport, type BrokerTransportOptions, AgentRelayProtocolError } from './transport.js';
4
4
  export {
5
5
  AgentRelayClient,
6
+ type AgentRelayBrokerInitArgs,
6
7
  type AgentRelayClientOptions,
7
8
  type AgentRelaySpawnOptions,
8
9
  type SessionInfo,
@@ -29,7 +29,11 @@ import path from 'node:path';
29
29
 
30
30
  import { RelayCast } from '@relaycast/sdk';
31
31
 
32
- import { AgentRelayClient, type AgentRelaySpawnOptions } from './client.js';
32
+ import {
33
+ AgentRelayClient,
34
+ type AgentRelayBrokerInitArgs,
35
+ type AgentRelaySpawnOptions,
36
+ } from './client.js';
33
37
  import { AgentRelayProtocolError } from './transport.js';
34
38
  import type { SendMessageInput, SpawnPtyInput } from './types.js';
35
39
  import type {
@@ -303,7 +307,7 @@ export type EventHook<T> = ((value: T) => void) | null;
303
307
 
304
308
  export interface AgentRelayOptions {
305
309
  binaryPath?: string;
306
- binaryArgs?: string[];
310
+ binaryArgs?: AgentRelayBrokerInitArgs;
307
311
  brokerName?: string;
308
312
  channels?: string[];
309
313
  cwd?: string;