agent-relay 3.2.1 → 3.2.3
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/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 +1201 -869
- package/dist/src/cli/commands/agent-management.d.ts +2 -2
- package/dist/src/cli/commands/agent-management.d.ts.map +1 -1
- package/dist/src/cli/commands/agent-management.js +41 -240
- package/dist/src/cli/commands/agent-management.js.map +1 -1
- package/dist/src/cli/commands/core.d.ts +1 -0
- package/dist/src/cli/commands/core.d.ts.map +1 -1
- package/dist/src/cli/commands/core.js +18 -0
- package/dist/src/cli/commands/core.js.map +1 -1
- package/dist/src/cli/commands/messaging.d.ts +1 -1
- package/dist/src/cli/commands/messaging.d.ts.map +1 -1
- package/dist/src/cli/commands/messaging.js +14 -5
- package/dist/src/cli/commands/messaging.js.map +1 -1
- package/dist/src/cli/lib/agent-management-listing.d.ts +4 -1
- package/dist/src/cli/lib/agent-management-listing.d.ts.map +1 -1
- package/dist/src/cli/lib/agent-management-listing.js +27 -2
- package/dist/src/cli/lib/agent-management-listing.js.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.d.ts.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.js +16 -13
- package/dist/src/cli/lib/broker-lifecycle.js.map +1 -1
- package/package.json +8 -8
- package/packages/acp-bridge/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/README.md +2 -2
- package/packages/openclaw/dist/identity/files.js +2 -2
- package/packages/openclaw/dist/identity/files.js.map +1 -1
- package/packages/openclaw/dist/setup.js +2 -2
- package/packages/openclaw/package.json +2 -2
- package/packages/openclaw/skill/SKILL.md +8 -8
- package/packages/openclaw/src/identity/files.ts +2 -2
- package/packages/openclaw/src/setup.ts +2 -2
- package/packages/openclaw/templates/SOUL.md.template +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/sdk/dist/client.d.ts +66 -0
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +230 -0
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/examples/example.js +1 -1
- package/packages/sdk/dist/examples/example.js.map +1 -1
- package/packages/sdk/dist/relay-adapter.js +4 -4
- package/packages/sdk/dist/relay-adapter.js.map +1 -1
- package/packages/sdk/dist/workflows/builder.d.ts +18 -3
- package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/builder.js +24 -12
- package/packages/sdk/dist/workflows/builder.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts +2 -0
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +103 -24
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/validator.js +1 -1
- package/packages/sdk/dist/workflows/validator.js.map +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/client.ts +301 -0
- package/packages/sdk/src/examples/example.ts +1 -1
- package/packages/sdk/src/relay-adapter.ts +4 -4
- package/packages/sdk/src/workflows/builder.ts +38 -11
- package/packages/sdk/src/workflows/runner.ts +108 -32
- package/packages/sdk/src/workflows/validator.ts +1 -1
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/relay-snippets/agent-relay-protocol.md +4 -4
- package/relay-snippets/agent-relay-snippet.md +9 -9
package/dist/index.cjs
CHANGED
|
@@ -35,13 +35,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
35
35
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
36
36
|
|
|
37
37
|
// packages/config/src/bridge-utils.ts
|
|
38
|
-
var
|
|
38
|
+
var import_node_child_process, import_node_util, execAsync;
|
|
39
39
|
var init_bridge_utils = __esm({
|
|
40
40
|
"packages/config/src/bridge-utils.ts"() {
|
|
41
41
|
"use strict";
|
|
42
|
-
|
|
42
|
+
import_node_child_process = require("node:child_process");
|
|
43
43
|
import_node_util = require("node:util");
|
|
44
|
-
execAsync = (0, import_node_util.promisify)(
|
|
44
|
+
execAsync = (0, import_node_util.promisify)(import_node_child_process.exec);
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -6885,14 +6885,14 @@ var require_parser = __commonJS({
|
|
|
6885
6885
|
case "scalar":
|
|
6886
6886
|
case "single-quoted-scalar":
|
|
6887
6887
|
case "double-quoted-scalar": {
|
|
6888
|
-
const
|
|
6888
|
+
const fs10 = this.flowScalar(this.type);
|
|
6889
6889
|
if (atNextItem || it.value) {
|
|
6890
|
-
map2.items.push({ start, key:
|
|
6890
|
+
map2.items.push({ start, key: fs10, sep: [] });
|
|
6891
6891
|
this.onKeyLine = true;
|
|
6892
6892
|
} else if (it.sep) {
|
|
6893
|
-
this.stack.push(
|
|
6893
|
+
this.stack.push(fs10);
|
|
6894
6894
|
} else {
|
|
6895
|
-
Object.assign(it, { key:
|
|
6895
|
+
Object.assign(it, { key: fs10, sep: [] });
|
|
6896
6896
|
this.onKeyLine = true;
|
|
6897
6897
|
}
|
|
6898
6898
|
return;
|
|
@@ -7020,13 +7020,13 @@ var require_parser = __commonJS({
|
|
|
7020
7020
|
case "scalar":
|
|
7021
7021
|
case "single-quoted-scalar":
|
|
7022
7022
|
case "double-quoted-scalar": {
|
|
7023
|
-
const
|
|
7023
|
+
const fs10 = this.flowScalar(this.type);
|
|
7024
7024
|
if (!it || it.value)
|
|
7025
|
-
fc.items.push({ start: [], key:
|
|
7025
|
+
fc.items.push({ start: [], key: fs10, sep: [] });
|
|
7026
7026
|
else if (it.sep)
|
|
7027
|
-
this.stack.push(
|
|
7027
|
+
this.stack.push(fs10);
|
|
7028
7028
|
else
|
|
7029
|
-
Object.assign(it, { key:
|
|
7029
|
+
Object.assign(it, { key: fs10, sep: [] });
|
|
7030
7030
|
return;
|
|
7031
7031
|
}
|
|
7032
7032
|
case "flow-map-end":
|
|
@@ -7467,11 +7467,11 @@ var init_types = __esm({
|
|
|
7467
7467
|
});
|
|
7468
7468
|
|
|
7469
7469
|
// packages/memory/dist/adapters/inmemory.js
|
|
7470
|
-
var
|
|
7470
|
+
var import_node_crypto11, InMemoryAdapter;
|
|
7471
7471
|
var init_inmemory = __esm({
|
|
7472
7472
|
"packages/memory/dist/adapters/inmemory.js"() {
|
|
7473
7473
|
"use strict";
|
|
7474
|
-
|
|
7474
|
+
import_node_crypto11 = require("node:crypto");
|
|
7475
7475
|
InMemoryAdapter = class {
|
|
7476
7476
|
type = "inmemory";
|
|
7477
7477
|
memories = /* @__PURE__ */ new Map();
|
|
@@ -7486,7 +7486,7 @@ var init_inmemory = __esm({
|
|
|
7486
7486
|
async init() {
|
|
7487
7487
|
}
|
|
7488
7488
|
async add(content, options) {
|
|
7489
|
-
const id = (0,
|
|
7489
|
+
const id = (0, import_node_crypto11.randomUUID)();
|
|
7490
7490
|
const now = Date.now();
|
|
7491
7491
|
const entry = {
|
|
7492
7492
|
id,
|
|
@@ -8825,6 +8825,7 @@ __export(index_exports, {
|
|
|
8825
8825
|
HOOK_ABI_VERSION: () => HOOK_ABI_VERSION,
|
|
8826
8826
|
HookEmitter: () => HookEmitter,
|
|
8827
8827
|
HookRegistry: () => HookRegistry,
|
|
8828
|
+
HttpAgentRelayClient: () => HttpAgentRelayClient,
|
|
8828
8829
|
InMemoryAdapter: () => InMemoryAdapter,
|
|
8829
8830
|
InMemoryWorkflowDb: () => InMemoryWorkflowDb,
|
|
8830
8831
|
JsonFileWorkflowDb: () => JsonFileWorkflowDb,
|
|
@@ -8951,623 +8952,12 @@ var PROTOCOL_VERSION = 1;
|
|
|
8951
8952
|
|
|
8952
8953
|
// packages/sdk/dist/client.js
|
|
8953
8954
|
var import_node_events = require("node:events");
|
|
8954
|
-
var
|
|
8955
|
+
var import_node_child_process2 = require("node:child_process");
|
|
8955
8956
|
var import_node_readline = require("node:readline");
|
|
8956
|
-
var
|
|
8957
|
-
var
|
|
8958
|
-
var
|
|
8957
|
+
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
8958
|
+
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
8959
|
+
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
8959
8960
|
var import_node_url = require("node:url");
|
|
8960
|
-
var AgentRelayProtocolError = class extends Error {
|
|
8961
|
-
code;
|
|
8962
|
-
retryable;
|
|
8963
|
-
data;
|
|
8964
|
-
constructor(payload) {
|
|
8965
|
-
super(payload.message);
|
|
8966
|
-
this.name = "AgentRelayProtocolError";
|
|
8967
|
-
this.code = payload.code;
|
|
8968
|
-
this.retryable = payload.retryable;
|
|
8969
|
-
this.data = payload.data;
|
|
8970
|
-
}
|
|
8971
|
-
};
|
|
8972
|
-
var AgentRelayProcessError = class extends Error {
|
|
8973
|
-
constructor(message) {
|
|
8974
|
-
super(message);
|
|
8975
|
-
this.name = "AgentRelayProcessError";
|
|
8976
|
-
}
|
|
8977
|
-
};
|
|
8978
|
-
function isHeadlessProvider(value) {
|
|
8979
|
-
return value === "claude" || value === "opencode";
|
|
8980
|
-
}
|
|
8981
|
-
var AgentRelayClient = class _AgentRelayClient {
|
|
8982
|
-
options;
|
|
8983
|
-
child;
|
|
8984
|
-
stdoutRl;
|
|
8985
|
-
stderrRl;
|
|
8986
|
-
lastStderrLine;
|
|
8987
|
-
requestSeq = 0;
|
|
8988
|
-
pending = /* @__PURE__ */ new Map();
|
|
8989
|
-
startingPromise;
|
|
8990
|
-
eventListeners = /* @__PURE__ */ new Set();
|
|
8991
|
-
stderrListeners = /* @__PURE__ */ new Set();
|
|
8992
|
-
eventBuffer = [];
|
|
8993
|
-
maxBufferSize = 1e3;
|
|
8994
|
-
exitPromise;
|
|
8995
|
-
/** The workspace key returned by the broker in its hello_ack response. */
|
|
8996
|
-
workspaceKey;
|
|
8997
|
-
constructor(options = {}) {
|
|
8998
|
-
this.options = {
|
|
8999
|
-
binaryPath: options.binaryPath ?? resolveDefaultBinaryPath(),
|
|
9000
|
-
binaryArgs: options.binaryArgs ?? [],
|
|
9001
|
-
brokerName: options.brokerName ?? (import_node_path.default.basename(options.cwd ?? process.cwd()) || "project"),
|
|
9002
|
-
channels: options.channels ?? ["general"],
|
|
9003
|
-
cwd: options.cwd ?? process.cwd(),
|
|
9004
|
-
env: options.env ?? process.env,
|
|
9005
|
-
requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
|
|
9006
|
-
shutdownTimeoutMs: options.shutdownTimeoutMs ?? 3e3,
|
|
9007
|
-
clientName: options.clientName ?? "@agent-relay/sdk",
|
|
9008
|
-
clientVersion: options.clientVersion ?? "0.1.0"
|
|
9009
|
-
};
|
|
9010
|
-
}
|
|
9011
|
-
static async start(options = {}) {
|
|
9012
|
-
const client = new _AgentRelayClient(options);
|
|
9013
|
-
await client.start();
|
|
9014
|
-
return client;
|
|
9015
|
-
}
|
|
9016
|
-
onEvent(listener) {
|
|
9017
|
-
this.eventListeners.add(listener);
|
|
9018
|
-
return () => {
|
|
9019
|
-
this.eventListeners.delete(listener);
|
|
9020
|
-
};
|
|
9021
|
-
}
|
|
9022
|
-
queryEvents(filter) {
|
|
9023
|
-
let events = [...this.eventBuffer];
|
|
9024
|
-
if (filter?.kind) {
|
|
9025
|
-
events = events.filter((event) => event.kind === filter.kind);
|
|
9026
|
-
}
|
|
9027
|
-
if (filter?.name) {
|
|
9028
|
-
events = events.filter((event) => "name" in event && event.name === filter.name);
|
|
9029
|
-
}
|
|
9030
|
-
const since = filter?.since;
|
|
9031
|
-
if (since !== void 0) {
|
|
9032
|
-
events = events.filter((event) => "timestamp" in event && typeof event.timestamp === "number" && event.timestamp >= since);
|
|
9033
|
-
}
|
|
9034
|
-
const limit = filter?.limit;
|
|
9035
|
-
if (limit !== void 0) {
|
|
9036
|
-
events = events.slice(-limit);
|
|
9037
|
-
}
|
|
9038
|
-
return events;
|
|
9039
|
-
}
|
|
9040
|
-
getLastEvent(kind, name) {
|
|
9041
|
-
for (let i = this.eventBuffer.length - 1; i >= 0; i -= 1) {
|
|
9042
|
-
const event = this.eventBuffer[i];
|
|
9043
|
-
if (event.kind === kind && (!name || "name" in event && event.name === name)) {
|
|
9044
|
-
return event;
|
|
9045
|
-
}
|
|
9046
|
-
}
|
|
9047
|
-
return void 0;
|
|
9048
|
-
}
|
|
9049
|
-
onBrokerStderr(listener) {
|
|
9050
|
-
this.stderrListeners.add(listener);
|
|
9051
|
-
return () => {
|
|
9052
|
-
this.stderrListeners.delete(listener);
|
|
9053
|
-
};
|
|
9054
|
-
}
|
|
9055
|
-
get brokerPid() {
|
|
9056
|
-
return this.child?.pid;
|
|
9057
|
-
}
|
|
9058
|
-
async start() {
|
|
9059
|
-
if (this.child) {
|
|
9060
|
-
return;
|
|
9061
|
-
}
|
|
9062
|
-
if (this.startingPromise) {
|
|
9063
|
-
return this.startingPromise;
|
|
9064
|
-
}
|
|
9065
|
-
this.startingPromise = this.startInternal();
|
|
9066
|
-
try {
|
|
9067
|
-
await this.startingPromise;
|
|
9068
|
-
} finally {
|
|
9069
|
-
this.startingPromise = void 0;
|
|
9070
|
-
}
|
|
9071
|
-
}
|
|
9072
|
-
/**
|
|
9073
|
-
* Pre-register a batch of agents with Relaycast before their steps execute.
|
|
9074
|
-
* The broker warms its token cache in parallel; subsequent spawn_agent calls
|
|
9075
|
-
* hit the cache rather than waiting on individual HTTP registrations.
|
|
9076
|
-
* Fire-and-forget from the caller's perspective — broker responds immediately
|
|
9077
|
-
* and registers in the background.
|
|
9078
|
-
*/
|
|
9079
|
-
async preflightAgents(agents) {
|
|
9080
|
-
if (agents.length === 0)
|
|
9081
|
-
return;
|
|
9082
|
-
await this.start();
|
|
9083
|
-
await this.requestOk("preflight_agents", { agents });
|
|
9084
|
-
}
|
|
9085
|
-
async spawnPty(input) {
|
|
9086
|
-
await this.start();
|
|
9087
|
-
const args = buildPtyArgsWithModel(input.cli, input.args ?? [], input.model);
|
|
9088
|
-
const agent = {
|
|
9089
|
-
name: input.name,
|
|
9090
|
-
runtime: "pty",
|
|
9091
|
-
cli: input.cli,
|
|
9092
|
-
args,
|
|
9093
|
-
channels: input.channels ?? [],
|
|
9094
|
-
model: input.model,
|
|
9095
|
-
cwd: input.cwd ?? this.options.cwd,
|
|
9096
|
-
team: input.team,
|
|
9097
|
-
shadow_of: input.shadowOf,
|
|
9098
|
-
shadow_mode: input.shadowMode,
|
|
9099
|
-
restart_policy: input.restartPolicy
|
|
9100
|
-
};
|
|
9101
|
-
const result = await this.requestOk("spawn_agent", {
|
|
9102
|
-
agent,
|
|
9103
|
-
...input.task != null ? { initial_task: input.task } : {},
|
|
9104
|
-
...input.idleThresholdSecs != null ? { idle_threshold_secs: input.idleThresholdSecs } : {},
|
|
9105
|
-
...input.continueFrom != null ? { continue_from: input.continueFrom } : {},
|
|
9106
|
-
...input.skipRelayPrompt != null ? { skip_relay_prompt: input.skipRelayPrompt } : {}
|
|
9107
|
-
});
|
|
9108
|
-
return result;
|
|
9109
|
-
}
|
|
9110
|
-
async spawnHeadless(input) {
|
|
9111
|
-
await this.start();
|
|
9112
|
-
const agent = {
|
|
9113
|
-
name: input.name,
|
|
9114
|
-
runtime: "headless",
|
|
9115
|
-
provider: input.provider,
|
|
9116
|
-
args: input.args ?? [],
|
|
9117
|
-
channels: input.channels ?? []
|
|
9118
|
-
};
|
|
9119
|
-
const result = await this.requestOk("spawn_agent", {
|
|
9120
|
-
agent,
|
|
9121
|
-
...input.task != null ? { initial_task: input.task } : {},
|
|
9122
|
-
...input.skipRelayPrompt != null ? { skip_relay_prompt: input.skipRelayPrompt } : {}
|
|
9123
|
-
});
|
|
9124
|
-
return result;
|
|
9125
|
-
}
|
|
9126
|
-
async spawnProvider(input) {
|
|
9127
|
-
const transport = input.transport ?? (input.provider === "opencode" ? "headless" : "pty");
|
|
9128
|
-
if (transport === "headless") {
|
|
9129
|
-
if (!isHeadlessProvider(input.provider)) {
|
|
9130
|
-
throw new AgentRelayProcessError(`provider '${input.provider}' does not support headless transport (supported: claude, opencode)`);
|
|
9131
|
-
}
|
|
9132
|
-
return this.spawnHeadless({
|
|
9133
|
-
name: input.name,
|
|
9134
|
-
provider: input.provider,
|
|
9135
|
-
args: input.args,
|
|
9136
|
-
channels: input.channels,
|
|
9137
|
-
task: input.task,
|
|
9138
|
-
skipRelayPrompt: input.skipRelayPrompt
|
|
9139
|
-
});
|
|
9140
|
-
}
|
|
9141
|
-
return this.spawnPty({
|
|
9142
|
-
name: input.name,
|
|
9143
|
-
cli: input.provider,
|
|
9144
|
-
args: input.args,
|
|
9145
|
-
channels: input.channels,
|
|
9146
|
-
task: input.task,
|
|
9147
|
-
model: input.model,
|
|
9148
|
-
cwd: input.cwd,
|
|
9149
|
-
team: input.team,
|
|
9150
|
-
shadowOf: input.shadowOf,
|
|
9151
|
-
shadowMode: input.shadowMode,
|
|
9152
|
-
idleThresholdSecs: input.idleThresholdSecs,
|
|
9153
|
-
restartPolicy: input.restartPolicy,
|
|
9154
|
-
continueFrom: input.continueFrom,
|
|
9155
|
-
skipRelayPrompt: input.skipRelayPrompt
|
|
9156
|
-
});
|
|
9157
|
-
}
|
|
9158
|
-
async spawnClaude(input) {
|
|
9159
|
-
return this.spawnProvider({ ...input, provider: "claude" });
|
|
9160
|
-
}
|
|
9161
|
-
async spawnOpencode(input) {
|
|
9162
|
-
return this.spawnProvider({ ...input, provider: "opencode" });
|
|
9163
|
-
}
|
|
9164
|
-
async release(name, reason) {
|
|
9165
|
-
await this.start();
|
|
9166
|
-
return this.requestOk("release_agent", { name, reason });
|
|
9167
|
-
}
|
|
9168
|
-
async sendInput(name, data) {
|
|
9169
|
-
await this.start();
|
|
9170
|
-
return this.requestOk("send_input", { name, data });
|
|
9171
|
-
}
|
|
9172
|
-
async setModel(name, model, opts) {
|
|
9173
|
-
await this.start();
|
|
9174
|
-
return this.requestOk("set_model", {
|
|
9175
|
-
name,
|
|
9176
|
-
model,
|
|
9177
|
-
timeout_ms: opts?.timeoutMs
|
|
9178
|
-
});
|
|
9179
|
-
}
|
|
9180
|
-
async getMetrics(agent) {
|
|
9181
|
-
await this.start();
|
|
9182
|
-
return this.requestOk("get_metrics", { agent });
|
|
9183
|
-
}
|
|
9184
|
-
async getCrashInsights() {
|
|
9185
|
-
await this.start();
|
|
9186
|
-
return this.requestOk("get_crash_insights", {});
|
|
9187
|
-
}
|
|
9188
|
-
async sendMessage(input) {
|
|
9189
|
-
await this.start();
|
|
9190
|
-
try {
|
|
9191
|
-
return await this.requestOk("send_message", {
|
|
9192
|
-
to: input.to,
|
|
9193
|
-
text: input.text,
|
|
9194
|
-
from: input.from,
|
|
9195
|
-
thread_id: input.threadId,
|
|
9196
|
-
workspace_id: input.workspaceId,
|
|
9197
|
-
workspace_alias: input.workspaceAlias,
|
|
9198
|
-
priority: input.priority,
|
|
9199
|
-
data: input.data
|
|
9200
|
-
});
|
|
9201
|
-
} catch (error48) {
|
|
9202
|
-
if (error48 instanceof AgentRelayProtocolError && error48.code === "unsupported_operation") {
|
|
9203
|
-
return { event_id: "unsupported_operation", targets: [] };
|
|
9204
|
-
}
|
|
9205
|
-
throw error48;
|
|
9206
|
-
}
|
|
9207
|
-
}
|
|
9208
|
-
async listAgents() {
|
|
9209
|
-
await this.start();
|
|
9210
|
-
const result = await this.requestOk("list_agents", {});
|
|
9211
|
-
return result.agents;
|
|
9212
|
-
}
|
|
9213
|
-
async getStatus() {
|
|
9214
|
-
await this.start();
|
|
9215
|
-
return this.requestOk("get_status", {});
|
|
9216
|
-
}
|
|
9217
|
-
async shutdown() {
|
|
9218
|
-
if (!this.child) {
|
|
9219
|
-
return;
|
|
9220
|
-
}
|
|
9221
|
-
void this.requestOk("shutdown", {}).catch(() => {
|
|
9222
|
-
});
|
|
9223
|
-
const child = this.child;
|
|
9224
|
-
const wait = this.exitPromise ?? Promise.resolve();
|
|
9225
|
-
const waitForExit = async (timeoutMs) => {
|
|
9226
|
-
let timer;
|
|
9227
|
-
const result = await Promise.race([
|
|
9228
|
-
wait.then(() => true),
|
|
9229
|
-
new Promise((resolve3) => {
|
|
9230
|
-
timer = setTimeout(() => resolve3(false), timeoutMs);
|
|
9231
|
-
})
|
|
9232
|
-
]);
|
|
9233
|
-
if (timer !== void 0)
|
|
9234
|
-
clearTimeout(timer);
|
|
9235
|
-
return result;
|
|
9236
|
-
};
|
|
9237
|
-
if (await waitForExit(this.options.shutdownTimeoutMs)) {
|
|
9238
|
-
return;
|
|
9239
|
-
}
|
|
9240
|
-
if (child.exitCode === null && child.signalCode === null) {
|
|
9241
|
-
child.kill("SIGTERM");
|
|
9242
|
-
}
|
|
9243
|
-
if (await waitForExit(1e3)) {
|
|
9244
|
-
return;
|
|
9245
|
-
}
|
|
9246
|
-
if (child.exitCode === null && child.signalCode === null) {
|
|
9247
|
-
child.kill("SIGKILL");
|
|
9248
|
-
}
|
|
9249
|
-
await waitForExit(1e3);
|
|
9250
|
-
}
|
|
9251
|
-
async waitForExit() {
|
|
9252
|
-
if (!this.child) {
|
|
9253
|
-
return;
|
|
9254
|
-
}
|
|
9255
|
-
await this.exitPromise;
|
|
9256
|
-
}
|
|
9257
|
-
async startInternal() {
|
|
9258
|
-
const resolvedBinary = expandTilde(this.options.binaryPath);
|
|
9259
|
-
if (isExplicitPath(this.options.binaryPath) && !import_node_fs.default.existsSync(resolvedBinary)) {
|
|
9260
|
-
throw new AgentRelayProcessError(`broker binary not found: ${this.options.binaryPath}`);
|
|
9261
|
-
}
|
|
9262
|
-
this.lastStderrLine = void 0;
|
|
9263
|
-
const args = [
|
|
9264
|
-
"init",
|
|
9265
|
-
"--name",
|
|
9266
|
-
this.options.brokerName,
|
|
9267
|
-
...this.options.channels.length > 0 ? ["--channels", this.options.channels.join(",")] : [],
|
|
9268
|
-
...this.options.binaryArgs
|
|
9269
|
-
];
|
|
9270
|
-
const env = { ...this.options.env };
|
|
9271
|
-
if (isExplicitPath(this.options.binaryPath)) {
|
|
9272
|
-
const binDir = import_node_path.default.dirname(import_node_path.default.resolve(resolvedBinary));
|
|
9273
|
-
const currentPath = env.PATH ?? env.Path ?? "";
|
|
9274
|
-
if (!currentPath.split(import_node_path.default.delimiter).includes(binDir)) {
|
|
9275
|
-
env.PATH = `${binDir}${import_node_path.default.delimiter}${currentPath}`;
|
|
9276
|
-
}
|
|
9277
|
-
}
|
|
9278
|
-
console.error(`[broker] Starting: ${resolvedBinary} ${args.join(" ")}`);
|
|
9279
|
-
const child = (0, import_node_child_process.spawn)(resolvedBinary, args, {
|
|
9280
|
-
cwd: this.options.cwd,
|
|
9281
|
-
env,
|
|
9282
|
-
stdio: "pipe"
|
|
9283
|
-
});
|
|
9284
|
-
this.child = child;
|
|
9285
|
-
this.stdoutRl = (0, import_node_readline.createInterface)({ input: child.stdout, crlfDelay: Infinity });
|
|
9286
|
-
this.stderrRl = (0, import_node_readline.createInterface)({ input: child.stderr, crlfDelay: Infinity });
|
|
9287
|
-
this.stdoutRl.on("line", (line) => {
|
|
9288
|
-
this.handleStdoutLine(line);
|
|
9289
|
-
});
|
|
9290
|
-
this.stderrRl.on("line", (line) => {
|
|
9291
|
-
const trimmed = line.trim();
|
|
9292
|
-
if (trimmed) {
|
|
9293
|
-
this.lastStderrLine = trimmed;
|
|
9294
|
-
}
|
|
9295
|
-
for (const listener of this.stderrListeners) {
|
|
9296
|
-
listener(line);
|
|
9297
|
-
}
|
|
9298
|
-
});
|
|
9299
|
-
this.exitPromise = new Promise((resolve3) => {
|
|
9300
|
-
child.once("close", (code, signal) => {
|
|
9301
|
-
const detail = this.lastStderrLine ? `: ${this.lastStderrLine}` : "";
|
|
9302
|
-
const error48 = new AgentRelayProcessError(`broker exited (code=${code ?? "null"}, signal=${signal ?? "null"})${detail}`);
|
|
9303
|
-
this.failAllPending(error48);
|
|
9304
|
-
this.disposeProcessHandles();
|
|
9305
|
-
resolve3();
|
|
9306
|
-
});
|
|
9307
|
-
child.once("error", (error48) => {
|
|
9308
|
-
this.failAllPending(error48);
|
|
9309
|
-
this.disposeProcessHandles();
|
|
9310
|
-
resolve3();
|
|
9311
|
-
});
|
|
9312
|
-
});
|
|
9313
|
-
const helloAck = await this.requestHello();
|
|
9314
|
-
console.error("[broker] Broker ready (hello handshake complete)");
|
|
9315
|
-
if (helloAck.workspace_key) {
|
|
9316
|
-
this.workspaceKey = helloAck.workspace_key;
|
|
9317
|
-
}
|
|
9318
|
-
}
|
|
9319
|
-
disposeProcessHandles() {
|
|
9320
|
-
this.stdoutRl?.close();
|
|
9321
|
-
this.stderrRl?.close();
|
|
9322
|
-
this.stdoutRl = void 0;
|
|
9323
|
-
this.stderrRl = void 0;
|
|
9324
|
-
this.lastStderrLine = void 0;
|
|
9325
|
-
this.child = void 0;
|
|
9326
|
-
this.exitPromise = void 0;
|
|
9327
|
-
}
|
|
9328
|
-
failAllPending(error48) {
|
|
9329
|
-
for (const pending of this.pending.values()) {
|
|
9330
|
-
clearTimeout(pending.timeout);
|
|
9331
|
-
pending.reject(error48);
|
|
9332
|
-
}
|
|
9333
|
-
this.pending.clear();
|
|
9334
|
-
}
|
|
9335
|
-
handleStdoutLine(line) {
|
|
9336
|
-
let parsed;
|
|
9337
|
-
try {
|
|
9338
|
-
parsed = JSON.parse(line);
|
|
9339
|
-
} catch {
|
|
9340
|
-
return;
|
|
9341
|
-
}
|
|
9342
|
-
if (!parsed || typeof parsed !== "object") {
|
|
9343
|
-
return;
|
|
9344
|
-
}
|
|
9345
|
-
if (parsed.v !== PROTOCOL_VERSION || typeof parsed.type !== "string") {
|
|
9346
|
-
return;
|
|
9347
|
-
}
|
|
9348
|
-
const envelope = {
|
|
9349
|
-
v: parsed.v,
|
|
9350
|
-
type: parsed.type,
|
|
9351
|
-
request_id: parsed.request_id,
|
|
9352
|
-
payload: parsed.payload
|
|
9353
|
-
};
|
|
9354
|
-
if (envelope.type === "event") {
|
|
9355
|
-
const payload = envelope.payload;
|
|
9356
|
-
this.eventBuffer.push(payload);
|
|
9357
|
-
if (this.eventBuffer.length > this.maxBufferSize) {
|
|
9358
|
-
this.eventBuffer.shift();
|
|
9359
|
-
}
|
|
9360
|
-
for (const listener of this.eventListeners) {
|
|
9361
|
-
listener(payload);
|
|
9362
|
-
}
|
|
9363
|
-
return;
|
|
9364
|
-
}
|
|
9365
|
-
if (!envelope.request_id) {
|
|
9366
|
-
return;
|
|
9367
|
-
}
|
|
9368
|
-
const pending = this.pending.get(envelope.request_id);
|
|
9369
|
-
if (!pending) {
|
|
9370
|
-
return;
|
|
9371
|
-
}
|
|
9372
|
-
if (envelope.type === "error") {
|
|
9373
|
-
clearTimeout(pending.timeout);
|
|
9374
|
-
this.pending.delete(envelope.request_id);
|
|
9375
|
-
pending.reject(new AgentRelayProtocolError(envelope.payload));
|
|
9376
|
-
return;
|
|
9377
|
-
}
|
|
9378
|
-
if (envelope.type !== pending.expectedType) {
|
|
9379
|
-
clearTimeout(pending.timeout);
|
|
9380
|
-
this.pending.delete(envelope.request_id);
|
|
9381
|
-
pending.reject(new AgentRelayProcessError(`unexpected response type '${envelope.type}' for request '${envelope.request_id}' (expected '${pending.expectedType}')`));
|
|
9382
|
-
return;
|
|
9383
|
-
}
|
|
9384
|
-
clearTimeout(pending.timeout);
|
|
9385
|
-
this.pending.delete(envelope.request_id);
|
|
9386
|
-
pending.resolve(envelope);
|
|
9387
|
-
}
|
|
9388
|
-
async requestHello() {
|
|
9389
|
-
const payload = {
|
|
9390
|
-
client_name: this.options.clientName,
|
|
9391
|
-
client_version: this.options.clientVersion
|
|
9392
|
-
};
|
|
9393
|
-
const frame = await this.sendRequest("hello", payload, "hello_ack");
|
|
9394
|
-
return frame.payload;
|
|
9395
|
-
}
|
|
9396
|
-
async requestOk(type, payload) {
|
|
9397
|
-
const frame = await this.sendRequest(type, payload, "ok");
|
|
9398
|
-
const result = frame.payload;
|
|
9399
|
-
return result.result;
|
|
9400
|
-
}
|
|
9401
|
-
async sendRequest(type, payload, expectedType) {
|
|
9402
|
-
if (!this.child) {
|
|
9403
|
-
throw new AgentRelayProcessError("broker is not running");
|
|
9404
|
-
}
|
|
9405
|
-
const requestId = `req_${++this.requestSeq}`;
|
|
9406
|
-
const message = {
|
|
9407
|
-
v: PROTOCOL_VERSION,
|
|
9408
|
-
type,
|
|
9409
|
-
request_id: requestId,
|
|
9410
|
-
payload
|
|
9411
|
-
};
|
|
9412
|
-
const responsePromise = new Promise((resolve3, reject) => {
|
|
9413
|
-
const timeout = setTimeout(() => {
|
|
9414
|
-
this.pending.delete(requestId);
|
|
9415
|
-
reject(new AgentRelayProcessError(`request timed out after ${this.options.requestTimeoutMs}ms (type='${type}', request_id='${requestId}')`));
|
|
9416
|
-
}, this.options.requestTimeoutMs);
|
|
9417
|
-
this.pending.set(requestId, {
|
|
9418
|
-
expectedType,
|
|
9419
|
-
resolve: resolve3,
|
|
9420
|
-
reject,
|
|
9421
|
-
timeout
|
|
9422
|
-
});
|
|
9423
|
-
});
|
|
9424
|
-
const line = `${JSON.stringify(message)}
|
|
9425
|
-
`;
|
|
9426
|
-
if (!this.child.stdin.write(line)) {
|
|
9427
|
-
await (0, import_node_events.once)(this.child.stdin, "drain");
|
|
9428
|
-
}
|
|
9429
|
-
return responsePromise;
|
|
9430
|
-
}
|
|
9431
|
-
};
|
|
9432
|
-
var CLI_MODEL_FLAG_CLIS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "goose", "aider"]);
|
|
9433
|
-
var CLI_DEFAULT_ARGS = {
|
|
9434
|
-
codex: ["-c", "check_for_update_on_startup=false"]
|
|
9435
|
-
};
|
|
9436
|
-
function buildPtyArgsWithModel(cli, args, model) {
|
|
9437
|
-
const cliName = cli.split(":")[0].trim().toLowerCase();
|
|
9438
|
-
const defaultArgs = CLI_DEFAULT_ARGS[cliName] ?? [];
|
|
9439
|
-
const baseArgs = [...defaultArgs, ...args];
|
|
9440
|
-
if (!model) {
|
|
9441
|
-
return baseArgs;
|
|
9442
|
-
}
|
|
9443
|
-
if (!CLI_MODEL_FLAG_CLIS.has(cliName)) {
|
|
9444
|
-
return baseArgs;
|
|
9445
|
-
}
|
|
9446
|
-
if (hasModelArg(baseArgs)) {
|
|
9447
|
-
return baseArgs;
|
|
9448
|
-
}
|
|
9449
|
-
return ["--model", model, ...baseArgs];
|
|
9450
|
-
}
|
|
9451
|
-
function hasModelArg(args) {
|
|
9452
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
9453
|
-
const arg = args[i];
|
|
9454
|
-
if (arg === "--model") {
|
|
9455
|
-
return true;
|
|
9456
|
-
}
|
|
9457
|
-
if (arg.startsWith("--model=")) {
|
|
9458
|
-
return true;
|
|
9459
|
-
}
|
|
9460
|
-
}
|
|
9461
|
-
return false;
|
|
9462
|
-
}
|
|
9463
|
-
function expandTilde(p) {
|
|
9464
|
-
if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
|
|
9465
|
-
const home = import_node_os.default.homedir();
|
|
9466
|
-
return import_node_path.default.join(home, p.slice(2));
|
|
9467
|
-
}
|
|
9468
|
-
return p;
|
|
9469
|
-
}
|
|
9470
|
-
function isExplicitPath(binaryPath) {
|
|
9471
|
-
return binaryPath.includes("/") || binaryPath.includes("\\") || binaryPath.startsWith(".") || binaryPath.startsWith("~");
|
|
9472
|
-
}
|
|
9473
|
-
function detectPlatformSuffix() {
|
|
9474
|
-
const platformMap = {
|
|
9475
|
-
darwin: { arm64: "darwin-arm64", x64: "darwin-x64" },
|
|
9476
|
-
linux: { arm64: "linux-arm64", x64: "linux-x64" },
|
|
9477
|
-
win32: { x64: "win32-x64" }
|
|
9478
|
-
};
|
|
9479
|
-
return platformMap[process.platform]?.[process.arch] ?? null;
|
|
9480
|
-
}
|
|
9481
|
-
function getLatestVersionSync() {
|
|
9482
|
-
try {
|
|
9483
|
-
const result = (0, import_node_child_process.execSync)("curl -fsSL https://api.github.com/repos/AgentWorkforce/relay/releases/latest", {
|
|
9484
|
-
timeout: 15e3,
|
|
9485
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
9486
|
-
}).toString();
|
|
9487
|
-
const match = result.match(/"tag_name"\s*:\s*"([^"]+)"/);
|
|
9488
|
-
if (!match?.[1])
|
|
9489
|
-
return null;
|
|
9490
|
-
return match[1].replace(/^openclaw-/, "").replace(/^v/, "");
|
|
9491
|
-
} catch {
|
|
9492
|
-
return null;
|
|
9493
|
-
}
|
|
9494
|
-
}
|
|
9495
|
-
function installBrokerBinary() {
|
|
9496
|
-
const suffix = detectPlatformSuffix();
|
|
9497
|
-
if (!suffix) {
|
|
9498
|
-
throw new AgentRelayProcessError(`Unsupported platform: ${process.platform}-${process.arch}`);
|
|
9499
|
-
}
|
|
9500
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
9501
|
-
const installDir = import_node_path.default.join(homeDir, ".agent-relay", "bin");
|
|
9502
|
-
const brokerExe = process.platform === "win32" ? "agent-relay-broker.exe" : "agent-relay-broker";
|
|
9503
|
-
const targetPath = import_node_path.default.join(installDir, brokerExe);
|
|
9504
|
-
console.log(`[agent-relay] Broker binary not found, installing for ${suffix}...`);
|
|
9505
|
-
const version2 = getLatestVersionSync();
|
|
9506
|
-
if (!version2) {
|
|
9507
|
-
throw new AgentRelayProcessError("Failed to fetch latest agent-relay version from GitHub.\nInstall manually: curl -fsSL https://raw.githubusercontent.com/AgentWorkforce/relay/main/install.sh | bash");
|
|
9508
|
-
}
|
|
9509
|
-
const binaryName = `agent-relay-broker-${suffix}`;
|
|
9510
|
-
const downloadUrl = `https://github.com/AgentWorkforce/relay/releases/download/v${version2}/${binaryName}`;
|
|
9511
|
-
console.log(`[agent-relay] Downloading v${version2} from ${downloadUrl}`);
|
|
9512
|
-
try {
|
|
9513
|
-
import_node_fs.default.mkdirSync(installDir, { recursive: true });
|
|
9514
|
-
(0, import_node_child_process.execSync)(`curl -fsSL "${downloadUrl}" -o "${targetPath}"`, {
|
|
9515
|
-
timeout: 6e4,
|
|
9516
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
9517
|
-
});
|
|
9518
|
-
import_node_fs.default.chmodSync(targetPath, 493);
|
|
9519
|
-
if (process.platform === "darwin") {
|
|
9520
|
-
try {
|
|
9521
|
-
(0, import_node_child_process.execSync)(`xattr -d com.apple.quarantine "${targetPath}" 2>/dev/null || true`, {
|
|
9522
|
-
timeout: 1e4,
|
|
9523
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
9524
|
-
});
|
|
9525
|
-
} catch {
|
|
9526
|
-
}
|
|
9527
|
-
try {
|
|
9528
|
-
(0, import_node_child_process.execSync)(`codesign --force --sign - "${targetPath}"`, {
|
|
9529
|
-
timeout: 1e4,
|
|
9530
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
9531
|
-
});
|
|
9532
|
-
} catch {
|
|
9533
|
-
}
|
|
9534
|
-
}
|
|
9535
|
-
(0, import_node_child_process.execSync)(`"${targetPath}" --help`, { timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] });
|
|
9536
|
-
} catch (err) {
|
|
9537
|
-
try {
|
|
9538
|
-
import_node_fs.default.unlinkSync(targetPath);
|
|
9539
|
-
} catch {
|
|
9540
|
-
}
|
|
9541
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
9542
|
-
throw new AgentRelayProcessError(`Failed to install broker binary: ${message}
|
|
9543
|
-
Install manually: curl -fsSL https://raw.githubusercontent.com/AgentWorkforce/relay/main/install.sh | bash`);
|
|
9544
|
-
}
|
|
9545
|
-
console.log(`[agent-relay] Broker installed to ${targetPath}`);
|
|
9546
|
-
return targetPath;
|
|
9547
|
-
}
|
|
9548
|
-
function resolveDefaultBinaryPath() {
|
|
9549
|
-
const brokerExe = process.platform === "win32" ? "agent-relay-broker.exe" : "agent-relay-broker";
|
|
9550
|
-
const moduleDir = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta_url));
|
|
9551
|
-
const workspaceRelease = import_node_path.default.resolve(moduleDir, "..", "..", "..", "target", "release", brokerExe);
|
|
9552
|
-
if (import_node_fs.default.existsSync(workspaceRelease)) {
|
|
9553
|
-
return workspaceRelease;
|
|
9554
|
-
}
|
|
9555
|
-
const binDir = import_node_path.default.resolve(moduleDir, "..", "bin");
|
|
9556
|
-
const suffix = detectPlatformSuffix();
|
|
9557
|
-
if (suffix) {
|
|
9558
|
-
const ext = process.platform === "win32" ? ".exe" : "";
|
|
9559
|
-
const platformBinary = import_node_path.default.join(binDir, `agent-relay-broker-${suffix}${ext}`);
|
|
9560
|
-
if (import_node_fs.default.existsSync(platformBinary)) {
|
|
9561
|
-
return platformBinary;
|
|
9562
|
-
}
|
|
9563
|
-
}
|
|
9564
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
9565
|
-
const standaloneBroker = import_node_path.default.join(homeDir, ".agent-relay", "bin", brokerExe);
|
|
9566
|
-
if (import_node_fs.default.existsSync(standaloneBroker)) {
|
|
9567
|
-
return standaloneBroker;
|
|
9568
|
-
}
|
|
9569
|
-
return installBrokerBinary();
|
|
9570
|
-
}
|
|
9571
8961
|
|
|
9572
8962
|
// packages/config/src/relay-config.ts
|
|
9573
8963
|
var DEFAULT_CONNECTION_CONFIG = {
|
|
@@ -9580,28 +8970,66 @@ var DEFAULT_CONNECTION_CONFIG = {
|
|
|
9580
8970
|
};
|
|
9581
8971
|
|
|
9582
8972
|
// packages/config/src/bridge-config.ts
|
|
9583
|
-
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
9584
|
-
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
9585
|
-
|
|
9586
|
-
// packages/config/src/project-namespace.ts
|
|
9587
8973
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
9588
8974
|
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
8975
|
+
|
|
8976
|
+
// packages/config/src/project-namespace.ts
|
|
8977
|
+
var import_node_crypto = __toESM(require("node:crypto"), 1);
|
|
8978
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
8979
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
8980
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
9589
8981
|
function getGlobalBaseDir() {
|
|
9590
8982
|
if (process.env.AGENT_RELAY_DATA_DIR) {
|
|
9591
8983
|
return process.env.AGENT_RELAY_DATA_DIR;
|
|
9592
8984
|
}
|
|
9593
8985
|
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
9594
8986
|
if (xdgDataHome) {
|
|
9595
|
-
return
|
|
8987
|
+
return import_node_path.default.join(xdgDataHome, "agent-relay");
|
|
9596
8988
|
}
|
|
9597
|
-
return
|
|
8989
|
+
return import_node_path.default.join(import_node_os.default.homedir(), ".agent-relay");
|
|
9598
8990
|
}
|
|
9599
8991
|
var GLOBAL_BASE_DIR = getGlobalBaseDir();
|
|
8992
|
+
var PROJECT_DATA_DIR = ".agent-relay";
|
|
8993
|
+
function hashPath(projectPath) {
|
|
8994
|
+
const normalized = import_node_path.default.resolve(projectPath);
|
|
8995
|
+
const hash2 = import_node_crypto.default.createHash("sha256").update(normalized).digest("hex");
|
|
8996
|
+
return hash2.substring(0, 12);
|
|
8997
|
+
}
|
|
8998
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
8999
|
+
if (process.env.AGENT_RELAY_PROJECT) {
|
|
9000
|
+
return import_node_path.default.resolve(process.env.AGENT_RELAY_PROJECT);
|
|
9001
|
+
}
|
|
9002
|
+
let current = import_node_path.default.resolve(startDir);
|
|
9003
|
+
const root = import_node_path.default.parse(current).root;
|
|
9004
|
+
const markers = [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".agent-relay"];
|
|
9005
|
+
while (current !== root) {
|
|
9006
|
+
for (const marker of markers) {
|
|
9007
|
+
if (import_node_fs.default.existsSync(import_node_path.default.join(current, marker))) {
|
|
9008
|
+
return current;
|
|
9009
|
+
}
|
|
9010
|
+
}
|
|
9011
|
+
current = import_node_path.default.dirname(current);
|
|
9012
|
+
}
|
|
9013
|
+
return import_node_path.default.resolve(startDir);
|
|
9014
|
+
}
|
|
9015
|
+
function getProjectPaths(projectRoot) {
|
|
9016
|
+
const root = projectRoot ?? findProjectRoot();
|
|
9017
|
+
const projectId = hashPath(root);
|
|
9018
|
+
const dataDir = import_node_path.default.join(root, PROJECT_DATA_DIR);
|
|
9019
|
+
return {
|
|
9020
|
+
dataDir,
|
|
9021
|
+
teamDir: import_node_path.default.join(dataDir, "team"),
|
|
9022
|
+
dbPath: import_node_path.default.join(dataDir, "messages.sqlite"),
|
|
9023
|
+
socketPath: import_node_path.default.join(dataDir, "relay.sock"),
|
|
9024
|
+
projectRoot: root,
|
|
9025
|
+
projectId
|
|
9026
|
+
};
|
|
9027
|
+
}
|
|
9600
9028
|
|
|
9601
9029
|
// packages/config/src/bridge-config.ts
|
|
9602
9030
|
var CONFIG_PATHS = [
|
|
9603
|
-
|
|
9604
|
-
|
|
9031
|
+
import_node_path2.default.join(import_node_os2.default.homedir(), ".agent-relay", "bridge.json"),
|
|
9032
|
+
import_node_path2.default.join(import_node_os2.default.homedir(), ".config", "agent-relay", "bridge.json")
|
|
9605
9033
|
];
|
|
9606
9034
|
|
|
9607
9035
|
// packages/config/src/index.ts
|
|
@@ -15501,93 +14929,924 @@ var ModelOptions = {
|
|
|
15501
14929
|
Droid: DROID_MODEL_OPTIONS,
|
|
15502
14930
|
Opencode: OPENCODE_MODEL_OPTIONS
|
|
15503
14931
|
};
|
|
15504
|
-
var SwarmPatterns = {
|
|
15505
|
-
/** Central coordinator distributes tasks to workers */
|
|
15506
|
-
HUB_SPOKE: "hub-spoke",
|
|
15507
|
-
/** Directed acyclic graph with dependencies */
|
|
15508
|
-
DAG: "dag",
|
|
15509
|
-
/** Parallel execution across multiple agents */
|
|
15510
|
-
FAN_OUT: "fan-out",
|
|
15511
|
-
/** Sequential processing through stages */
|
|
15512
|
-
PIPELINE: "pipeline",
|
|
15513
|
-
/** Agents reach agreement before proceeding */
|
|
15514
|
-
CONSENSUS: "consensus",
|
|
15515
|
-
/** Fully connected peer-to-peer communication */
|
|
15516
|
-
MESH: "mesh",
|
|
15517
|
-
/** Sequential handoff between agents */
|
|
15518
|
-
HANDOFF: "handoff",
|
|
15519
|
-
/** Cascading delegation */
|
|
15520
|
-
CASCADE: "cascade",
|
|
15521
|
-
/** Agents debate to reach conclusion */
|
|
15522
|
-
DEBATE: "debate",
|
|
15523
|
-
/** Tree-structured coordination */
|
|
15524
|
-
HIERARCHICAL: "hierarchical"
|
|
14932
|
+
var SwarmPatterns = {
|
|
14933
|
+
/** Central coordinator distributes tasks to workers */
|
|
14934
|
+
HUB_SPOKE: "hub-spoke",
|
|
14935
|
+
/** Directed acyclic graph with dependencies */
|
|
14936
|
+
DAG: "dag",
|
|
14937
|
+
/** Parallel execution across multiple agents */
|
|
14938
|
+
FAN_OUT: "fan-out",
|
|
14939
|
+
/** Sequential processing through stages */
|
|
14940
|
+
PIPELINE: "pipeline",
|
|
14941
|
+
/** Agents reach agreement before proceeding */
|
|
14942
|
+
CONSENSUS: "consensus",
|
|
14943
|
+
/** Fully connected peer-to-peer communication */
|
|
14944
|
+
MESH: "mesh",
|
|
14945
|
+
/** Sequential handoff between agents */
|
|
14946
|
+
HANDOFF: "handoff",
|
|
14947
|
+
/** Cascading delegation */
|
|
14948
|
+
CASCADE: "cascade",
|
|
14949
|
+
/** Agents debate to reach conclusion */
|
|
14950
|
+
DEBATE: "debate",
|
|
14951
|
+
/** Tree-structured coordination */
|
|
14952
|
+
HIERARCHICAL: "hierarchical"
|
|
14953
|
+
};
|
|
14954
|
+
var CLIRegistry = {
|
|
14955
|
+
claude: {
|
|
14956
|
+
name: "Claude Code",
|
|
14957
|
+
package: "@anthropic-ai/claude-code",
|
|
14958
|
+
version: "2.1.72",
|
|
14959
|
+
install: "npm install -g @anthropic-ai/claude-code",
|
|
14960
|
+
npmLink: "https://www.npmjs.com/package/@anthropic-ai"
|
|
14961
|
+
},
|
|
14962
|
+
codex: {
|
|
14963
|
+
name: "Codex CLI",
|
|
14964
|
+
package: "@openai/codex",
|
|
14965
|
+
version: "0.114.0",
|
|
14966
|
+
install: "npm install -g @openai/codex",
|
|
14967
|
+
npmLink: "https://www.npmjs.com/package/@openai/codex"
|
|
14968
|
+
},
|
|
14969
|
+
gemini: {
|
|
14970
|
+
name: "Gemini CLI",
|
|
14971
|
+
package: "@google/gemini-cli",
|
|
14972
|
+
version: "0.33.0",
|
|
14973
|
+
install: "npm install -g @google/gemini-cli",
|
|
14974
|
+
npmLink: "https://www.npmjs.com/package/@google/gemini-cli"
|
|
14975
|
+
},
|
|
14976
|
+
cursor: {
|
|
14977
|
+
name: "Cursor",
|
|
14978
|
+
package: "cursor",
|
|
14979
|
+
version: "2026.02.27-e7d2ef6",
|
|
14980
|
+
install: "Download from cursor.com",
|
|
14981
|
+
npmLink: void 0
|
|
14982
|
+
},
|
|
14983
|
+
droid: {
|
|
14984
|
+
name: "Droid",
|
|
14985
|
+
package: "droid",
|
|
14986
|
+
version: "0.1.0",
|
|
14987
|
+
install: "Download from droid.dev",
|
|
14988
|
+
npmLink: void 0
|
|
14989
|
+
},
|
|
14990
|
+
opencode: {
|
|
14991
|
+
name: "OpenCode",
|
|
14992
|
+
package: "opencode-ai",
|
|
14993
|
+
version: "1.2.24",
|
|
14994
|
+
install: "npm install -g opencode-ai",
|
|
14995
|
+
npmLink: "https://www.npmjs.com/package/opencode-ai"
|
|
14996
|
+
},
|
|
14997
|
+
aider: {
|
|
14998
|
+
name: "Aider",
|
|
14999
|
+
package: "aider-chat",
|
|
15000
|
+
version: "0.72.1",
|
|
15001
|
+
install: "pip install aider-chat",
|
|
15002
|
+
npmLink: void 0
|
|
15003
|
+
},
|
|
15004
|
+
goose: {
|
|
15005
|
+
name: "Goose",
|
|
15006
|
+
package: "goose-ai",
|
|
15007
|
+
version: "1.0.16",
|
|
15008
|
+
install: "pip install goose-ai",
|
|
15009
|
+
npmLink: void 0
|
|
15010
|
+
}
|
|
15011
|
+
};
|
|
15012
|
+
var DefaultModels = {
|
|
15013
|
+
claude: "sonnet",
|
|
15014
|
+
codex: "gpt-5.4",
|
|
15015
|
+
gemini: "gemini-3.1-pro-preview",
|
|
15016
|
+
cursor: "opus-4.6-thinking",
|
|
15017
|
+
droid: "opus-4.6-fast",
|
|
15018
|
+
opencode: "openai/gpt-5.2"
|
|
15019
|
+
};
|
|
15020
|
+
|
|
15021
|
+
// packages/sdk/dist/client.js
|
|
15022
|
+
var AgentRelayProtocolError = class extends Error {
|
|
15023
|
+
code;
|
|
15024
|
+
retryable;
|
|
15025
|
+
data;
|
|
15026
|
+
constructor(payload) {
|
|
15027
|
+
super(payload.message);
|
|
15028
|
+
this.name = "AgentRelayProtocolError";
|
|
15029
|
+
this.code = payload.code;
|
|
15030
|
+
this.retryable = payload.retryable;
|
|
15031
|
+
this.data = payload.data;
|
|
15032
|
+
}
|
|
15033
|
+
};
|
|
15034
|
+
var AgentRelayProcessError = class extends Error {
|
|
15035
|
+
constructor(message) {
|
|
15036
|
+
super(message);
|
|
15037
|
+
this.name = "AgentRelayProcessError";
|
|
15038
|
+
}
|
|
15039
|
+
};
|
|
15040
|
+
function isHeadlessProvider(value) {
|
|
15041
|
+
return value === "claude" || value === "opencode";
|
|
15042
|
+
}
|
|
15043
|
+
var AgentRelayClient = class _AgentRelayClient {
|
|
15044
|
+
options;
|
|
15045
|
+
child;
|
|
15046
|
+
stdoutRl;
|
|
15047
|
+
stderrRl;
|
|
15048
|
+
lastStderrLine;
|
|
15049
|
+
requestSeq = 0;
|
|
15050
|
+
pending = /* @__PURE__ */ new Map();
|
|
15051
|
+
startingPromise;
|
|
15052
|
+
eventListeners = /* @__PURE__ */ new Set();
|
|
15053
|
+
stderrListeners = /* @__PURE__ */ new Set();
|
|
15054
|
+
eventBuffer = [];
|
|
15055
|
+
maxBufferSize = 1e3;
|
|
15056
|
+
exitPromise;
|
|
15057
|
+
/** The workspace key returned by the broker in its hello_ack response. */
|
|
15058
|
+
workspaceKey;
|
|
15059
|
+
constructor(options = {}) {
|
|
15060
|
+
this.options = {
|
|
15061
|
+
binaryPath: options.binaryPath ?? resolveDefaultBinaryPath(),
|
|
15062
|
+
binaryArgs: options.binaryArgs ?? [],
|
|
15063
|
+
brokerName: options.brokerName ?? (import_node_path3.default.basename(options.cwd ?? process.cwd()) || "project"),
|
|
15064
|
+
channels: options.channels ?? ["general"],
|
|
15065
|
+
cwd: options.cwd ?? process.cwd(),
|
|
15066
|
+
env: options.env ?? process.env,
|
|
15067
|
+
requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
|
|
15068
|
+
shutdownTimeoutMs: options.shutdownTimeoutMs ?? 3e3,
|
|
15069
|
+
clientName: options.clientName ?? "@agent-relay/sdk",
|
|
15070
|
+
clientVersion: options.clientVersion ?? "0.1.0"
|
|
15071
|
+
};
|
|
15072
|
+
}
|
|
15073
|
+
static async start(options = {}) {
|
|
15074
|
+
const client = new _AgentRelayClient(options);
|
|
15075
|
+
await client.start();
|
|
15076
|
+
return client;
|
|
15077
|
+
}
|
|
15078
|
+
onEvent(listener) {
|
|
15079
|
+
this.eventListeners.add(listener);
|
|
15080
|
+
return () => {
|
|
15081
|
+
this.eventListeners.delete(listener);
|
|
15082
|
+
};
|
|
15083
|
+
}
|
|
15084
|
+
queryEvents(filter) {
|
|
15085
|
+
let events = [...this.eventBuffer];
|
|
15086
|
+
if (filter?.kind) {
|
|
15087
|
+
events = events.filter((event) => event.kind === filter.kind);
|
|
15088
|
+
}
|
|
15089
|
+
if (filter?.name) {
|
|
15090
|
+
events = events.filter((event) => "name" in event && event.name === filter.name);
|
|
15091
|
+
}
|
|
15092
|
+
const since = filter?.since;
|
|
15093
|
+
if (since !== void 0) {
|
|
15094
|
+
events = events.filter((event) => "timestamp" in event && typeof event.timestamp === "number" && event.timestamp >= since);
|
|
15095
|
+
}
|
|
15096
|
+
const limit = filter?.limit;
|
|
15097
|
+
if (limit !== void 0) {
|
|
15098
|
+
events = events.slice(-limit);
|
|
15099
|
+
}
|
|
15100
|
+
return events;
|
|
15101
|
+
}
|
|
15102
|
+
getLastEvent(kind, name) {
|
|
15103
|
+
for (let i = this.eventBuffer.length - 1; i >= 0; i -= 1) {
|
|
15104
|
+
const event = this.eventBuffer[i];
|
|
15105
|
+
if (event.kind === kind && (!name || "name" in event && event.name === name)) {
|
|
15106
|
+
return event;
|
|
15107
|
+
}
|
|
15108
|
+
}
|
|
15109
|
+
return void 0;
|
|
15110
|
+
}
|
|
15111
|
+
onBrokerStderr(listener) {
|
|
15112
|
+
this.stderrListeners.add(listener);
|
|
15113
|
+
return () => {
|
|
15114
|
+
this.stderrListeners.delete(listener);
|
|
15115
|
+
};
|
|
15116
|
+
}
|
|
15117
|
+
get brokerPid() {
|
|
15118
|
+
return this.child?.pid;
|
|
15119
|
+
}
|
|
15120
|
+
async start() {
|
|
15121
|
+
if (this.child) {
|
|
15122
|
+
return;
|
|
15123
|
+
}
|
|
15124
|
+
if (this.startingPromise) {
|
|
15125
|
+
return this.startingPromise;
|
|
15126
|
+
}
|
|
15127
|
+
this.startingPromise = this.startInternal();
|
|
15128
|
+
try {
|
|
15129
|
+
await this.startingPromise;
|
|
15130
|
+
} finally {
|
|
15131
|
+
this.startingPromise = void 0;
|
|
15132
|
+
}
|
|
15133
|
+
}
|
|
15134
|
+
/**
|
|
15135
|
+
* Pre-register a batch of agents with Relaycast before their steps execute.
|
|
15136
|
+
* The broker warms its token cache in parallel; subsequent spawn_agent calls
|
|
15137
|
+
* hit the cache rather than waiting on individual HTTP registrations.
|
|
15138
|
+
* Fire-and-forget from the caller's perspective — broker responds immediately
|
|
15139
|
+
* and registers in the background.
|
|
15140
|
+
*/
|
|
15141
|
+
async preflightAgents(agents) {
|
|
15142
|
+
if (agents.length === 0)
|
|
15143
|
+
return;
|
|
15144
|
+
await this.start();
|
|
15145
|
+
await this.requestOk("preflight_agents", { agents });
|
|
15146
|
+
}
|
|
15147
|
+
async spawnPty(input) {
|
|
15148
|
+
await this.start();
|
|
15149
|
+
const args = buildPtyArgsWithModel(input.cli, input.args ?? [], input.model);
|
|
15150
|
+
const agent = {
|
|
15151
|
+
name: input.name,
|
|
15152
|
+
runtime: "pty",
|
|
15153
|
+
cli: input.cli,
|
|
15154
|
+
args,
|
|
15155
|
+
channels: input.channels ?? [],
|
|
15156
|
+
model: input.model,
|
|
15157
|
+
cwd: input.cwd ?? this.options.cwd,
|
|
15158
|
+
team: input.team,
|
|
15159
|
+
shadow_of: input.shadowOf,
|
|
15160
|
+
shadow_mode: input.shadowMode,
|
|
15161
|
+
restart_policy: input.restartPolicy
|
|
15162
|
+
};
|
|
15163
|
+
const result = await this.requestOk("spawn_agent", {
|
|
15164
|
+
agent,
|
|
15165
|
+
...input.task != null ? { initial_task: input.task } : {},
|
|
15166
|
+
...input.idleThresholdSecs != null ? { idle_threshold_secs: input.idleThresholdSecs } : {},
|
|
15167
|
+
...input.continueFrom != null ? { continue_from: input.continueFrom } : {},
|
|
15168
|
+
...input.skipRelayPrompt != null ? { skip_relay_prompt: input.skipRelayPrompt } : {}
|
|
15169
|
+
});
|
|
15170
|
+
return result;
|
|
15171
|
+
}
|
|
15172
|
+
async spawnHeadless(input) {
|
|
15173
|
+
await this.start();
|
|
15174
|
+
const agent = {
|
|
15175
|
+
name: input.name,
|
|
15176
|
+
runtime: "headless",
|
|
15177
|
+
provider: input.provider,
|
|
15178
|
+
args: input.args ?? [],
|
|
15179
|
+
channels: input.channels ?? []
|
|
15180
|
+
};
|
|
15181
|
+
const result = await this.requestOk("spawn_agent", {
|
|
15182
|
+
agent,
|
|
15183
|
+
...input.task != null ? { initial_task: input.task } : {},
|
|
15184
|
+
...input.skipRelayPrompt != null ? { skip_relay_prompt: input.skipRelayPrompt } : {}
|
|
15185
|
+
});
|
|
15186
|
+
return result;
|
|
15187
|
+
}
|
|
15188
|
+
async spawnProvider(input) {
|
|
15189
|
+
const transport = input.transport ?? (input.provider === "opencode" ? "headless" : "pty");
|
|
15190
|
+
if (transport === "headless") {
|
|
15191
|
+
if (!isHeadlessProvider(input.provider)) {
|
|
15192
|
+
throw new AgentRelayProcessError(`provider '${input.provider}' does not support headless transport (supported: claude, opencode)`);
|
|
15193
|
+
}
|
|
15194
|
+
return this.spawnHeadless({
|
|
15195
|
+
name: input.name,
|
|
15196
|
+
provider: input.provider,
|
|
15197
|
+
args: input.args,
|
|
15198
|
+
channels: input.channels,
|
|
15199
|
+
task: input.task,
|
|
15200
|
+
skipRelayPrompt: input.skipRelayPrompt
|
|
15201
|
+
});
|
|
15202
|
+
}
|
|
15203
|
+
return this.spawnPty({
|
|
15204
|
+
name: input.name,
|
|
15205
|
+
cli: input.provider,
|
|
15206
|
+
args: input.args,
|
|
15207
|
+
channels: input.channels,
|
|
15208
|
+
task: input.task,
|
|
15209
|
+
model: input.model,
|
|
15210
|
+
cwd: input.cwd,
|
|
15211
|
+
team: input.team,
|
|
15212
|
+
shadowOf: input.shadowOf,
|
|
15213
|
+
shadowMode: input.shadowMode,
|
|
15214
|
+
idleThresholdSecs: input.idleThresholdSecs,
|
|
15215
|
+
restartPolicy: input.restartPolicy,
|
|
15216
|
+
continueFrom: input.continueFrom,
|
|
15217
|
+
skipRelayPrompt: input.skipRelayPrompt
|
|
15218
|
+
});
|
|
15219
|
+
}
|
|
15220
|
+
async spawnClaude(input) {
|
|
15221
|
+
return this.spawnProvider({ ...input, provider: "claude" });
|
|
15222
|
+
}
|
|
15223
|
+
async spawnOpencode(input) {
|
|
15224
|
+
return this.spawnProvider({ ...input, provider: "opencode" });
|
|
15225
|
+
}
|
|
15226
|
+
async release(name, reason) {
|
|
15227
|
+
await this.start();
|
|
15228
|
+
return this.requestOk("release_agent", { name, reason });
|
|
15229
|
+
}
|
|
15230
|
+
async sendInput(name, data) {
|
|
15231
|
+
await this.start();
|
|
15232
|
+
return this.requestOk("send_input", { name, data });
|
|
15233
|
+
}
|
|
15234
|
+
async setModel(name, model, opts) {
|
|
15235
|
+
await this.start();
|
|
15236
|
+
return this.requestOk("set_model", {
|
|
15237
|
+
name,
|
|
15238
|
+
model,
|
|
15239
|
+
timeout_ms: opts?.timeoutMs
|
|
15240
|
+
});
|
|
15241
|
+
}
|
|
15242
|
+
async getMetrics(agent) {
|
|
15243
|
+
await this.start();
|
|
15244
|
+
return this.requestOk("get_metrics", { agent });
|
|
15245
|
+
}
|
|
15246
|
+
async getCrashInsights() {
|
|
15247
|
+
await this.start();
|
|
15248
|
+
return this.requestOk("get_crash_insights", {});
|
|
15249
|
+
}
|
|
15250
|
+
async sendMessage(input) {
|
|
15251
|
+
await this.start();
|
|
15252
|
+
try {
|
|
15253
|
+
return await this.requestOk("send_message", {
|
|
15254
|
+
to: input.to,
|
|
15255
|
+
text: input.text,
|
|
15256
|
+
from: input.from,
|
|
15257
|
+
thread_id: input.threadId,
|
|
15258
|
+
workspace_id: input.workspaceId,
|
|
15259
|
+
workspace_alias: input.workspaceAlias,
|
|
15260
|
+
priority: input.priority,
|
|
15261
|
+
data: input.data
|
|
15262
|
+
});
|
|
15263
|
+
} catch (error48) {
|
|
15264
|
+
if (error48 instanceof AgentRelayProtocolError && error48.code === "unsupported_operation") {
|
|
15265
|
+
return { event_id: "unsupported_operation", targets: [] };
|
|
15266
|
+
}
|
|
15267
|
+
throw error48;
|
|
15268
|
+
}
|
|
15269
|
+
}
|
|
15270
|
+
async listAgents() {
|
|
15271
|
+
await this.start();
|
|
15272
|
+
const result = await this.requestOk("list_agents", {});
|
|
15273
|
+
return result.agents;
|
|
15274
|
+
}
|
|
15275
|
+
async getStatus() {
|
|
15276
|
+
await this.start();
|
|
15277
|
+
return this.requestOk("get_status", {});
|
|
15278
|
+
}
|
|
15279
|
+
async shutdown() {
|
|
15280
|
+
if (!this.child) {
|
|
15281
|
+
return;
|
|
15282
|
+
}
|
|
15283
|
+
void this.requestOk("shutdown", {}).catch(() => {
|
|
15284
|
+
});
|
|
15285
|
+
const child = this.child;
|
|
15286
|
+
const wait = this.exitPromise ?? Promise.resolve();
|
|
15287
|
+
const waitForExit = async (timeoutMs) => {
|
|
15288
|
+
let timer;
|
|
15289
|
+
const result = await Promise.race([
|
|
15290
|
+
wait.then(() => true),
|
|
15291
|
+
new Promise((resolve3) => {
|
|
15292
|
+
timer = setTimeout(() => resolve3(false), timeoutMs);
|
|
15293
|
+
})
|
|
15294
|
+
]);
|
|
15295
|
+
if (timer !== void 0)
|
|
15296
|
+
clearTimeout(timer);
|
|
15297
|
+
return result;
|
|
15298
|
+
};
|
|
15299
|
+
if (await waitForExit(this.options.shutdownTimeoutMs)) {
|
|
15300
|
+
return;
|
|
15301
|
+
}
|
|
15302
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
15303
|
+
child.kill("SIGTERM");
|
|
15304
|
+
}
|
|
15305
|
+
if (await waitForExit(1e3)) {
|
|
15306
|
+
return;
|
|
15307
|
+
}
|
|
15308
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
15309
|
+
child.kill("SIGKILL");
|
|
15310
|
+
}
|
|
15311
|
+
await waitForExit(1e3);
|
|
15312
|
+
}
|
|
15313
|
+
async waitForExit() {
|
|
15314
|
+
if (!this.child) {
|
|
15315
|
+
return;
|
|
15316
|
+
}
|
|
15317
|
+
await this.exitPromise;
|
|
15318
|
+
}
|
|
15319
|
+
async startInternal() {
|
|
15320
|
+
const resolvedBinary = expandTilde(this.options.binaryPath);
|
|
15321
|
+
if (isExplicitPath(this.options.binaryPath) && !import_node_fs2.default.existsSync(resolvedBinary)) {
|
|
15322
|
+
throw new AgentRelayProcessError(`broker binary not found: ${this.options.binaryPath}`);
|
|
15323
|
+
}
|
|
15324
|
+
this.lastStderrLine = void 0;
|
|
15325
|
+
const args = [
|
|
15326
|
+
"init",
|
|
15327
|
+
"--name",
|
|
15328
|
+
this.options.brokerName,
|
|
15329
|
+
...this.options.channels.length > 0 ? ["--channels", this.options.channels.join(",")] : [],
|
|
15330
|
+
...this.options.binaryArgs
|
|
15331
|
+
];
|
|
15332
|
+
const env = { ...this.options.env };
|
|
15333
|
+
if (isExplicitPath(this.options.binaryPath)) {
|
|
15334
|
+
const binDir = import_node_path3.default.dirname(import_node_path3.default.resolve(resolvedBinary));
|
|
15335
|
+
const currentPath = env.PATH ?? env.Path ?? "";
|
|
15336
|
+
if (!currentPath.split(import_node_path3.default.delimiter).includes(binDir)) {
|
|
15337
|
+
env.PATH = `${binDir}${import_node_path3.default.delimiter}${currentPath}`;
|
|
15338
|
+
}
|
|
15339
|
+
}
|
|
15340
|
+
console.error(`[broker] Starting: ${resolvedBinary} ${args.join(" ")}`);
|
|
15341
|
+
const child = (0, import_node_child_process2.spawn)(resolvedBinary, args, {
|
|
15342
|
+
cwd: this.options.cwd,
|
|
15343
|
+
env,
|
|
15344
|
+
stdio: "pipe"
|
|
15345
|
+
});
|
|
15346
|
+
this.child = child;
|
|
15347
|
+
this.stdoutRl = (0, import_node_readline.createInterface)({ input: child.stdout, crlfDelay: Infinity });
|
|
15348
|
+
this.stderrRl = (0, import_node_readline.createInterface)({ input: child.stderr, crlfDelay: Infinity });
|
|
15349
|
+
this.stdoutRl.on("line", (line) => {
|
|
15350
|
+
this.handleStdoutLine(line);
|
|
15351
|
+
});
|
|
15352
|
+
this.stderrRl.on("line", (line) => {
|
|
15353
|
+
const trimmed = line.trim();
|
|
15354
|
+
if (trimmed) {
|
|
15355
|
+
this.lastStderrLine = trimmed;
|
|
15356
|
+
}
|
|
15357
|
+
for (const listener of this.stderrListeners) {
|
|
15358
|
+
listener(line);
|
|
15359
|
+
}
|
|
15360
|
+
});
|
|
15361
|
+
this.exitPromise = new Promise((resolve3) => {
|
|
15362
|
+
child.once("close", (code, signal) => {
|
|
15363
|
+
const detail = this.lastStderrLine ? `: ${this.lastStderrLine}` : "";
|
|
15364
|
+
const error48 = new AgentRelayProcessError(`broker exited (code=${code ?? "null"}, signal=${signal ?? "null"})${detail}`);
|
|
15365
|
+
this.failAllPending(error48);
|
|
15366
|
+
this.disposeProcessHandles();
|
|
15367
|
+
resolve3();
|
|
15368
|
+
});
|
|
15369
|
+
child.once("error", (error48) => {
|
|
15370
|
+
this.failAllPending(error48);
|
|
15371
|
+
this.disposeProcessHandles();
|
|
15372
|
+
resolve3();
|
|
15373
|
+
});
|
|
15374
|
+
});
|
|
15375
|
+
const helloAck = await this.requestHello();
|
|
15376
|
+
console.error("[broker] Broker ready (hello handshake complete)");
|
|
15377
|
+
if (helloAck.workspace_key) {
|
|
15378
|
+
this.workspaceKey = helloAck.workspace_key;
|
|
15379
|
+
}
|
|
15380
|
+
}
|
|
15381
|
+
disposeProcessHandles() {
|
|
15382
|
+
this.stdoutRl?.close();
|
|
15383
|
+
this.stderrRl?.close();
|
|
15384
|
+
this.stdoutRl = void 0;
|
|
15385
|
+
this.stderrRl = void 0;
|
|
15386
|
+
this.lastStderrLine = void 0;
|
|
15387
|
+
this.child = void 0;
|
|
15388
|
+
this.exitPromise = void 0;
|
|
15389
|
+
}
|
|
15390
|
+
failAllPending(error48) {
|
|
15391
|
+
for (const pending of this.pending.values()) {
|
|
15392
|
+
clearTimeout(pending.timeout);
|
|
15393
|
+
pending.reject(error48);
|
|
15394
|
+
}
|
|
15395
|
+
this.pending.clear();
|
|
15396
|
+
}
|
|
15397
|
+
handleStdoutLine(line) {
|
|
15398
|
+
let parsed;
|
|
15399
|
+
try {
|
|
15400
|
+
parsed = JSON.parse(line);
|
|
15401
|
+
} catch {
|
|
15402
|
+
return;
|
|
15403
|
+
}
|
|
15404
|
+
if (!parsed || typeof parsed !== "object") {
|
|
15405
|
+
return;
|
|
15406
|
+
}
|
|
15407
|
+
if (parsed.v !== PROTOCOL_VERSION || typeof parsed.type !== "string") {
|
|
15408
|
+
return;
|
|
15409
|
+
}
|
|
15410
|
+
const envelope = {
|
|
15411
|
+
v: parsed.v,
|
|
15412
|
+
type: parsed.type,
|
|
15413
|
+
request_id: parsed.request_id,
|
|
15414
|
+
payload: parsed.payload
|
|
15415
|
+
};
|
|
15416
|
+
if (envelope.type === "event") {
|
|
15417
|
+
const payload = envelope.payload;
|
|
15418
|
+
this.eventBuffer.push(payload);
|
|
15419
|
+
if (this.eventBuffer.length > this.maxBufferSize) {
|
|
15420
|
+
this.eventBuffer.shift();
|
|
15421
|
+
}
|
|
15422
|
+
for (const listener of this.eventListeners) {
|
|
15423
|
+
listener(payload);
|
|
15424
|
+
}
|
|
15425
|
+
return;
|
|
15426
|
+
}
|
|
15427
|
+
if (!envelope.request_id) {
|
|
15428
|
+
return;
|
|
15429
|
+
}
|
|
15430
|
+
const pending = this.pending.get(envelope.request_id);
|
|
15431
|
+
if (!pending) {
|
|
15432
|
+
return;
|
|
15433
|
+
}
|
|
15434
|
+
if (envelope.type === "error") {
|
|
15435
|
+
clearTimeout(pending.timeout);
|
|
15436
|
+
this.pending.delete(envelope.request_id);
|
|
15437
|
+
pending.reject(new AgentRelayProtocolError(envelope.payload));
|
|
15438
|
+
return;
|
|
15439
|
+
}
|
|
15440
|
+
if (envelope.type !== pending.expectedType) {
|
|
15441
|
+
clearTimeout(pending.timeout);
|
|
15442
|
+
this.pending.delete(envelope.request_id);
|
|
15443
|
+
pending.reject(new AgentRelayProcessError(`unexpected response type '${envelope.type}' for request '${envelope.request_id}' (expected '${pending.expectedType}')`));
|
|
15444
|
+
return;
|
|
15445
|
+
}
|
|
15446
|
+
clearTimeout(pending.timeout);
|
|
15447
|
+
this.pending.delete(envelope.request_id);
|
|
15448
|
+
pending.resolve(envelope);
|
|
15449
|
+
}
|
|
15450
|
+
async requestHello() {
|
|
15451
|
+
const payload = {
|
|
15452
|
+
client_name: this.options.clientName,
|
|
15453
|
+
client_version: this.options.clientVersion
|
|
15454
|
+
};
|
|
15455
|
+
const frame = await this.sendRequest("hello", payload, "hello_ack");
|
|
15456
|
+
return frame.payload;
|
|
15457
|
+
}
|
|
15458
|
+
async requestOk(type, payload) {
|
|
15459
|
+
const frame = await this.sendRequest(type, payload, "ok");
|
|
15460
|
+
const result = frame.payload;
|
|
15461
|
+
return result.result;
|
|
15462
|
+
}
|
|
15463
|
+
async sendRequest(type, payload, expectedType) {
|
|
15464
|
+
if (!this.child) {
|
|
15465
|
+
throw new AgentRelayProcessError("broker is not running");
|
|
15466
|
+
}
|
|
15467
|
+
const requestId = `req_${++this.requestSeq}`;
|
|
15468
|
+
const message = {
|
|
15469
|
+
v: PROTOCOL_VERSION,
|
|
15470
|
+
type,
|
|
15471
|
+
request_id: requestId,
|
|
15472
|
+
payload
|
|
15473
|
+
};
|
|
15474
|
+
const responsePromise = new Promise((resolve3, reject) => {
|
|
15475
|
+
const timeout = setTimeout(() => {
|
|
15476
|
+
this.pending.delete(requestId);
|
|
15477
|
+
reject(new AgentRelayProcessError(`request timed out after ${this.options.requestTimeoutMs}ms (type='${type}', request_id='${requestId}')`));
|
|
15478
|
+
}, this.options.requestTimeoutMs);
|
|
15479
|
+
this.pending.set(requestId, {
|
|
15480
|
+
expectedType,
|
|
15481
|
+
resolve: resolve3,
|
|
15482
|
+
reject,
|
|
15483
|
+
timeout
|
|
15484
|
+
});
|
|
15485
|
+
});
|
|
15486
|
+
const line = `${JSON.stringify(message)}
|
|
15487
|
+
`;
|
|
15488
|
+
if (!this.child.stdin.write(line)) {
|
|
15489
|
+
await (0, import_node_events.once)(this.child.stdin, "drain");
|
|
15490
|
+
}
|
|
15491
|
+
return responsePromise;
|
|
15492
|
+
}
|
|
15493
|
+
};
|
|
15494
|
+
var CLI_MODEL_FLAG_CLIS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "goose", "aider"]);
|
|
15495
|
+
var CLI_DEFAULT_ARGS = {
|
|
15496
|
+
codex: ["-c", "check_for_update_on_startup=false"]
|
|
15525
15497
|
};
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
|
|
15552
|
-
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
}
|
|
15569
|
-
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15498
|
+
function buildPtyArgsWithModel(cli, args, model) {
|
|
15499
|
+
const cliName = cli.split(":")[0].trim().toLowerCase();
|
|
15500
|
+
const defaultArgs = CLI_DEFAULT_ARGS[cliName] ?? [];
|
|
15501
|
+
const baseArgs = [...defaultArgs, ...args];
|
|
15502
|
+
if (!model) {
|
|
15503
|
+
return baseArgs;
|
|
15504
|
+
}
|
|
15505
|
+
if (!CLI_MODEL_FLAG_CLIS.has(cliName)) {
|
|
15506
|
+
return baseArgs;
|
|
15507
|
+
}
|
|
15508
|
+
if (hasModelArg(baseArgs)) {
|
|
15509
|
+
return baseArgs;
|
|
15510
|
+
}
|
|
15511
|
+
return ["--model", model, ...baseArgs];
|
|
15512
|
+
}
|
|
15513
|
+
function hasModelArg(args) {
|
|
15514
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
15515
|
+
const arg = args[i];
|
|
15516
|
+
if (arg === "--model") {
|
|
15517
|
+
return true;
|
|
15518
|
+
}
|
|
15519
|
+
if (arg.startsWith("--model=")) {
|
|
15520
|
+
return true;
|
|
15521
|
+
}
|
|
15522
|
+
}
|
|
15523
|
+
return false;
|
|
15524
|
+
}
|
|
15525
|
+
function expandTilde(p) {
|
|
15526
|
+
if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
|
|
15527
|
+
const home = import_node_os3.default.homedir();
|
|
15528
|
+
return import_node_path3.default.join(home, p.slice(2));
|
|
15529
|
+
}
|
|
15530
|
+
return p;
|
|
15531
|
+
}
|
|
15532
|
+
function isExplicitPath(binaryPath) {
|
|
15533
|
+
return binaryPath.includes("/") || binaryPath.includes("\\") || binaryPath.startsWith(".") || binaryPath.startsWith("~");
|
|
15534
|
+
}
|
|
15535
|
+
function detectPlatformSuffix() {
|
|
15536
|
+
const platformMap = {
|
|
15537
|
+
darwin: { arm64: "darwin-arm64", x64: "darwin-x64" },
|
|
15538
|
+
linux: { arm64: "linux-arm64", x64: "linux-x64" },
|
|
15539
|
+
win32: { x64: "win32-x64" }
|
|
15540
|
+
};
|
|
15541
|
+
return platformMap[process.platform]?.[process.arch] ?? null;
|
|
15542
|
+
}
|
|
15543
|
+
function getLatestVersionSync() {
|
|
15544
|
+
try {
|
|
15545
|
+
const result = (0, import_node_child_process2.execSync)("curl -fsSL https://api.github.com/repos/AgentWorkforce/relay/releases/latest", {
|
|
15546
|
+
timeout: 15e3,
|
|
15547
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
15548
|
+
}).toString();
|
|
15549
|
+
const match = result.match(/"tag_name"\s*:\s*"([^"]+)"/);
|
|
15550
|
+
if (!match?.[1])
|
|
15551
|
+
return null;
|
|
15552
|
+
return match[1].replace(/^openclaw-/, "").replace(/^v/, "");
|
|
15553
|
+
} catch {
|
|
15554
|
+
return null;
|
|
15555
|
+
}
|
|
15556
|
+
}
|
|
15557
|
+
function installBrokerBinary() {
|
|
15558
|
+
const suffix = detectPlatformSuffix();
|
|
15559
|
+
if (!suffix) {
|
|
15560
|
+
throw new AgentRelayProcessError(`Unsupported platform: ${process.platform}-${process.arch}`);
|
|
15561
|
+
}
|
|
15562
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
15563
|
+
const installDir = import_node_path3.default.join(homeDir, ".agent-relay", "bin");
|
|
15564
|
+
const brokerExe = process.platform === "win32" ? "agent-relay-broker.exe" : "agent-relay-broker";
|
|
15565
|
+
const targetPath = import_node_path3.default.join(installDir, brokerExe);
|
|
15566
|
+
console.log(`[agent-relay] Broker binary not found, installing for ${suffix}...`);
|
|
15567
|
+
const version2 = getLatestVersionSync();
|
|
15568
|
+
if (!version2) {
|
|
15569
|
+
throw new AgentRelayProcessError("Failed to fetch latest agent-relay version from GitHub.\nInstall manually: curl -fsSL https://raw.githubusercontent.com/AgentWorkforce/relay/main/install.sh | bash");
|
|
15570
|
+
}
|
|
15571
|
+
const binaryName = `agent-relay-broker-${suffix}`;
|
|
15572
|
+
const downloadUrl = `https://github.com/AgentWorkforce/relay/releases/download/v${version2}/${binaryName}`;
|
|
15573
|
+
console.log(`[agent-relay] Downloading v${version2} from ${downloadUrl}`);
|
|
15574
|
+
try {
|
|
15575
|
+
import_node_fs2.default.mkdirSync(installDir, { recursive: true });
|
|
15576
|
+
(0, import_node_child_process2.execSync)(`curl -fsSL "${downloadUrl}" -o "${targetPath}"`, {
|
|
15577
|
+
timeout: 6e4,
|
|
15578
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
15579
|
+
});
|
|
15580
|
+
import_node_fs2.default.chmodSync(targetPath, 493);
|
|
15581
|
+
if (process.platform === "darwin") {
|
|
15582
|
+
try {
|
|
15583
|
+
(0, import_node_child_process2.execSync)(`xattr -d com.apple.quarantine "${targetPath}" 2>/dev/null || true`, {
|
|
15584
|
+
timeout: 1e4,
|
|
15585
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
15586
|
+
});
|
|
15587
|
+
} catch {
|
|
15588
|
+
}
|
|
15589
|
+
try {
|
|
15590
|
+
(0, import_node_child_process2.execSync)(`codesign --force --sign - "${targetPath}"`, {
|
|
15591
|
+
timeout: 1e4,
|
|
15592
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
15593
|
+
});
|
|
15594
|
+
} catch {
|
|
15595
|
+
}
|
|
15596
|
+
}
|
|
15597
|
+
(0, import_node_child_process2.execSync)(`"${targetPath}" --help`, { timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] });
|
|
15598
|
+
} catch (err) {
|
|
15599
|
+
try {
|
|
15600
|
+
import_node_fs2.default.unlinkSync(targetPath);
|
|
15601
|
+
} catch {
|
|
15602
|
+
}
|
|
15603
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15604
|
+
throw new AgentRelayProcessError(`Failed to install broker binary: ${message}
|
|
15605
|
+
Install manually: curl -fsSL https://raw.githubusercontent.com/AgentWorkforce/relay/main/install.sh | bash`);
|
|
15606
|
+
}
|
|
15607
|
+
console.log(`[agent-relay] Broker installed to ${targetPath}`);
|
|
15608
|
+
return targetPath;
|
|
15609
|
+
}
|
|
15610
|
+
function resolveDefaultBinaryPath() {
|
|
15611
|
+
const brokerExe = process.platform === "win32" ? "agent-relay-broker.exe" : "agent-relay-broker";
|
|
15612
|
+
const moduleDir = import_node_path3.default.dirname((0, import_node_url.fileURLToPath)(import_meta_url));
|
|
15613
|
+
const workspaceRelease = import_node_path3.default.resolve(moduleDir, "..", "..", "..", "target", "release", brokerExe);
|
|
15614
|
+
if (import_node_fs2.default.existsSync(workspaceRelease)) {
|
|
15615
|
+
return workspaceRelease;
|
|
15616
|
+
}
|
|
15617
|
+
const binDir = import_node_path3.default.resolve(moduleDir, "..", "bin");
|
|
15618
|
+
const suffix = detectPlatformSuffix();
|
|
15619
|
+
if (suffix) {
|
|
15620
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
15621
|
+
const platformBinary = import_node_path3.default.join(binDir, `agent-relay-broker-${suffix}${ext}`);
|
|
15622
|
+
if (import_node_fs2.default.existsSync(platformBinary)) {
|
|
15623
|
+
return platformBinary;
|
|
15624
|
+
}
|
|
15625
|
+
}
|
|
15626
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
15627
|
+
const standaloneBroker = import_node_path3.default.join(homeDir, ".agent-relay", "bin", brokerExe);
|
|
15628
|
+
if (import_node_fs2.default.existsSync(standaloneBroker)) {
|
|
15629
|
+
return standaloneBroker;
|
|
15630
|
+
}
|
|
15631
|
+
return installBrokerBinary();
|
|
15632
|
+
}
|
|
15633
|
+
var DEFAULT_DASHBOARD_PORT = (() => {
|
|
15634
|
+
const envPort = typeof process !== "undefined" ? process.env.AGENT_RELAY_DASHBOARD_PORT : void 0;
|
|
15635
|
+
if (envPort) {
|
|
15636
|
+
const parsed = Number.parseInt(envPort, 10);
|
|
15637
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
15638
|
+
return parsed;
|
|
15639
|
+
}
|
|
15640
|
+
return 3888;
|
|
15641
|
+
})();
|
|
15642
|
+
var HTTP_MAX_PORT_SCAN = 25;
|
|
15643
|
+
var HTTP_AUTOSTART_TIMEOUT_MS = 1e4;
|
|
15644
|
+
var HTTP_AUTOSTART_POLL_MS = 250;
|
|
15645
|
+
function sanitizeBrokerName(name) {
|
|
15646
|
+
return name.replace(/[^\p{L}\p{N}-]/gu, "-");
|
|
15647
|
+
}
|
|
15648
|
+
function brokerPidFilename(projectRoot) {
|
|
15649
|
+
const brokerName = import_node_path3.default.basename(projectRoot) || "project";
|
|
15650
|
+
return `broker-${sanitizeBrokerName(brokerName)}.pid`;
|
|
15651
|
+
}
|
|
15652
|
+
var HttpAgentRelayClient = class _HttpAgentRelayClient {
|
|
15653
|
+
port;
|
|
15654
|
+
apiKey;
|
|
15655
|
+
constructor(options) {
|
|
15656
|
+
this.port = options.port;
|
|
15657
|
+
this.apiKey = options.apiKey;
|
|
15658
|
+
}
|
|
15659
|
+
/**
|
|
15660
|
+
* Connect to an already-running broker on the given port.
|
|
15661
|
+
*/
|
|
15662
|
+
static async connectHttp(port, options) {
|
|
15663
|
+
const client = new _HttpAgentRelayClient({ port, apiKey: options?.apiKey });
|
|
15664
|
+
await client.healthCheck();
|
|
15665
|
+
return client;
|
|
15666
|
+
}
|
|
15667
|
+
/**
|
|
15668
|
+
* Discover a running broker for the current project and connect to it.
|
|
15669
|
+
* Reads the broker PID file, verifies the process is alive, scans ports
|
|
15670
|
+
* for the HTTP API, and returns a connected client.
|
|
15671
|
+
*/
|
|
15672
|
+
static async discoverAndConnect(options) {
|
|
15673
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
15674
|
+
const apiKey = options?.apiKey ?? process.env.RELAY_BROKER_API_KEY?.trim();
|
|
15675
|
+
const autoStart = options?.autoStart ?? false;
|
|
15676
|
+
const paths = getProjectPaths(cwd);
|
|
15677
|
+
const preferredApiPort = DEFAULT_DASHBOARD_PORT + 1;
|
|
15678
|
+
const pidFilePath = import_node_path3.default.join(paths.dataDir, brokerPidFilename(paths.projectRoot));
|
|
15679
|
+
const legacyPidPath = import_node_path3.default.join(paths.dataDir, "broker.pid");
|
|
15680
|
+
let brokerRunning = false;
|
|
15681
|
+
for (const pidPath of [pidFilePath, legacyPidPath]) {
|
|
15682
|
+
if (import_node_fs2.default.existsSync(pidPath)) {
|
|
15683
|
+
const pidStr = import_node_fs2.default.readFileSync(pidPath, "utf-8").trim();
|
|
15684
|
+
const pid = Number.parseInt(pidStr, 10);
|
|
15685
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
15686
|
+
try {
|
|
15687
|
+
process.kill(pid, 0);
|
|
15688
|
+
brokerRunning = true;
|
|
15689
|
+
break;
|
|
15690
|
+
} catch {
|
|
15691
|
+
}
|
|
15692
|
+
}
|
|
15693
|
+
}
|
|
15694
|
+
}
|
|
15695
|
+
if (brokerRunning) {
|
|
15696
|
+
const port = await _HttpAgentRelayClient.scanForBrokerPort(preferredApiPort);
|
|
15697
|
+
if (port !== null) {
|
|
15698
|
+
return new _HttpAgentRelayClient({ port, apiKey });
|
|
15699
|
+
}
|
|
15700
|
+
throw new AgentRelayProcessError("broker is running for this project, but its local API is unavailable");
|
|
15701
|
+
}
|
|
15702
|
+
if (!autoStart) {
|
|
15703
|
+
throw new AgentRelayProcessError("broker is not running for this project");
|
|
15704
|
+
}
|
|
15705
|
+
const brokerBinary = options?.brokerBinaryPath ?? resolveDefaultBinaryPath();
|
|
15706
|
+
const child = (0, import_node_child_process2.spawn)(brokerBinary, ["init", "--persist", "--api-port", String(preferredApiPort)], {
|
|
15707
|
+
cwd: paths.projectRoot,
|
|
15708
|
+
env: process.env,
|
|
15709
|
+
detached: true,
|
|
15710
|
+
stdio: "ignore"
|
|
15711
|
+
});
|
|
15712
|
+
child.unref();
|
|
15713
|
+
const startedAt = Date.now();
|
|
15714
|
+
while (Date.now() - startedAt < HTTP_AUTOSTART_TIMEOUT_MS) {
|
|
15715
|
+
const port = await _HttpAgentRelayClient.scanForBrokerPort(preferredApiPort);
|
|
15716
|
+
if (port !== null) {
|
|
15717
|
+
return new _HttpAgentRelayClient({ port, apiKey });
|
|
15718
|
+
}
|
|
15719
|
+
await new Promise((resolve3) => setTimeout(resolve3, HTTP_AUTOSTART_POLL_MS));
|
|
15720
|
+
}
|
|
15721
|
+
throw new AgentRelayProcessError(`broker did not become ready within ${HTTP_AUTOSTART_TIMEOUT_MS}ms`);
|
|
15722
|
+
}
|
|
15723
|
+
static async scanForBrokerPort(startPort) {
|
|
15724
|
+
for (let i = 0; i < HTTP_MAX_PORT_SCAN; i++) {
|
|
15725
|
+
const port = startPort + i;
|
|
15726
|
+
try {
|
|
15727
|
+
const res = await fetch(`http://127.0.0.1:${port}/health`);
|
|
15728
|
+
if (!res.ok)
|
|
15729
|
+
continue;
|
|
15730
|
+
const payload = await res.json().catch(() => null);
|
|
15731
|
+
if (payload?.service === "agent-relay-listen") {
|
|
15732
|
+
return port;
|
|
15733
|
+
}
|
|
15734
|
+
} catch {
|
|
15735
|
+
}
|
|
15736
|
+
}
|
|
15737
|
+
return null;
|
|
15738
|
+
}
|
|
15739
|
+
async request(pathname, init) {
|
|
15740
|
+
const headers = new Headers(init?.headers);
|
|
15741
|
+
if (this.apiKey && !headers.has("x-api-key") && !headers.has("authorization")) {
|
|
15742
|
+
headers.set("x-api-key", this.apiKey);
|
|
15743
|
+
}
|
|
15744
|
+
const response = await fetch(`http://127.0.0.1:${this.port}${pathname}`, {
|
|
15745
|
+
...init,
|
|
15746
|
+
headers
|
|
15747
|
+
});
|
|
15748
|
+
const text = await response.text();
|
|
15749
|
+
let payload;
|
|
15750
|
+
try {
|
|
15751
|
+
payload = text ? JSON.parse(text) : void 0;
|
|
15752
|
+
} catch {
|
|
15753
|
+
payload = text;
|
|
15754
|
+
}
|
|
15755
|
+
if (!response.ok) {
|
|
15756
|
+
const msg = _HttpAgentRelayClient.extractErrorMessage(response, payload);
|
|
15757
|
+
throw new AgentRelayProcessError(msg);
|
|
15758
|
+
}
|
|
15759
|
+
return payload;
|
|
15760
|
+
}
|
|
15761
|
+
static extractErrorMessage(response, payload) {
|
|
15762
|
+
if (typeof payload === "string" && payload.trim())
|
|
15763
|
+
return payload.trim();
|
|
15764
|
+
const p = payload;
|
|
15765
|
+
if (typeof p?.error === "string")
|
|
15766
|
+
return p.error;
|
|
15767
|
+
if (typeof p?.error?.message === "string")
|
|
15768
|
+
return p.error.message;
|
|
15769
|
+
if (typeof p?.message === "string" && p.message.trim())
|
|
15770
|
+
return p.message.trim();
|
|
15771
|
+
return `${response.status} ${response.statusText}`.trim();
|
|
15772
|
+
}
|
|
15773
|
+
async healthCheck() {
|
|
15774
|
+
return this.request("/health");
|
|
15775
|
+
}
|
|
15776
|
+
/** No-op — broker is already running. */
|
|
15777
|
+
async start() {
|
|
15778
|
+
}
|
|
15779
|
+
/** No-op — don't kill an externally-managed broker. */
|
|
15780
|
+
async shutdown() {
|
|
15781
|
+
}
|
|
15782
|
+
async spawnPty(input) {
|
|
15783
|
+
const payload = await this.request("/api/spawn", {
|
|
15784
|
+
method: "POST",
|
|
15785
|
+
headers: { "content-type": "application/json" },
|
|
15786
|
+
body: JSON.stringify({
|
|
15787
|
+
name: input.name,
|
|
15788
|
+
cli: input.cli,
|
|
15789
|
+
model: input.model,
|
|
15790
|
+
args: input.args ?? [],
|
|
15791
|
+
task: input.task,
|
|
15792
|
+
channels: input.channels ?? [],
|
|
15793
|
+
cwd: input.cwd,
|
|
15794
|
+
team: input.team,
|
|
15795
|
+
shadowOf: input.shadowOf,
|
|
15796
|
+
shadowMode: input.shadowMode,
|
|
15797
|
+
continueFrom: input.continueFrom,
|
|
15798
|
+
idleThresholdSecs: input.idleThresholdSecs,
|
|
15799
|
+
restartPolicy: input.restartPolicy,
|
|
15800
|
+
skipRelayPrompt: input.skipRelayPrompt
|
|
15801
|
+
})
|
|
15802
|
+
});
|
|
15803
|
+
return {
|
|
15804
|
+
name: typeof payload?.name === "string" ? payload.name : input.name,
|
|
15805
|
+
runtime: "pty"
|
|
15806
|
+
};
|
|
15807
|
+
}
|
|
15808
|
+
async sendMessage(input) {
|
|
15809
|
+
return this.request("/api/send", {
|
|
15810
|
+
method: "POST",
|
|
15811
|
+
headers: { "content-type": "application/json" },
|
|
15812
|
+
body: JSON.stringify({
|
|
15813
|
+
to: input.to,
|
|
15814
|
+
text: input.text,
|
|
15815
|
+
from: input.from,
|
|
15816
|
+
threadId: input.threadId,
|
|
15817
|
+
workspaceId: input.workspaceId,
|
|
15818
|
+
workspaceAlias: input.workspaceAlias,
|
|
15819
|
+
priority: input.priority,
|
|
15820
|
+
data: input.data
|
|
15821
|
+
})
|
|
15822
|
+
});
|
|
15823
|
+
}
|
|
15824
|
+
async listAgents() {
|
|
15825
|
+
const payload = await this.request("/api/spawned", { method: "GET" });
|
|
15826
|
+
return Array.isArray(payload?.agents) ? payload.agents : [];
|
|
15827
|
+
}
|
|
15828
|
+
async release(name, reason) {
|
|
15829
|
+
const payload = await this.request(`/api/spawned/${encodeURIComponent(name)}`, {
|
|
15830
|
+
method: "DELETE",
|
|
15831
|
+
...reason ? { headers: { "content-type": "application/json" }, body: JSON.stringify({ reason }) } : {}
|
|
15832
|
+
});
|
|
15833
|
+
return { name: typeof payload?.name === "string" ? payload.name : name };
|
|
15834
|
+
}
|
|
15835
|
+
async setModel(name, model, opts) {
|
|
15836
|
+
const payload = await this.request(`/api/spawned/${encodeURIComponent(name)}/model`, {
|
|
15837
|
+
method: "POST",
|
|
15838
|
+
headers: { "content-type": "application/json" },
|
|
15839
|
+
body: JSON.stringify({ model, timeoutMs: opts?.timeoutMs })
|
|
15840
|
+
});
|
|
15841
|
+
return {
|
|
15842
|
+
name,
|
|
15843
|
+
model: typeof payload?.model === "string" ? payload.model : model,
|
|
15844
|
+
success: payload?.success !== false
|
|
15845
|
+
};
|
|
15846
|
+
}
|
|
15847
|
+
async getConfig() {
|
|
15848
|
+
return this.request("/api/config");
|
|
15582
15849
|
}
|
|
15583
|
-
};
|
|
15584
|
-
var DefaultModels = {
|
|
15585
|
-
claude: "sonnet",
|
|
15586
|
-
codex: "gpt-5.4",
|
|
15587
|
-
gemini: "gemini-3.1-pro-preview",
|
|
15588
|
-
cursor: "opus-4.6-thinking",
|
|
15589
|
-
droid: "opus-4.6-fast",
|
|
15590
|
-
opencode: "openai/gpt-5.2"
|
|
15591
15850
|
};
|
|
15592
15851
|
|
|
15593
15852
|
// node_modules/@relaycast/sdk/dist/version.js
|
|
@@ -31425,7 +31684,7 @@ function cleanLines(raw) {
|
|
|
31425
31684
|
}
|
|
31426
31685
|
|
|
31427
31686
|
// packages/sdk/dist/relay.js
|
|
31428
|
-
var
|
|
31687
|
+
var import_node_crypto2 = require("node:crypto");
|
|
31429
31688
|
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
31430
31689
|
|
|
31431
31690
|
// packages/sdk/dist/logs.js
|
|
@@ -31843,7 +32102,7 @@ var AgentRelay = class {
|
|
|
31843
32102
|
if (result?.event_id === "unsupported_operation") {
|
|
31844
32103
|
return buildUnsupportedOperationMessage(opts.name, input);
|
|
31845
32104
|
}
|
|
31846
|
-
const eventId = result?.event_id ?? (0,
|
|
32105
|
+
const eventId = result?.event_id ?? (0, import_node_crypto2.randomBytes)(8).toString("hex");
|
|
31847
32106
|
const msg = {
|
|
31848
32107
|
eventId,
|
|
31849
32108
|
from: opts.name,
|
|
@@ -32505,7 +32764,7 @@ var AgentRelay = class {
|
|
|
32505
32764
|
if (result?.event_id === "unsupported_operation") {
|
|
32506
32765
|
return buildUnsupportedOperationMessage(name, input);
|
|
32507
32766
|
}
|
|
32508
|
-
const eventId = result?.event_id ?? (0,
|
|
32767
|
+
const eventId = result?.event_id ?? (0, import_node_crypto2.randomBytes)(8).toString("hex");
|
|
32509
32768
|
const msg = {
|
|
32510
32769
|
eventId,
|
|
32511
32770
|
from: name,
|
|
@@ -32622,7 +32881,7 @@ var AgentRelay = class {
|
|
|
32622
32881
|
};
|
|
32623
32882
|
|
|
32624
32883
|
// packages/sdk/dist/consensus.js
|
|
32625
|
-
var
|
|
32884
|
+
var import_node_crypto3 = require("node:crypto");
|
|
32626
32885
|
var import_node_events2 = require("node:events");
|
|
32627
32886
|
|
|
32628
32887
|
// packages/sdk/dist/consensus-helpers.js
|
|
@@ -32759,7 +33018,7 @@ var ConsensusEngine = class extends import_node_events2.EventEmitter {
|
|
|
32759
33018
|
}
|
|
32760
33019
|
// ── Proposal management ──────────────────────────────────────────────────
|
|
32761
33020
|
createProposal(options) {
|
|
32762
|
-
const id = `prop_${Date.now()}_${(0,
|
|
33021
|
+
const id = `prop_${Date.now()}_${(0, import_node_crypto3.randomBytes)(4).toString("hex")}`;
|
|
32763
33022
|
const now = Date.now();
|
|
32764
33023
|
const timeoutMs = options.timeoutMs ?? this.config.defaultTimeoutMs;
|
|
32765
33024
|
const proposal = {
|
|
@@ -33227,15 +33486,15 @@ var ShadowManager = class {
|
|
|
33227
33486
|
var WORKFLOW_BOOTSTRAP_TASK = "You are connected to Agent Relay. Do not reply to this message and wait for relay messages and respond using Relaycast MCP tools.";
|
|
33228
33487
|
var WORKFLOW_CONVENTIONS = [
|
|
33229
33488
|
"Messaging requirements:",
|
|
33230
|
-
'- When you receive `Relay message from <sender> ...`, reply using `
|
|
33489
|
+
'- When you receive `Relay message from <sender> ...`, reply using `mcp__relaycast__message_dm_send(to: "<sender>", text: "...")`.',
|
|
33231
33490
|
"- Send `ACK: ...` when you receive a task.",
|
|
33232
33491
|
"- Send `DONE: ...` when the task is complete.",
|
|
33233
|
-
"- Do not reply only in terminal text; send the response via
|
|
33234
|
-
"- Use
|
|
33492
|
+
"- Do not reply only in terminal text; send the response via mcp__relaycast__message_dm_send.",
|
|
33493
|
+
"- Use mcp__relaycast__message_inbox_check() and mcp__relaycast__agent_list() when context is missing."
|
|
33235
33494
|
].join("\n");
|
|
33236
33495
|
function hasWorkflowConventions(task) {
|
|
33237
33496
|
const lower = task.toLowerCase();
|
|
33238
|
-
return lower.includes("
|
|
33497
|
+
return lower.includes("mcp__relaycast__message_dm_send(") || lower.includes("relay_send(") || lower.includes("ack:") && lower.includes("done:");
|
|
33239
33498
|
}
|
|
33240
33499
|
function buildSpawnTask(task, includeWorkflowConventions) {
|
|
33241
33500
|
const normalized = typeof task === "string" ? task.trim() : "";
|
|
@@ -33427,8 +33686,8 @@ function isAgentStep(step) {
|
|
|
33427
33686
|
|
|
33428
33687
|
// packages/sdk/dist/workflows/runner.js
|
|
33429
33688
|
var import_node_child_process3 = require("node:child_process");
|
|
33430
|
-
var
|
|
33431
|
-
var
|
|
33689
|
+
var import_node_crypto5 = require("node:crypto");
|
|
33690
|
+
var import_node_fs5 = require("node:fs");
|
|
33432
33691
|
var import_promises3 = require("node:fs/promises");
|
|
33433
33692
|
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
33434
33693
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
@@ -33559,18 +33818,18 @@ async function spawnFromEnv(options = {}) {
|
|
|
33559
33818
|
}
|
|
33560
33819
|
|
|
33561
33820
|
// packages/sdk/dist/workflows/custom-steps.js
|
|
33562
|
-
var
|
|
33821
|
+
var import_node_fs3 = require("node:fs");
|
|
33563
33822
|
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
33564
33823
|
var import_yaml = __toESM(require_dist(), 1);
|
|
33565
33824
|
var CUSTOM_STEPS_FILE = ".relay/steps.yaml";
|
|
33566
33825
|
function loadCustomSteps(cwd) {
|
|
33567
33826
|
const stepsPath = import_node_path6.default.join(cwd, CUSTOM_STEPS_FILE);
|
|
33568
33827
|
const steps = /* @__PURE__ */ new Map();
|
|
33569
|
-
if (!(0,
|
|
33828
|
+
if (!(0, import_node_fs3.existsSync)(stepsPath)) {
|
|
33570
33829
|
return steps;
|
|
33571
33830
|
}
|
|
33572
33831
|
try {
|
|
33573
|
-
const content = (0,
|
|
33832
|
+
const content = (0, import_node_fs3.readFileSync)(stepsPath, "utf-8");
|
|
33574
33833
|
if (!content.trim()) {
|
|
33575
33834
|
return steps;
|
|
33576
33835
|
}
|
|
@@ -33820,7 +34079,7 @@ function resolveAllCustomSteps(steps, customSteps) {
|
|
|
33820
34079
|
return steps.map((step) => resolveCustomStep(step, customSteps));
|
|
33821
34080
|
}
|
|
33822
34081
|
function customStepsFileExists(cwd) {
|
|
33823
|
-
return (0,
|
|
34082
|
+
return (0, import_node_fs3.existsSync)(import_node_path6.default.join(cwd, CUSTOM_STEPS_FILE));
|
|
33824
34083
|
}
|
|
33825
34084
|
function getCustomStepsPath(cwd) {
|
|
33826
34085
|
return import_node_path6.default.join(cwd, CUSTOM_STEPS_FILE);
|
|
@@ -33857,8 +34116,8 @@ var InMemoryWorkflowDb = class {
|
|
|
33857
34116
|
};
|
|
33858
34117
|
|
|
33859
34118
|
// packages/sdk/dist/workflows/trajectory.js
|
|
33860
|
-
var
|
|
33861
|
-
var
|
|
34119
|
+
var import_node_crypto4 = require("node:crypto");
|
|
34120
|
+
var import_node_fs4 = require("node:fs");
|
|
33862
34121
|
var import_promises2 = require("node:fs/promises");
|
|
33863
34122
|
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
33864
34123
|
function classifyFailure(error48) {
|
|
@@ -33921,7 +34180,7 @@ var WorkflowTrajectory = class {
|
|
|
33921
34180
|
return;
|
|
33922
34181
|
this.startTime = Date.now();
|
|
33923
34182
|
this.swarmPattern = pattern ?? "dag";
|
|
33924
|
-
const id = `traj_${Date.now()}_${(0,
|
|
34183
|
+
const id = `traj_${Date.now()}_${(0, import_node_crypto4.randomBytes)(4).toString("hex")}`;
|
|
33925
34184
|
this.trajectory = {
|
|
33926
34185
|
id,
|
|
33927
34186
|
version: 1,
|
|
@@ -34264,7 +34523,7 @@ var WorkflowTrajectory = class {
|
|
|
34264
34523
|
if (!this.trajectory)
|
|
34265
34524
|
return;
|
|
34266
34525
|
const chapter = {
|
|
34267
|
-
id: `ch_${(0,
|
|
34526
|
+
id: `ch_${(0, import_node_crypto4.randomBytes)(4).toString("hex")}`,
|
|
34268
34527
|
title,
|
|
34269
34528
|
agentName,
|
|
34270
34529
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -34337,7 +34596,7 @@ var WorkflowTrajectory = class {
|
|
|
34337
34596
|
await (0, import_promises2.mkdir)(completedDir, { recursive: true });
|
|
34338
34597
|
const activePath = import_node_path7.default.join(activeDir, `${this.trajectory.id}.json`);
|
|
34339
34598
|
const completedPath = import_node_path7.default.join(completedDir, `${this.trajectory.id}.json`);
|
|
34340
|
-
if ((0,
|
|
34599
|
+
if ((0, import_node_fs4.existsSync)(activePath)) {
|
|
34341
34600
|
await (0, import_promises2.rename)(activePath, completedPath);
|
|
34342
34601
|
}
|
|
34343
34602
|
} catch {
|
|
@@ -34435,6 +34694,8 @@ var WorkflowRunner = class _WorkflowRunner {
|
|
|
34435
34694
|
stepSignalParticipants = /* @__PURE__ */ new Map();
|
|
34436
34695
|
/** Resolved named paths from the top-level `paths` config, keyed by name → absolute directory. */
|
|
34437
34696
|
resolvedPaths = /* @__PURE__ */ new Map();
|
|
34697
|
+
/** Tracks agent names currently assigned as reviewers (ref-counted to handle concurrent usage). */
|
|
34698
|
+
activeReviewers = /* @__PURE__ */ new Map();
|
|
34438
34699
|
constructor(options = {}) {
|
|
34439
34700
|
this.db = options.db ?? new InMemoryWorkflowDb();
|
|
34440
34701
|
this.workspaceId = options.workspaceId ?? "local";
|
|
@@ -34473,7 +34734,7 @@ var WorkflowRunner = class _WorkflowRunner {
|
|
|
34473
34734
|
const abs = import_node_path8.default.resolve(baseCwd, expanded);
|
|
34474
34735
|
resolved.set(pd.name, abs);
|
|
34475
34736
|
const isRequired = pd.required !== false;
|
|
34476
|
-
if (!(0,
|
|
34737
|
+
if (!(0, import_node_fs5.existsSync)(abs)) {
|
|
34477
34738
|
if (isRequired) {
|
|
34478
34739
|
errors.push(`Path "${pd.name}" resolves to "${abs}" which does not exist (required)`);
|
|
34479
34740
|
} else {
|
|
@@ -34807,12 +35068,12 @@ ${next}` : next;
|
|
|
34807
35068
|
}
|
|
34808
35069
|
captureFileSnapshot(root) {
|
|
34809
35070
|
const snapshot = /* @__PURE__ */ new Map();
|
|
34810
|
-
if (!(0,
|
|
35071
|
+
if (!(0, import_node_fs5.existsSync)(root))
|
|
34811
35072
|
return snapshot;
|
|
34812
35073
|
const visit = (currentPath) => {
|
|
34813
35074
|
let entries;
|
|
34814
35075
|
try {
|
|
34815
|
-
entries = (0,
|
|
35076
|
+
entries = (0, import_node_fs5.readdirSync)(currentPath, { withFileTypes: true });
|
|
34816
35077
|
} catch {
|
|
34817
35078
|
return;
|
|
34818
35079
|
}
|
|
@@ -34826,7 +35087,7 @@ ${next}` : next;
|
|
|
34826
35087
|
continue;
|
|
34827
35088
|
}
|
|
34828
35089
|
try {
|
|
34829
|
-
const stats = (0,
|
|
35090
|
+
const stats = (0, import_node_fs5.statSync)(fullPath);
|
|
34830
35091
|
if (!stats.isFile())
|
|
34831
35092
|
continue;
|
|
34832
35093
|
snapshot.set(fullPath, { mtimeMs: stats.mtimeMs, size: stats.size });
|
|
@@ -34835,7 +35096,7 @@ ${next}` : next;
|
|
|
34835
35096
|
}
|
|
34836
35097
|
};
|
|
34837
35098
|
try {
|
|
34838
|
-
const stats = (0,
|
|
35099
|
+
const stats = (0, import_node_fs5.statSync)(root);
|
|
34839
35100
|
if (stats.isFile()) {
|
|
34840
35101
|
snapshot.set(root, { mtimeMs: stats.mtimeMs, size: stats.size });
|
|
34841
35102
|
return snapshot;
|
|
@@ -34956,7 +35217,7 @@ ${next}` : next;
|
|
|
34956
35217
|
this.relayApiKey = envKey;
|
|
34957
35218
|
return;
|
|
34958
35219
|
}
|
|
34959
|
-
const workspaceName = `relay-${channel}-${(0,
|
|
35220
|
+
const workspaceName = `relay-${channel}-${(0, import_node_crypto5.randomBytes)(4).toString("hex")}`;
|
|
34960
35221
|
const baseUrl = this.relayOptions.env?.RELAYCAST_BASE_URL ?? process.env.RELAYCAST_BASE_URL ?? "https://api.relaycast.dev";
|
|
34961
35222
|
const res = await fetch(`${baseUrl}/v1/workspaces`, {
|
|
34962
35223
|
method: "POST",
|
|
@@ -35020,7 +35281,7 @@ ${next}` : next;
|
|
|
35020
35281
|
} catch (err) {
|
|
35021
35282
|
if (err instanceof RelayError && err.code === "name_conflict") {
|
|
35022
35283
|
registration = await rc.agents.register({
|
|
35023
|
-
name: `WorkflowRunner-${(0,
|
|
35284
|
+
name: `WorkflowRunner-${(0, import_node_crypto5.randomBytes)(4).toString("hex")}`,
|
|
35024
35285
|
type: "agent"
|
|
35025
35286
|
});
|
|
35026
35287
|
} else {
|
|
@@ -35237,14 +35498,14 @@ ${err.suggestion}`);
|
|
|
35237
35498
|
for (const agent of resolved.agents) {
|
|
35238
35499
|
if (agent.cwd) {
|
|
35239
35500
|
const resolvedCwd = import_node_path8.default.resolve(this.cwd, agent.cwd);
|
|
35240
|
-
if (!(0,
|
|
35501
|
+
if (!(0, import_node_fs5.existsSync)(resolvedCwd)) {
|
|
35241
35502
|
warnings.push(`Agent "${agent.name}" cwd "${agent.cwd}" resolves to "${resolvedCwd}" which does not exist`);
|
|
35242
35503
|
}
|
|
35243
35504
|
}
|
|
35244
35505
|
if (agent.additionalPaths) {
|
|
35245
35506
|
for (const ap of agent.additionalPaths) {
|
|
35246
35507
|
const resolvedPath = import_node_path8.default.resolve(this.cwd, ap);
|
|
35247
|
-
if (!(0,
|
|
35508
|
+
if (!(0, import_node_fs5.existsSync)(resolvedPath)) {
|
|
35248
35509
|
warnings.push(`Agent "${agent.name}" additionalPath "${ap}" resolves to "${resolvedPath}" which does not exist`);
|
|
35249
35510
|
}
|
|
35250
35511
|
}
|
|
@@ -35983,6 +36244,7 @@ ${err.suggestion}`);
|
|
|
35983
36244
|
this.lastActivity.clear();
|
|
35984
36245
|
this.supervisedRuntimeAgents.clear();
|
|
35985
36246
|
this.runtimeStepAgents.clear();
|
|
36247
|
+
this.activeReviewers.clear();
|
|
35986
36248
|
this.log("Shutting down broker...");
|
|
35987
36249
|
await this.relay?.shutdown();
|
|
35988
36250
|
this.relay = void 0;
|
|
@@ -36577,9 +36839,11 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
36577
36839
|
}
|
|
36578
36840
|
const specialistDef = _WorkflowRunner.resolveAgentDef(rawAgentDef);
|
|
36579
36841
|
const usesOwnerFlow = specialistDef.interactive !== false;
|
|
36580
|
-
const
|
|
36842
|
+
const currentPattern = this.currentConfig?.swarm?.pattern ?? "";
|
|
36843
|
+
const isHubPattern = _WorkflowRunner.HUB_PATTERNS.has(currentPattern);
|
|
36844
|
+
const usesAutoHardening = usesOwnerFlow && isHubPattern && !this.isExplicitInteractiveWorker(specialistDef);
|
|
36581
36845
|
const ownerDef = usesAutoHardening ? this.resolveAutoStepOwner(specialistDef, agentMap) : specialistDef;
|
|
36582
|
-
|
|
36846
|
+
let reviewDef;
|
|
36583
36847
|
const supervised = {
|
|
36584
36848
|
specialist: specialistDef,
|
|
36585
36849
|
owner: ownerDef,
|
|
@@ -36700,6 +36964,7 @@ ${resolvedTask}`;
|
|
|
36700
36964
|
const spawnResult = this.executor ? await this.executor.executeAgentStep(resolvedStep, effectiveOwner, ownerTask, timeoutMs) : await this.spawnAndWait(effectiveOwner, resolvedStep, timeoutMs, {
|
|
36701
36965
|
evidenceStepName: step.name,
|
|
36702
36966
|
evidenceRole: usesOwnerFlow ? "owner" : "specialist",
|
|
36967
|
+
preserveOnIdle: !isHubPattern || !this.isLeadLikeAgent(effectiveOwner) ? false : void 0,
|
|
36703
36968
|
logicalName: effectiveOwner.name,
|
|
36704
36969
|
onSpawned: explicitInteractiveWorker ? ({ agent }) => {
|
|
36705
36970
|
explicitWorkerHandle = agent;
|
|
@@ -36750,11 +37015,24 @@ ${resolvedTask}`;
|
|
|
36750
37015
|
if (completionReason === "retry_requested_by_owner") {
|
|
36751
37016
|
throw new WorkflowCompletionError(`Step "${step.name}" owner requested another attempt`, "retry_requested_by_owner");
|
|
36752
37017
|
}
|
|
37018
|
+
if (usesAutoHardening && usesDedicatedOwner && !reviewDef) {
|
|
37019
|
+
reviewDef = this.resolveAutoReviewAgent(ownerDef, agentMap);
|
|
37020
|
+
supervised.reviewer = reviewDef;
|
|
37021
|
+
}
|
|
36753
37022
|
let combinedOutput = specialistOutput;
|
|
36754
37023
|
if (usesOwnerFlow && reviewDef) {
|
|
36755
|
-
|
|
36756
|
-
|
|
36757
|
-
|
|
37024
|
+
this.activeReviewers.set(reviewDef.name, (this.activeReviewers.get(reviewDef.name) ?? 0) + 1);
|
|
37025
|
+
try {
|
|
37026
|
+
const remainingMs = timeoutMs ? Math.max(0, timeoutMs - ownerElapsed) : void 0;
|
|
37027
|
+
const reviewOutput = await this.runStepReviewGate(step, resolvedTask, specialistOutput, ownerOutput, ownerDef, reviewDef, remainingMs);
|
|
37028
|
+
combinedOutput = this.combineStepAndReviewOutput(specialistOutput, reviewOutput);
|
|
37029
|
+
} finally {
|
|
37030
|
+
const count = (this.activeReviewers.get(reviewDef.name) ?? 1) - 1;
|
|
37031
|
+
if (count <= 0)
|
|
37032
|
+
this.activeReviewers.delete(reviewDef.name);
|
|
37033
|
+
else
|
|
37034
|
+
this.activeReviewers.set(reviewDef.name, count);
|
|
37035
|
+
}
|
|
36758
37036
|
}
|
|
36759
37037
|
state.row.status = "completed";
|
|
36760
37038
|
state.row.output = combinedOutput;
|
|
@@ -37113,10 +37391,13 @@ WORKER COMPLETION CONTRACT:
|
|
|
37113
37391
|
return 2;
|
|
37114
37392
|
return isReviewer(def) ? 1 : 0;
|
|
37115
37393
|
};
|
|
37116
|
-
const
|
|
37394
|
+
const notBusy = (def) => !this.activeReviewers.has(def.name);
|
|
37395
|
+
const dedicatedCandidates = allDefs.filter((d) => eligible(d) && isReviewer(d)).sort((a, b) => reviewerPriority(b) - reviewerPriority(a) || a.name.localeCompare(b.name));
|
|
37396
|
+
const dedicated = dedicatedCandidates.find(notBusy) ?? dedicatedCandidates[0];
|
|
37117
37397
|
if (dedicated)
|
|
37118
37398
|
return dedicated;
|
|
37119
|
-
const
|
|
37399
|
+
const alternateCandidates = allDefs.filter((d) => eligible(d) && d.interactive !== false);
|
|
37400
|
+
const alternate = alternateCandidates.find(notBusy) ?? alternateCandidates[0];
|
|
37120
37401
|
if (alternate)
|
|
37121
37402
|
return alternate;
|
|
37122
37403
|
return ownerDef;
|
|
@@ -37610,7 +37891,7 @@ ${review}
|
|
|
37610
37891
|
buildPresetInjection(preset) {
|
|
37611
37892
|
switch (preset) {
|
|
37612
37893
|
case "worker":
|
|
37613
|
-
return "You are a non-interactive worker agent. Produce clean, structured output to stdout.\nDo NOT use mcp__relaycast__agent_add, add_agent, or any MCP tool to spawn sub-agents.\nDo NOT use
|
|
37894
|
+
return "You are a non-interactive worker agent. Produce clean, structured output to stdout.\nDo NOT use mcp__relaycast__agent_add, add_agent, or any MCP tool to spawn sub-agents.\nDo NOT use mcp__relaycast__message_dm_send or any Relaycast messaging tools \u2014 you have no relay connection.\n\n";
|
|
37614
37895
|
case "reviewer":
|
|
37615
37896
|
return "You are a non-interactive reviewer agent. Read the specified files/artifacts and produce a clear verdict.\nDo NOT spawn sub-agents or use any Relaycast messaging tools.\n\n";
|
|
37616
37897
|
case "analyst":
|
|
@@ -37650,7 +37931,7 @@ DO NOT:
|
|
|
37650
37931
|
const { cmd, args } = _WorkflowRunner.buildNonInteractiveCommand(agentDef.cli, taskWithDeliverable, modelArgs);
|
|
37651
37932
|
const logsDir = this.getWorkerLogsDir();
|
|
37652
37933
|
const logPath = import_node_path8.default.join(logsDir, `${agentName}.log`);
|
|
37653
|
-
const logStream = (0,
|
|
37934
|
+
const logStream = (0, import_node_fs5.createWriteStream)(logPath, { flags: "a" });
|
|
37654
37935
|
this.registerWorker(agentName, agentDef.cli, step.task ?? "", void 0, false);
|
|
37655
37936
|
let stopHeartbeat;
|
|
37656
37937
|
if (this.relayApiKey) {
|
|
@@ -37786,7 +38067,7 @@ DO NOT:
|
|
|
37786
38067
|
const taskWithExit = step.task + (relayRegistrationNote ? "\n\n" + relayRegistrationNote : "") + (delegationGuidance ? "\n\n" + delegationGuidance + "\n" : "") + '\n\n---\nIMPORTANT: When you have fully completed this task, you MUST self-terminate by either: (a) calling remove_agent(name: "<your-agent-name>", reason: "task completed") \u2014 preferred, or (b) outputting the exact text "/exit" on its own line as a fallback. Do not wait for further input \u2014 terminate immediately after finishing. Do NOT spawn sub-agents unless the task explicitly requires it.';
|
|
37787
38068
|
this.ptyOutputBuffers.set(agentName, []);
|
|
37788
38069
|
const logsDir = this.getWorkerLogsDir();
|
|
37789
|
-
const logStream = (0,
|
|
38070
|
+
const logStream = (0, import_node_fs5.createWriteStream)(import_node_path8.default.join(logsDir, `${agentName}.log`), { flags: "a" });
|
|
37790
38071
|
this.ptyLogStreams.set(agentName, logStream);
|
|
37791
38072
|
this.ptyListeners.set(agentName, (chunk) => {
|
|
37792
38073
|
const stripped = _WorkflowRunner.stripAnsi(chunk);
|
|
@@ -37828,11 +38109,11 @@ DO NOT:
|
|
|
37828
38109
|
oldLogStream.end();
|
|
37829
38110
|
this.ptyLogStreams.delete(oldName);
|
|
37830
38111
|
try {
|
|
37831
|
-
(0,
|
|
38112
|
+
(0, import_node_fs5.renameSync)(oldLogPath, newLogPath);
|
|
37832
38113
|
} catch {
|
|
37833
38114
|
}
|
|
37834
38115
|
}
|
|
37835
|
-
const newLogStream = (0,
|
|
38116
|
+
const newLogStream = (0, import_node_fs5.createWriteStream)(newLogPath, { flags: "a" });
|
|
37836
38117
|
this.ptyLogStreams.set(agent.name, newLogStream);
|
|
37837
38118
|
const oldListener = this.ptyListeners.get(oldName);
|
|
37838
38119
|
if (oldListener) {
|
|
@@ -37937,7 +38218,7 @@ DO NOT:
|
|
|
37937
38218
|
output = ptyChunks.join("");
|
|
37938
38219
|
} else {
|
|
37939
38220
|
const summaryPath = import_node_path8.default.join(this.summaryDir, `${step.name}.md`);
|
|
37940
|
-
output = (0,
|
|
38221
|
+
output = (0, import_node_fs5.existsSync)(summaryPath) ? await (0, import_promises3.readFile)(summaryPath, "utf-8") : exitResult === "timeout" ? "Agent completed (released after idle timeout)" : exitResult === "released" ? "Agent completed (idle \u2014 treated as done)" : `Agent exited (${exitResult})`;
|
|
37941
38222
|
}
|
|
37942
38223
|
if (ptyChunks.length === 0) {
|
|
37943
38224
|
this.captureStepTerminalEvidence(evidenceStepName, { stdout: output, combined: output }, { exitCode: agent?.exitCode, exitSignal: agent?.exitSignal }, {
|
|
@@ -38001,17 +38282,56 @@ DO NOT:
|
|
|
38001
38282
|
this.log(`[${step.name}] Supervising agent "${agent.name}" may idle while waiting \u2014 using exit-only completion`);
|
|
38002
38283
|
return agent.waitForExit(timeoutMs);
|
|
38003
38284
|
}
|
|
38004
|
-
const
|
|
38005
|
-
|
|
38006
|
-
|
|
38007
|
-
|
|
38008
|
-
|
|
38009
|
-
|
|
38010
|
-
|
|
38011
|
-
await
|
|
38012
|
-
|
|
38285
|
+
const idleLoopStart = Date.now();
|
|
38286
|
+
while (true) {
|
|
38287
|
+
const elapsed = Date.now() - idleLoopStart;
|
|
38288
|
+
const remaining = timeoutMs != null ? Math.max(0, timeoutMs - elapsed) : void 0;
|
|
38289
|
+
if (remaining != null && remaining <= 0) {
|
|
38290
|
+
return "timeout";
|
|
38291
|
+
}
|
|
38292
|
+
const result = await Promise.race([
|
|
38293
|
+
agent.waitForExit(remaining).then((r) => ({ kind: "exit", result: r })),
|
|
38294
|
+
agent.waitForIdle(remaining).then((r) => ({ kind: "idle", result: r }))
|
|
38295
|
+
]);
|
|
38296
|
+
if (result.kind === "idle" && result.result === "idle") {
|
|
38297
|
+
if (step.verification && step.verification.type === "output_contains") {
|
|
38298
|
+
const token = step.verification.value;
|
|
38299
|
+
const ptyOutput = (this.ptyOutputBuffers.get(agent.name) ?? []).join("");
|
|
38300
|
+
const taskText = step.task ?? "";
|
|
38301
|
+
const taskHasToken = taskText.includes(token);
|
|
38302
|
+
let verificationPassed = true;
|
|
38303
|
+
if (taskHasToken) {
|
|
38304
|
+
const first = ptyOutput.indexOf(token);
|
|
38305
|
+
verificationPassed = first !== -1 && ptyOutput.includes(token, first + token.length);
|
|
38306
|
+
} else {
|
|
38307
|
+
verificationPassed = ptyOutput.includes(token);
|
|
38308
|
+
}
|
|
38309
|
+
if (!verificationPassed) {
|
|
38310
|
+
this.log(`[${step.name}] Agent "${agent.name}" went idle but verification not yet passed \u2014 waiting for more output`);
|
|
38311
|
+
const idleGraceSecs = 15;
|
|
38312
|
+
const graceResult = await Promise.race([
|
|
38313
|
+
agent.waitForExit(idleGraceSecs * 1e3).then((r) => ({ kind: "exit", result: r })),
|
|
38314
|
+
agent.waitForIdle(idleGraceSecs * 1e3).then((r) => ({ kind: "idle", result: r }))
|
|
38315
|
+
]);
|
|
38316
|
+
if (graceResult.kind === "idle" && graceResult.result === "idle") {
|
|
38317
|
+
continue;
|
|
38318
|
+
}
|
|
38319
|
+
if (graceResult.kind === "exit") {
|
|
38320
|
+
return graceResult.result;
|
|
38321
|
+
}
|
|
38322
|
+
this.log(`[${step.name}] Agent "${agent.name}" still idle after ${idleGraceSecs}s grace \u2014 releasing`);
|
|
38323
|
+
this.postToChannel(`**[${step.name}]** Agent \`${agent.name}\` idle \u2014 releasing (verification pending)`);
|
|
38324
|
+
await agent.release();
|
|
38325
|
+
return "released";
|
|
38326
|
+
}
|
|
38327
|
+
}
|
|
38328
|
+
this.log(`[${step.name}] Agent "${agent.name}" went idle \u2014 treating as complete`);
|
|
38329
|
+
this.postToChannel(`**[${step.name}]** Agent \`${agent.name}\` idle \u2014 treating as complete`);
|
|
38330
|
+
await agent.release();
|
|
38331
|
+
return "released";
|
|
38332
|
+
}
|
|
38333
|
+
return result.result;
|
|
38013
38334
|
}
|
|
38014
|
-
return result.result;
|
|
38015
38335
|
}
|
|
38016
38336
|
const nudgeAfterMs = nudgeConfig.nudgeAfterMs ?? 12e4;
|
|
38017
38337
|
const escalateAfterMs = nudgeConfig.escalateAfterMs ?? 12e4;
|
|
@@ -38149,7 +38469,7 @@ DO NOT:
|
|
|
38149
38469
|
case "exit_code":
|
|
38150
38470
|
break;
|
|
38151
38471
|
case "file_exists":
|
|
38152
|
-
if (!(0,
|
|
38472
|
+
if (!(0, import_node_fs5.existsSync)(import_node_path8.default.resolve(this.cwd, check2.value))) {
|
|
38153
38473
|
return fail(`Verification failed for "${stepName}": file "${check2.value}" does not exist`);
|
|
38154
38474
|
}
|
|
38155
38475
|
break;
|
|
@@ -38303,7 +38623,7 @@ DO NOT:
|
|
|
38303
38623
|
RELAY SETUP \u2014 do this FIRST before any other relay tool:
|
|
38304
38624
|
1. Call: register(name="${agentName}")
|
|
38305
38625
|
This authenticates you in the Relaycast workspace.
|
|
38306
|
-
ALL relay tools (
|
|
38626
|
+
ALL relay tools (mcp__relaycast__message_dm_send, mcp__relaycast__message_inbox_check, mcp__relaycast__message_post, etc.) require
|
|
38307
38627
|
registration first \u2014 they will fail with "Not registered" otherwise.
|
|
38308
38628
|
2. Your agent name is "${agentName}" \u2014 use this exact name when registering.`;
|
|
38309
38629
|
}
|
|
@@ -38312,7 +38632,7 @@ RELAY SETUP \u2014 do this FIRST before any other relay tool:
|
|
|
38312
38632
|
|
|
38313
38633
|
` : "";
|
|
38314
38634
|
const subAgentOption = cli === "claude" ? "Option 2 \u2014 Use built-in sub-agents (Task tool) for research or scoped work:\n - Good for exploring code, reading files, or making targeted changes\n - Can run multiple sub-agents in parallel\n\n" : "";
|
|
38315
|
-
return "---\nAUTONOMOUS DELEGATION \u2014 READ THIS BEFORE STARTING:\n" + timeoutNote + 'Before diving in, assess whether this task is too large or complex for a single agent. If it involves multiple independent subtasks, touches many files, or could take a long time, you should break it down and delegate to helper agents to avoid timeouts.\n\nOption 1 \u2014 Spawn relay agents (for real parallel coding work):\n - mcp__relaycast__agent_add(name="helper-1", cli="claude", task="Specific subtask description")\n - Coordinate via
|
|
38635
|
+
return "---\nAUTONOMOUS DELEGATION \u2014 READ THIS BEFORE STARTING:\n" + timeoutNote + 'Before diving in, assess whether this task is too large or complex for a single agent. If it involves multiple independent subtasks, touches many files, or could take a long time, you should break it down and delegate to helper agents to avoid timeouts.\n\nOption 1 \u2014 Spawn relay agents (for real parallel coding work):\n - mcp__relaycast__agent_add(name="helper-1", cli="claude", task="Specific subtask description")\n - Coordinate via mcp__relaycast__message_dm_send(to="helper-1", text="...")\n - Check on them with mcp__relaycast__message_inbox_check()\n - Clean up when done: mcp__relaycast__agent_remove(name="helper-1")\n\n' + subAgentOption + `Guidelines:
|
|
38316
38636
|
- You are the lead \u2014 delegate but stay in control, track progress, integrate results
|
|
38317
38637
|
- Give each helper a clear, self-contained task with enough context to work independently
|
|
38318
38638
|
- For simple or quick work, just do it yourself \u2014 don't over-delegate
|
|
@@ -38488,10 +38808,10 @@ ${excerpt}` : "";
|
|
|
38488
38808
|
}
|
|
38489
38809
|
// ── ID generation ─────────────────────────────────────────────────────
|
|
38490
38810
|
generateId() {
|
|
38491
|
-
return (0,
|
|
38811
|
+
return (0, import_node_crypto5.randomBytes)(12).toString("hex");
|
|
38492
38812
|
}
|
|
38493
38813
|
generateShortId() {
|
|
38494
|
-
return (0,
|
|
38814
|
+
return (0, import_node_crypto5.randomBytes)(4).toString("hex");
|
|
38495
38815
|
}
|
|
38496
38816
|
/** Strip ANSI escape codes from terminal output — delegates to pty.ts canonical regex. */
|
|
38497
38817
|
static stripAnsi(text) {
|
|
@@ -38592,7 +38912,7 @@ ${excerpt}` : "";
|
|
|
38592
38912
|
const outputPath = import_node_path8.default.join(this.getStepOutputDir(runId), `${stepName}.md`);
|
|
38593
38913
|
try {
|
|
38594
38914
|
const dir = this.getStepOutputDir(runId);
|
|
38595
|
-
(0,
|
|
38915
|
+
(0, import_node_fs5.mkdirSync)(dir, { recursive: true });
|
|
38596
38916
|
const cleaned = _WorkflowRunner.stripAnsi(output);
|
|
38597
38917
|
await (0, import_promises3.writeFile)(outputPath, cleaned);
|
|
38598
38918
|
} catch {
|
|
@@ -38618,9 +38938,9 @@ ${preview}
|
|
|
38618
38938
|
loadStepOutput(runId, stepName) {
|
|
38619
38939
|
try {
|
|
38620
38940
|
const filePath = import_node_path8.default.join(this.getStepOutputDir(runId), `${stepName}.md`);
|
|
38621
|
-
if (!(0,
|
|
38941
|
+
if (!(0, import_node_fs5.existsSync)(filePath))
|
|
38622
38942
|
return void 0;
|
|
38623
|
-
return (0,
|
|
38943
|
+
return (0, import_node_fs5.readFileSync)(filePath, "utf-8");
|
|
38624
38944
|
} catch {
|
|
38625
38945
|
return void 0;
|
|
38626
38946
|
}
|
|
@@ -38628,7 +38948,7 @@ ${preview}
|
|
|
38628
38948
|
/** Get or create the worker logs directory (.agent-relay/team/worker-logs) */
|
|
38629
38949
|
getWorkerLogsDir() {
|
|
38630
38950
|
const logsDir = import_node_path8.default.join(this.cwd, ".agent-relay", "team", "worker-logs");
|
|
38631
|
-
(0,
|
|
38951
|
+
(0, import_node_fs5.mkdirSync)(logsDir, { recursive: true });
|
|
38632
38952
|
return logsDir;
|
|
38633
38953
|
}
|
|
38634
38954
|
/** Register a spawned agent in workers.json so `agents:kill` can find it. */
|
|
@@ -38644,7 +38964,7 @@ ${preview}
|
|
|
38644
38964
|
this.activeWorkers.set(agentName, workerEntry);
|
|
38645
38965
|
this.workersFileLock = this.workersFileLock.then(() => {
|
|
38646
38966
|
try {
|
|
38647
|
-
(0,
|
|
38967
|
+
(0, import_node_fs5.mkdirSync)(import_node_path8.default.dirname(this.workersPath), { recursive: true });
|
|
38648
38968
|
const existing = this.readWorkers().filter((w) => w.name !== agentName);
|
|
38649
38969
|
existing.push({ name: agentName, ...workerEntry });
|
|
38650
38970
|
this.writeWorkers(existing);
|
|
@@ -38666,21 +38986,21 @@ ${preview}
|
|
|
38666
38986
|
}
|
|
38667
38987
|
readWorkers() {
|
|
38668
38988
|
try {
|
|
38669
|
-
if (!(0,
|
|
38989
|
+
if (!(0, import_node_fs5.existsSync)(this.workersPath))
|
|
38670
38990
|
return [];
|
|
38671
|
-
const raw = JSON.parse((0,
|
|
38991
|
+
const raw = JSON.parse((0, import_node_fs5.readFileSync)(this.workersPath, "utf-8"));
|
|
38672
38992
|
return Array.isArray(raw?.workers) ? raw.workers : [];
|
|
38673
38993
|
} catch {
|
|
38674
38994
|
return [];
|
|
38675
38995
|
}
|
|
38676
38996
|
}
|
|
38677
38997
|
writeWorkers(workers) {
|
|
38678
|
-
(0,
|
|
38998
|
+
(0, import_node_fs5.writeFileSync)(this.workersPath, JSON.stringify({ workers }, null, 2));
|
|
38679
38999
|
}
|
|
38680
39000
|
};
|
|
38681
39001
|
|
|
38682
39002
|
// packages/sdk/dist/workflows/file-db.js
|
|
38683
|
-
var
|
|
39003
|
+
var import_node_fs6 = require("node:fs");
|
|
38684
39004
|
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
38685
39005
|
var JsonFileWorkflowDb = class {
|
|
38686
39006
|
filePath;
|
|
@@ -38690,7 +39010,7 @@ var JsonFileWorkflowDb = class {
|
|
|
38690
39010
|
this.filePath = filePath;
|
|
38691
39011
|
let writable = false;
|
|
38692
39012
|
try {
|
|
38693
|
-
(0,
|
|
39013
|
+
(0, import_node_fs6.mkdirSync)(import_node_path9.default.dirname(filePath), { recursive: true });
|
|
38694
39014
|
writable = true;
|
|
38695
39015
|
} catch {
|
|
38696
39016
|
}
|
|
@@ -38705,7 +39025,7 @@ var JsonFileWorkflowDb = class {
|
|
|
38705
39025
|
if (!this.writable)
|
|
38706
39026
|
return;
|
|
38707
39027
|
try {
|
|
38708
|
-
(0,
|
|
39028
|
+
(0, import_node_fs6.appendFileSync)(this.filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
38709
39029
|
} catch {
|
|
38710
39030
|
}
|
|
38711
39031
|
}
|
|
@@ -38715,7 +39035,7 @@ var JsonFileWorkflowDb = class {
|
|
|
38715
39035
|
const steps = /* @__PURE__ */ new Map();
|
|
38716
39036
|
let raw = "";
|
|
38717
39037
|
try {
|
|
38718
|
-
raw = (0,
|
|
39038
|
+
raw = (0, import_node_fs6.readFileSync)(this.filePath, "utf8");
|
|
38719
39039
|
} catch {
|
|
38720
39040
|
return { runs, steps };
|
|
38721
39041
|
}
|
|
@@ -38906,6 +39226,8 @@ var WorkflowBuilder = class {
|
|
|
38906
39226
|
def.task = options.task;
|
|
38907
39227
|
if (options.channels !== void 0)
|
|
38908
39228
|
def.channels = options.channels;
|
|
39229
|
+
if (options.preset !== void 0)
|
|
39230
|
+
def.preset = options.preset;
|
|
38909
39231
|
if (options.interactive !== void 0)
|
|
38910
39232
|
def.interactive = options.interactive;
|
|
38911
39233
|
if (options.model !== void 0 || options.maxTokens !== void 0 || options.timeoutMs !== void 0 || options.retries !== void 0 || options.idleThresholdSecs !== void 0) {
|
|
@@ -38924,21 +39246,29 @@ var WorkflowBuilder = class {
|
|
|
38924
39246
|
this._agents.push(def);
|
|
38925
39247
|
return this;
|
|
38926
39248
|
}
|
|
38927
|
-
/** Add a workflow step. */
|
|
39249
|
+
/** Add a workflow step (agent or deterministic). */
|
|
38928
39250
|
step(name, options) {
|
|
38929
|
-
const step = {
|
|
38930
|
-
|
|
38931
|
-
|
|
38932
|
-
|
|
38933
|
-
|
|
39251
|
+
const step = { name };
|
|
39252
|
+
if ("type" in options && options.type === "deterministic") {
|
|
39253
|
+
step.type = "deterministic";
|
|
39254
|
+
step.command = options.command;
|
|
39255
|
+
if (options.failOnError !== void 0)
|
|
39256
|
+
step.failOnError = options.failOnError;
|
|
39257
|
+
if (options.captureOutput !== void 0)
|
|
39258
|
+
step.captureOutput = options.captureOutput;
|
|
39259
|
+
} else {
|
|
39260
|
+
const agentOpts = options;
|
|
39261
|
+
step.agent = agentOpts.agent;
|
|
39262
|
+
step.task = agentOpts.task;
|
|
39263
|
+
if (agentOpts.verification !== void 0)
|
|
39264
|
+
step.verification = agentOpts.verification;
|
|
39265
|
+
if (agentOpts.retries !== void 0)
|
|
39266
|
+
step.retries = agentOpts.retries;
|
|
39267
|
+
}
|
|
38934
39268
|
if (options.dependsOn !== void 0)
|
|
38935
39269
|
step.dependsOn = options.dependsOn;
|
|
38936
|
-
if (options.verification !== void 0)
|
|
38937
|
-
step.verification = options.verification;
|
|
38938
39270
|
if (options.timeoutMs !== void 0)
|
|
38939
39271
|
step.timeoutMs = options.timeoutMs;
|
|
38940
|
-
if (options.retries !== void 0)
|
|
38941
|
-
step.retries = options.retries;
|
|
38942
39272
|
this._steps.push(step);
|
|
38943
39273
|
return this;
|
|
38944
39274
|
}
|
|
@@ -38955,8 +39285,9 @@ var WorkflowBuilder = class {
|
|
|
38955
39285
|
}
|
|
38956
39286
|
/** Build and return the RelayYamlConfig object. */
|
|
38957
39287
|
toConfig() {
|
|
38958
|
-
|
|
38959
|
-
|
|
39288
|
+
const hasAgentSteps = this._steps.some((s) => s.type !== "deterministic" && s.type !== "worktree");
|
|
39289
|
+
if (hasAgentSteps && this._agents.length === 0) {
|
|
39290
|
+
throw new Error("Workflow must have at least one agent when using agent steps");
|
|
38960
39291
|
}
|
|
38961
39292
|
if (this._steps.length === 0) {
|
|
38962
39293
|
throw new Error("Workflow must have at least one step");
|
|
@@ -39016,7 +39347,7 @@ function workflow(name) {
|
|
|
39016
39347
|
}
|
|
39017
39348
|
|
|
39018
39349
|
// packages/sdk/dist/workflows/coordinator.js
|
|
39019
|
-
var
|
|
39350
|
+
var import_node_crypto6 = require("node:crypto");
|
|
39020
39351
|
var import_node_events3 = require("node:events");
|
|
39021
39352
|
var PATTERN_HEURISTICS = [
|
|
39022
39353
|
// ── Dependency-based patterns (highest priority) ──────────────────────
|
|
@@ -39371,7 +39702,7 @@ var SwarmCoordinator = class extends import_node_events3.EventEmitter {
|
|
|
39371
39702
|
}
|
|
39372
39703
|
// ── Lifecycle: create run ───────────────────────────────────────────────
|
|
39373
39704
|
async createRun(workspaceId, config2) {
|
|
39374
|
-
const id = `run_${Date.now()}_${(0,
|
|
39705
|
+
const id = `run_${Date.now()}_${(0, import_node_crypto6.randomBytes)(4).toString("hex")}`;
|
|
39375
39706
|
const pattern = this.selectPattern(config2);
|
|
39376
39707
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
39377
39708
|
const { rows } = await this.db.query(`INSERT INTO workflow_runs (id, workspace_id, workflow_name, pattern, status, config, started_at, created_at, updated_at)
|
|
@@ -39410,7 +39741,7 @@ var SwarmCoordinator = class extends import_node_events3.EventEmitter {
|
|
|
39410
39741
|
const created = [];
|
|
39411
39742
|
for (const wf of workflows) {
|
|
39412
39743
|
for (const step of wf.steps) {
|
|
39413
|
-
const id = `step_${Date.now()}_${(0,
|
|
39744
|
+
const id = `step_${Date.now()}_${(0, import_node_crypto6.randomBytes)(4).toString("hex")}`;
|
|
39414
39745
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
39415
39746
|
const { rows } = await this.db.query(`INSERT INTO workflow_steps (id, run_id, step_name, agent_name, status, task, depends_on, created_at, updated_at)
|
|
39416
39747
|
VALUES ($1, $2, $3, $4, 'pending', $5, $6, $7, $7)
|
|
@@ -39587,7 +39918,7 @@ var SwarmCoordinator = class extends import_node_events3.EventEmitter {
|
|
|
39587
39918
|
};
|
|
39588
39919
|
|
|
39589
39920
|
// packages/sdk/dist/workflows/barrier.js
|
|
39590
|
-
var
|
|
39921
|
+
var import_node_crypto7 = require("node:crypto");
|
|
39591
39922
|
var import_node_events4 = require("node:events");
|
|
39592
39923
|
var BarrierManager = class extends import_node_events4.EventEmitter {
|
|
39593
39924
|
db;
|
|
@@ -39603,7 +39934,7 @@ var BarrierManager = class extends import_node_events4.EventEmitter {
|
|
|
39603
39934
|
* Create a barrier for a workflow run.
|
|
39604
39935
|
*/
|
|
39605
39936
|
async createBarrier(runId, definition) {
|
|
39606
|
-
const id = `bar_${Date.now()}_${(0,
|
|
39937
|
+
const id = `bar_${Date.now()}_${(0, import_node_crypto7.randomBytes)(4).toString("hex")}`;
|
|
39607
39938
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
39608
39939
|
const mode = definition.mode ?? "all";
|
|
39609
39940
|
const { rows } = await this.db.query(`INSERT INTO workflow_barriers (id, run_id, barrier_name, wait_for, resolved, is_satisfied, timeout_ms, created_at, updated_at)
|
|
@@ -39737,7 +40068,7 @@ var BarrierManager = class extends import_node_events4.EventEmitter {
|
|
|
39737
40068
|
};
|
|
39738
40069
|
|
|
39739
40070
|
// packages/sdk/dist/workflows/state.js
|
|
39740
|
-
var
|
|
40071
|
+
var import_node_crypto8 = require("node:crypto");
|
|
39741
40072
|
var import_node_events5 = require("node:events");
|
|
39742
40073
|
var StateStore = class extends import_node_events5.EventEmitter {
|
|
39743
40074
|
db;
|
|
@@ -39780,7 +40111,7 @@ var StateStore = class extends import_node_events5.EventEmitter {
|
|
|
39780
40111
|
const namespace = options.namespace ?? this.defaultNamespace;
|
|
39781
40112
|
const ttlMs = options.ttlMs ?? this.defaultTtlMs;
|
|
39782
40113
|
const expiresAt = ttlMs ? new Date(Date.now() + ttlMs).toISOString() : null;
|
|
39783
|
-
const id = `st_${Date.now()}_${(0,
|
|
40114
|
+
const id = `st_${Date.now()}_${(0, import_node_crypto8.randomBytes)(4).toString("hex")}`;
|
|
39784
40115
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
39785
40116
|
const { rows } = await this.db.query(`INSERT INTO swarm_state (id, run_id, namespace, key, value, expires_at, created_at, updated_at)
|
|
39786
40117
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $7)
|
|
@@ -39868,7 +40199,7 @@ var StateStore = class extends import_node_events5.EventEmitter {
|
|
|
39868
40199
|
};
|
|
39869
40200
|
|
|
39870
40201
|
// packages/sdk/dist/workflows/templates.js
|
|
39871
|
-
var
|
|
40202
|
+
var import_node_fs7 = require("node:fs");
|
|
39872
40203
|
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
39873
40204
|
var import_node_url2 = require("node:url");
|
|
39874
40205
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
@@ -39972,9 +40303,9 @@ var TemplateRegistry = class {
|
|
|
39972
40303
|
throw new Error(`Invalid template name: "${templateName}" contains path separators or traversal sequences`);
|
|
39973
40304
|
}
|
|
39974
40305
|
this.validateRelayConfig(parsed, url2);
|
|
39975
|
-
await
|
|
40306
|
+
await import_node_fs7.promises.mkdir(this.customTemplatesDir, { recursive: true });
|
|
39976
40307
|
const targetPath = import_node_path10.default.join(this.customTemplatesDir, `${templateName}.yaml`);
|
|
39977
|
-
await
|
|
40308
|
+
await import_node_fs7.promises.writeFile(targetPath, (0, import_yaml4.stringify)(parsed), "utf-8");
|
|
39978
40309
|
return targetPath;
|
|
39979
40310
|
}
|
|
39980
40311
|
isTemplateShorthand(input) {
|
|
@@ -40003,7 +40334,7 @@ var TemplateRegistry = class {
|
|
|
40003
40334
|
import_node_path10.default.resolve(currentDir, "../workflows/builtin-templates")
|
|
40004
40335
|
];
|
|
40005
40336
|
for (const candidate of candidates) {
|
|
40006
|
-
if ((0,
|
|
40337
|
+
if ((0, import_node_fs7.existsSync)(candidate)) {
|
|
40007
40338
|
return candidate;
|
|
40008
40339
|
}
|
|
40009
40340
|
}
|
|
@@ -40025,7 +40356,7 @@ var TemplateRegistry = class {
|
|
|
40025
40356
|
for (const ext of YAML_EXTENSIONS) {
|
|
40026
40357
|
const candidate = import_node_path10.default.join(directory, `${templateName}${ext}`);
|
|
40027
40358
|
try {
|
|
40028
|
-
const stat2 = await
|
|
40359
|
+
const stat2 = await import_node_fs7.promises.stat(candidate);
|
|
40029
40360
|
if (stat2.isFile()) {
|
|
40030
40361
|
return candidate;
|
|
40031
40362
|
}
|
|
@@ -40035,7 +40366,7 @@ var TemplateRegistry = class {
|
|
|
40035
40366
|
return void 0;
|
|
40036
40367
|
}
|
|
40037
40368
|
async readTemplateFile(templatePath) {
|
|
40038
|
-
const raw = await
|
|
40369
|
+
const raw = await import_node_fs7.promises.readFile(templatePath, "utf-8");
|
|
40039
40370
|
const parsed = (0, import_yaml4.parse)(raw);
|
|
40040
40371
|
if (!isRecord(parsed)) {
|
|
40041
40372
|
throw new Error(`Template at ${templatePath} is not a YAML object`);
|
|
@@ -40228,7 +40559,7 @@ var TemplateRegistry = class {
|
|
|
40228
40559
|
}
|
|
40229
40560
|
async safeReadDir(directory) {
|
|
40230
40561
|
try {
|
|
40231
|
-
return await
|
|
40562
|
+
return await import_node_fs7.promises.readdir(directory);
|
|
40232
40563
|
} catch {
|
|
40233
40564
|
return [];
|
|
40234
40565
|
}
|
|
@@ -40392,7 +40723,7 @@ function isValidAgentName(name) {
|
|
|
40392
40723
|
}
|
|
40393
40724
|
|
|
40394
40725
|
// packages/utils/dist/logger.js
|
|
40395
|
-
var
|
|
40726
|
+
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
40396
40727
|
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
40397
40728
|
function getLogFile() {
|
|
40398
40729
|
return process.env.AGENT_RELAY_LOG_FILE;
|
|
@@ -40412,8 +40743,8 @@ var LEVEL_PRIORITY = {
|
|
|
40412
40743
|
var createdLogDirs = /* @__PURE__ */ new Set();
|
|
40413
40744
|
function ensureLogDir(logFile) {
|
|
40414
40745
|
const logDir = import_node_path11.default.dirname(logFile);
|
|
40415
|
-
if (!createdLogDirs.has(logDir) && !
|
|
40416
|
-
|
|
40746
|
+
if (!createdLogDirs.has(logDir) && !import_node_fs8.default.existsSync(logDir)) {
|
|
40747
|
+
import_node_fs8.default.mkdirSync(logDir, { recursive: true });
|
|
40417
40748
|
createdLogDirs.add(logDir);
|
|
40418
40749
|
}
|
|
40419
40750
|
}
|
|
@@ -40442,7 +40773,7 @@ function log(level, component, msg, extra) {
|
|
|
40442
40773
|
const logFile = getLogFile();
|
|
40443
40774
|
if (logFile) {
|
|
40444
40775
|
ensureLogDir(logFile);
|
|
40445
|
-
|
|
40776
|
+
import_node_fs8.default.appendFileSync(logFile, formatted + "\n");
|
|
40446
40777
|
return;
|
|
40447
40778
|
}
|
|
40448
40779
|
if (level === "ERROR" || level === "WARN") {
|
|
@@ -40713,7 +41044,7 @@ function benchmarkPatterns(iterations = 1e4) {
|
|
|
40713
41044
|
|
|
40714
41045
|
// packages/utils/dist/command-resolver.js
|
|
40715
41046
|
var import_node_child_process4 = require("node:child_process");
|
|
40716
|
-
var
|
|
41047
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
40717
41048
|
function resolveCommand(command) {
|
|
40718
41049
|
if (command.startsWith("/")) {
|
|
40719
41050
|
return resolveSymlinks(command);
|
|
@@ -40739,7 +41070,7 @@ function resolveCommand(command) {
|
|
|
40739
41070
|
}
|
|
40740
41071
|
function resolveSymlinks(filePath) {
|
|
40741
41072
|
try {
|
|
40742
|
-
const resolved =
|
|
41073
|
+
const resolved = import_node_fs9.default.realpathSync(filePath);
|
|
40743
41074
|
if (resolved !== filePath && process.env.DEBUG_SPAWN === "1") {
|
|
40744
41075
|
console.log(`[command-resolver] Resolved symlink: ${filePath} -> ${resolved}`);
|
|
40745
41076
|
}
|
|
@@ -40764,7 +41095,7 @@ function commandExists(command) {
|
|
|
40764
41095
|
}
|
|
40765
41096
|
|
|
40766
41097
|
// packages/utils/dist/git-remote.js
|
|
40767
|
-
var
|
|
41098
|
+
var fs6 = __toESM(require("node:fs"), 1);
|
|
40768
41099
|
var path11 = __toESM(require("node:path"), 1);
|
|
40769
41100
|
var import_node_child_process5 = require("node:child_process");
|
|
40770
41101
|
function parseGitRemoteUrl(url2) {
|
|
@@ -40783,7 +41114,7 @@ function parseGitRemoteUrl(url2) {
|
|
|
40783
41114
|
function getGitRemoteUrl(workingDirectory, remoteName = "origin") {
|
|
40784
41115
|
try {
|
|
40785
41116
|
const gitDir = path11.join(workingDirectory, ".git");
|
|
40786
|
-
if (!
|
|
41117
|
+
if (!fs6.existsSync(gitDir)) {
|
|
40787
41118
|
return null;
|
|
40788
41119
|
}
|
|
40789
41120
|
const result = (0, import_node_child_process5.execSync)(`git remote get-url ${remoteName}`, {
|
|
@@ -40796,10 +41127,10 @@ function getGitRemoteUrl(workingDirectory, remoteName = "origin") {
|
|
|
40796
41127
|
} catch {
|
|
40797
41128
|
try {
|
|
40798
41129
|
const configPath = path11.join(workingDirectory, ".git", "config");
|
|
40799
|
-
if (!
|
|
41130
|
+
if (!fs6.existsSync(configPath)) {
|
|
40800
41131
|
return null;
|
|
40801
41132
|
}
|
|
40802
|
-
const config2 =
|
|
41133
|
+
const config2 = fs6.readFileSync(configPath, "utf-8");
|
|
40803
41134
|
const remoteSection = new RegExp(`\\[remote\\s+"${remoteName}"\\][^\\[]*url\\s*=\\s*([^\\n]+)`, "i");
|
|
40804
41135
|
const match = config2.match(remoteSection);
|
|
40805
41136
|
return match?.[1]?.trim() || null;
|
|
@@ -40819,7 +41150,7 @@ function findGitRoot(startPath) {
|
|
|
40819
41150
|
let currentPath = path11.resolve(startPath);
|
|
40820
41151
|
const root = path11.parse(currentPath).root;
|
|
40821
41152
|
while (currentPath !== root) {
|
|
40822
|
-
if (
|
|
41153
|
+
if (fs6.existsSync(path11.join(currentPath, ".git"))) {
|
|
40823
41154
|
return currentPath;
|
|
40824
41155
|
}
|
|
40825
41156
|
currentPath = path11.dirname(currentPath);
|
|
@@ -40839,7 +41170,7 @@ function getRepoFullNameFromPath(workingDirectory) {
|
|
|
40839
41170
|
}
|
|
40840
41171
|
|
|
40841
41172
|
// packages/utils/dist/update-checker.js
|
|
40842
|
-
var
|
|
41173
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
40843
41174
|
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
40844
41175
|
var import_node_https = __toESM(require("node:https"), 1);
|
|
40845
41176
|
var import_node_os4 = __toESM(require("node:os"), 1);
|
|
@@ -40854,9 +41185,9 @@ function getCachePath() {
|
|
|
40854
41185
|
function readCache() {
|
|
40855
41186
|
try {
|
|
40856
41187
|
const cachePath = getCachePath();
|
|
40857
|
-
if (!
|
|
41188
|
+
if (!import_node_fs10.default.existsSync(cachePath))
|
|
40858
41189
|
return null;
|
|
40859
|
-
const data =
|
|
41190
|
+
const data = import_node_fs10.default.readFileSync(cachePath, "utf-8");
|
|
40860
41191
|
return JSON.parse(data);
|
|
40861
41192
|
} catch {
|
|
40862
41193
|
return null;
|
|
@@ -40866,10 +41197,10 @@ function writeCache(cache) {
|
|
|
40866
41197
|
try {
|
|
40867
41198
|
const cachePath = getCachePath();
|
|
40868
41199
|
const cacheDir = import_node_path12.default.dirname(cachePath);
|
|
40869
|
-
if (!
|
|
40870
|
-
|
|
41200
|
+
if (!import_node_fs10.default.existsSync(cacheDir)) {
|
|
41201
|
+
import_node_fs10.default.mkdirSync(cacheDir, { recursive: true });
|
|
40871
41202
|
}
|
|
40872
|
-
|
|
41203
|
+
import_node_fs10.default.writeFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
40873
41204
|
} catch {
|
|
40874
41205
|
}
|
|
40875
41206
|
}
|
|
@@ -40987,10 +41318,10 @@ function checkForUpdatesInBackground(currentVersion) {
|
|
|
40987
41318
|
}
|
|
40988
41319
|
|
|
40989
41320
|
// packages/utils/dist/error-tracking.js
|
|
40990
|
-
var
|
|
41321
|
+
var import_node_crypto9 = require("node:crypto");
|
|
40991
41322
|
function generateErrorId() {
|
|
40992
41323
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
40993
|
-
const random = (0,
|
|
41324
|
+
const random = (0, import_node_crypto9.randomBytes)(2).toString("hex");
|
|
40994
41325
|
return `ERR-${timestamp}-${random}`;
|
|
40995
41326
|
}
|
|
40996
41327
|
function createTraceableError(message, context = {}, originalError) {
|
|
@@ -41148,7 +41479,7 @@ function validateModelForCli(cli, model) {
|
|
|
41148
41479
|
}
|
|
41149
41480
|
|
|
41150
41481
|
// packages/utils/dist/relay-pty-path.js
|
|
41151
|
-
var
|
|
41482
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
41152
41483
|
var import_node_os5 = __toESM(require("node:os"), 1);
|
|
41153
41484
|
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
41154
41485
|
var SUPPORTED_PLATFORMS = {
|
|
@@ -41209,12 +41540,12 @@ function findRelayPtyBinary(callerDirname) {
|
|
|
41209
41540
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
41210
41541
|
if (home) {
|
|
41211
41542
|
const npxCacheBase = import_node_path13.default.join(home, ".npm", "_npx");
|
|
41212
|
-
if (
|
|
41543
|
+
if (import_node_fs11.default.existsSync(npxCacheBase)) {
|
|
41213
41544
|
try {
|
|
41214
|
-
const entries =
|
|
41545
|
+
const entries = import_node_fs11.default.readdirSync(npxCacheBase);
|
|
41215
41546
|
for (const entry of entries) {
|
|
41216
41547
|
const npxPackage = import_node_path13.default.join(npxCacheBase, entry, "node_modules", "agent-relay");
|
|
41217
|
-
if (
|
|
41548
|
+
if (import_node_fs11.default.existsSync(npxPackage)) {
|
|
41218
41549
|
packageRoots.push(npxPackage);
|
|
41219
41550
|
}
|
|
41220
41551
|
}
|
|
@@ -41272,7 +41603,7 @@ function findRelayPtyBinary(callerDirname) {
|
|
|
41272
41603
|
}
|
|
41273
41604
|
function isExecutable(filePath) {
|
|
41274
41605
|
try {
|
|
41275
|
-
|
|
41606
|
+
import_node_fs11.default.accessSync(filePath, import_node_fs11.default.constants.X_OK);
|
|
41276
41607
|
return true;
|
|
41277
41608
|
} catch {
|
|
41278
41609
|
return false;
|
|
@@ -41281,9 +41612,9 @@ function isExecutable(filePath) {
|
|
|
41281
41612
|
function isPlatformCompatibleBinary(filePath) {
|
|
41282
41613
|
let fd;
|
|
41283
41614
|
try {
|
|
41284
|
-
fd =
|
|
41615
|
+
fd = import_node_fs11.default.openSync(filePath, "r");
|
|
41285
41616
|
const header = Buffer.alloc(4);
|
|
41286
|
-
const bytesRead =
|
|
41617
|
+
const bytesRead = import_node_fs11.default.readSync(fd, header, 0, 4, 0);
|
|
41287
41618
|
if (bytesRead < 4) {
|
|
41288
41619
|
return false;
|
|
41289
41620
|
}
|
|
@@ -41301,7 +41632,7 @@ function isPlatformCompatibleBinary(filePath) {
|
|
|
41301
41632
|
} finally {
|
|
41302
41633
|
if (fd !== void 0) {
|
|
41303
41634
|
try {
|
|
41304
|
-
|
|
41635
|
+
import_node_fs11.default.closeSync(fd);
|
|
41305
41636
|
} catch {
|
|
41306
41637
|
}
|
|
41307
41638
|
}
|
|
@@ -41332,7 +41663,7 @@ function clearBinaryCache() {
|
|
|
41332
41663
|
|
|
41333
41664
|
// packages/utils/dist/client-helpers.js
|
|
41334
41665
|
var import_node_net = require("node:net");
|
|
41335
|
-
var
|
|
41666
|
+
var import_node_crypto10 = require("node:crypto");
|
|
41336
41667
|
|
|
41337
41668
|
// packages/utils/dist/legacy-protocol.js
|
|
41338
41669
|
var PROTOCOL_VERSION2 = 1;
|
|
@@ -41459,14 +41790,14 @@ function createRequestHandler(socketPath, envelope, options) {
|
|
|
41459
41790
|
});
|
|
41460
41791
|
}
|
|
41461
41792
|
function generateRequestId(prefix = "") {
|
|
41462
|
-
return `${prefix}${Date.now().toString(36)}-${(0,
|
|
41793
|
+
return `${prefix}${Date.now().toString(36)}-${(0, import_node_crypto10.randomUUID)().slice(0, 8)}`;
|
|
41463
41794
|
}
|
|
41464
41795
|
|
|
41465
41796
|
// packages/hooks/dist/types.js
|
|
41466
41797
|
var HOOK_ABI_VERSION = PROTOCOL_VERSION;
|
|
41467
41798
|
|
|
41468
41799
|
// packages/hooks/dist/registry.js
|
|
41469
|
-
var
|
|
41800
|
+
var import_node_crypto12 = require("node:crypto");
|
|
41470
41801
|
var InMemoryHookMemory = class {
|
|
41471
41802
|
store = /* @__PURE__ */ new Map();
|
|
41472
41803
|
get(key) {
|
|
@@ -41515,8 +41846,8 @@ var HookRegistry = class {
|
|
|
41515
41846
|
injectFn;
|
|
41516
41847
|
sendFn;
|
|
41517
41848
|
constructor(options = {}) {
|
|
41518
|
-
this.sessionId = (0,
|
|
41519
|
-
this.agentId = options.agentId ?? (0,
|
|
41849
|
+
this.sessionId = (0, import_node_crypto12.randomUUID)();
|
|
41850
|
+
this.agentId = options.agentId ?? (0, import_node_crypto12.randomUUID)();
|
|
41520
41851
|
this.agentName = options.agentName ?? "agent";
|
|
41521
41852
|
this.workingDir = options.workingDir ?? process.cwd();
|
|
41522
41853
|
this.projectId = options.projectId ?? "default";
|
|
@@ -41920,9 +42251,9 @@ var HookRegistry = class {
|
|
|
41920
42251
|
var import_node_child_process6 = require("node:child_process");
|
|
41921
42252
|
|
|
41922
42253
|
// packages/config/dist/project-namespace.js
|
|
41923
|
-
var
|
|
42254
|
+
var import_node_crypto13 = __toESM(require("node:crypto"), 1);
|
|
41924
42255
|
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
41925
|
-
var
|
|
42256
|
+
var import_node_fs12 = __toESM(require("node:fs"), 1);
|
|
41926
42257
|
var import_node_os6 = __toESM(require("node:os"), 1);
|
|
41927
42258
|
function getGlobalBaseDir2() {
|
|
41928
42259
|
if (process.env.AGENT_RELAY_DATA_DIR) {
|
|
@@ -41935,13 +42266,13 @@ function getGlobalBaseDir2() {
|
|
|
41935
42266
|
return import_node_path14.default.join(import_node_os6.default.homedir(), ".agent-relay");
|
|
41936
42267
|
}
|
|
41937
42268
|
var GLOBAL_BASE_DIR2 = getGlobalBaseDir2();
|
|
41938
|
-
var
|
|
41939
|
-
function
|
|
42269
|
+
var PROJECT_DATA_DIR2 = ".agent-relay";
|
|
42270
|
+
function hashPath2(projectPath) {
|
|
41940
42271
|
const normalized = import_node_path14.default.resolve(projectPath);
|
|
41941
|
-
const hash2 =
|
|
42272
|
+
const hash2 = import_node_crypto13.default.createHash("sha256").update(normalized).digest("hex");
|
|
41942
42273
|
return hash2.substring(0, 12);
|
|
41943
42274
|
}
|
|
41944
|
-
function
|
|
42275
|
+
function findProjectRoot2(startDir = process.cwd()) {
|
|
41945
42276
|
if (process.env.AGENT_RELAY_PROJECT) {
|
|
41946
42277
|
return import_node_path14.default.resolve(process.env.AGENT_RELAY_PROJECT);
|
|
41947
42278
|
}
|
|
@@ -41950,7 +42281,7 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
41950
42281
|
const markers = [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".agent-relay"];
|
|
41951
42282
|
while (current !== root) {
|
|
41952
42283
|
for (const marker of markers) {
|
|
41953
|
-
if (
|
|
42284
|
+
if (import_node_fs12.default.existsSync(import_node_path14.default.join(current, marker))) {
|
|
41954
42285
|
return current;
|
|
41955
42286
|
}
|
|
41956
42287
|
}
|
|
@@ -41959,9 +42290,9 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
41959
42290
|
return import_node_path14.default.resolve(startDir);
|
|
41960
42291
|
}
|
|
41961
42292
|
function getProjectPaths2(projectRoot) {
|
|
41962
|
-
const root = projectRoot ??
|
|
41963
|
-
const projectId =
|
|
41964
|
-
const dataDir = import_node_path14.default.join(root,
|
|
42293
|
+
const root = projectRoot ?? findProjectRoot2();
|
|
42294
|
+
const projectId = hashPath2(root);
|
|
42295
|
+
const dataDir = import_node_path14.default.join(root, PROJECT_DATA_DIR2);
|
|
41965
42296
|
return {
|
|
41966
42297
|
dataDir,
|
|
41967
42298
|
teamDir: import_node_path14.default.join(dataDir, "team"),
|
|
@@ -41973,10 +42304,10 @@ function getProjectPaths2(projectRoot) {
|
|
|
41973
42304
|
}
|
|
41974
42305
|
|
|
41975
42306
|
// packages/config/dist/trajectory-config.js
|
|
41976
|
-
var
|
|
42307
|
+
var import_node_fs13 = require("node:fs");
|
|
41977
42308
|
var import_node_path15 = require("node:path");
|
|
41978
42309
|
var import_node_os7 = require("node:os");
|
|
41979
|
-
var
|
|
42310
|
+
var import_node_crypto14 = require("node:crypto");
|
|
41980
42311
|
function getAgentRelayConfigDir() {
|
|
41981
42312
|
return process.env.AGENT_RELAY_CONFIG_DIR ?? (0, import_node_path15.join)((0, import_node_os7.homedir)(), ".config", "agent-relay");
|
|
41982
42313
|
}
|
|
@@ -41988,7 +42319,7 @@ function readRelayConfig(projectRoot) {
|
|
|
41988
42319
|
const configPath = getRelayConfigPath(projectRoot);
|
|
41989
42320
|
if (configCache && configCache.path === configPath) {
|
|
41990
42321
|
try {
|
|
41991
|
-
const stat2 = (0,
|
|
42322
|
+
const stat2 = (0, import_node_fs13.statSync)(configPath);
|
|
41992
42323
|
if (stat2.mtimeMs === configCache.mtime) {
|
|
41993
42324
|
return configCache.config;
|
|
41994
42325
|
}
|
|
@@ -41996,13 +42327,13 @@ function readRelayConfig(projectRoot) {
|
|
|
41996
42327
|
}
|
|
41997
42328
|
}
|
|
41998
42329
|
try {
|
|
41999
|
-
if (!(0,
|
|
42330
|
+
if (!(0, import_node_fs13.existsSync)(configPath)) {
|
|
42000
42331
|
return {};
|
|
42001
42332
|
}
|
|
42002
|
-
const content = (0,
|
|
42333
|
+
const content = (0, import_node_fs13.readFileSync)(configPath, "utf-8");
|
|
42003
42334
|
const config2 = JSON.parse(content);
|
|
42004
42335
|
try {
|
|
42005
|
-
const stat2 = (0,
|
|
42336
|
+
const stat2 = (0, import_node_fs13.statSync)(configPath);
|
|
42006
42337
|
configCache = { path: configPath, config: config2, mtime: stat2.mtimeMs };
|
|
42007
42338
|
} catch {
|
|
42008
42339
|
}
|
|
@@ -42018,7 +42349,7 @@ function shouldStoreInRepo(projectRoot) {
|
|
|
42018
42349
|
}
|
|
42019
42350
|
function getProjectHash(projectRoot) {
|
|
42020
42351
|
const root = projectRoot ?? getProjectPaths2().projectRoot;
|
|
42021
|
-
return (0,
|
|
42352
|
+
return (0, import_node_crypto14.createHash)("sha256").update(root).digest("hex").slice(0, 16);
|
|
42022
42353
|
}
|
|
42023
42354
|
function getUserTrajectoriesDir(projectRoot) {
|
|
42024
42355
|
const projectHash = getProjectHash(projectRoot);
|
|
@@ -42667,7 +42998,7 @@ var HookEmitter = class {
|
|
|
42667
42998
|
};
|
|
42668
42999
|
|
|
42669
43000
|
// packages/hooks/dist/inbox-check/utils.js
|
|
42670
|
-
var
|
|
43001
|
+
var import_node_fs14 = require("node:fs");
|
|
42671
43002
|
var import_node_path16 = require("node:path");
|
|
42672
43003
|
var DEFAULT_INBOX_DIR = "/tmp/agent-relay";
|
|
42673
43004
|
function getAgentName() {
|
|
@@ -42681,13 +43012,13 @@ function getInboxPath(config2) {
|
|
|
42681
43012
|
return (0, import_node_path16.join)(config2.inboxDir, agentName, "inbox.md");
|
|
42682
43013
|
}
|
|
42683
43014
|
function inboxExists(inboxPath) {
|
|
42684
|
-
return (0,
|
|
43015
|
+
return (0, import_node_fs14.existsSync)(inboxPath);
|
|
42685
43016
|
}
|
|
42686
43017
|
function readInbox(inboxPath) {
|
|
42687
43018
|
if (!inboxExists(inboxPath)) {
|
|
42688
43019
|
return "";
|
|
42689
43020
|
}
|
|
42690
|
-
return (0,
|
|
43021
|
+
return (0, import_node_fs14.readFileSync)(inboxPath, "utf-8");
|
|
42691
43022
|
}
|
|
42692
43023
|
function hasUnreadMessages(inboxPath) {
|
|
42693
43024
|
const content = readInbox(inboxPath);
|
|
@@ -42774,6 +43105,7 @@ init_dist();
|
|
|
42774
43105
|
HOOK_ABI_VERSION,
|
|
42775
43106
|
HookEmitter,
|
|
42776
43107
|
HookRegistry,
|
|
43108
|
+
HttpAgentRelayClient,
|
|
42777
43109
|
InMemoryAdapter,
|
|
42778
43110
|
InMemoryWorkflowDb,
|
|
42779
43111
|
JsonFileWorkflowDb,
|