@relayflows/sdk 2.0.55 → 2.0.57
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/dist/adapters/base.d.ts +2 -0
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/devin.d.ts +4 -0
- package/dist/adapters/devin.d.ts.map +1 -0
- package/dist/adapters/devin.js +20 -0
- package/dist/adapters/devin.js.map +1 -0
- package/dist/adapters/index.d.ts +3 -2
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +5 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/authored-broker.d.ts +13 -0
- package/dist/authored-broker.d.ts.map +1 -0
- package/dist/authored-broker.js +75 -0
- package/dist/authored-broker.js.map +1 -0
- package/dist/authored-budget.d.ts +1 -1
- package/dist/authored-budget.d.ts.map +1 -1
- package/dist/authored-budget.js +8 -2
- package/dist/authored-budget.js.map +1 -1
- package/dist/authored-flow-error.d.ts +6 -0
- package/dist/authored-flow-error.d.ts.map +1 -1
- package/dist/authored-flow-error.js.map +1 -1
- package/dist/authored-flow-executor.d.ts +11 -0
- package/dist/authored-flow-executor.d.ts.map +1 -1
- package/dist/authored-flow-executor.js +104 -20
- package/dist/authored-flow-executor.js.map +1 -1
- package/dist/authored-flow-loader.d.ts.map +1 -1
- package/dist/authored-flow-loader.js +42 -3
- package/dist/authored-flow-loader.js.map +1 -1
- package/dist/authored-helper-effect.d.ts.map +1 -1
- package/dist/authored-helper-effect.js +1 -1
- package/dist/authored-helper-effect.js.map +1 -1
- package/dist/authored-hooks.d.ts +2 -1
- package/dist/authored-hooks.d.ts.map +1 -1
- package/dist/authored-hooks.js +2 -2
- package/dist/authored-hooks.js.map +1 -1
- package/dist/authored-human.d.ts +18 -0
- package/dist/authored-human.d.ts.map +1 -1
- package/dist/authored-human.js +32 -4
- package/dist/authored-human.js.map +1 -1
- package/dist/authored-mcp.d.ts.map +1 -1
- package/dist/authored-mcp.js +3 -2
- package/dist/authored-mcp.js.map +1 -1
- package/dist/authored-node-entry.js +4 -0
- package/dist/authored-node-entry.js.map +1 -1
- package/dist/authored-node-runner.d.ts.map +1 -1
- package/dist/authored-node-runner.js +1 -0
- package/dist/authored-node-runner.js.map +1 -1
- package/dist/authored-plugin-effect.d.ts.map +1 -1
- package/dist/authored-plugin-effect.js +3 -2
- package/dist/authored-plugin-effect.js.map +1 -1
- package/dist/authored-preflight.d.ts.map +1 -1
- package/dist/authored-preflight.js +2 -1
- package/dist/authored-preflight.js.map +1 -1
- package/dist/authored-root.d.ts.map +1 -1
- package/dist/authored-root.js +13 -3
- package/dist/authored-root.js.map +1 -1
- package/dist/authored-serial-ordinals.d.ts +41 -0
- package/dist/authored-serial-ordinals.d.ts.map +1 -0
- package/dist/authored-serial-ordinals.js +45 -0
- package/dist/authored-serial-ordinals.js.map +1 -0
- package/dist/authored-worker-step.d.ts +5 -1
- package/dist/authored-worker-step.d.ts.map +1 -1
- package/dist/authored-worker-step.js +19 -2
- package/dist/authored-worker-step.js.map +1 -1
- package/dist/budget-preflight.d.ts.map +1 -1
- package/dist/budget-preflight.js +18 -3
- package/dist/budget-preflight.js.map +1 -1
- package/dist/bundle-transport.d.ts +2 -0
- package/dist/bundle-transport.d.ts.map +1 -1
- package/dist/bundle-transport.js +4 -0
- package/dist/bundle-transport.js.map +1 -1
- package/dist/cached-flow-runtime.d.ts +3 -0
- package/dist/cached-flow-runtime.d.ts.map +1 -0
- package/dist/cached-flow-runtime.js +37 -0
- package/dist/cached-flow-runtime.js.map +1 -0
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +13 -1
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/cli-probe.d.ts.map +1 -1
- package/dist/cli/cli-probe.js +72 -11
- package/dist/cli/cli-probe.js.map +1 -1
- package/dist/cli/cloud-deploy.js +6 -5
- package/dist/cli/cloud-deploy.js.map +1 -1
- package/dist/cli/cloud-mirror-session.d.ts +8 -2
- package/dist/cli/cloud-mirror-session.d.ts.map +1 -1
- package/dist/cli/cloud-mirror-session.js +20 -27
- package/dist/cli/cloud-mirror-session.js.map +1 -1
- package/dist/cli/direct-run.d.ts.map +1 -1
- package/dist/cli/direct-run.js +2 -1
- package/dist/cli/direct-run.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +19 -4
- package/dist/cli/run.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +39 -18
- package/dist/cli.js.map +1 -1
- package/dist/cloud-deploy.d.ts +3 -3
- package/dist/cloud-deploy.d.ts.map +1 -1
- package/dist/cloud-deploy.js +15 -9
- package/dist/cloud-deploy.js.map +1 -1
- package/dist/cloud-flow-source.d.ts +13 -0
- package/dist/cloud-flow-source.d.ts.map +1 -0
- package/dist/cloud-flow-source.js +55 -0
- package/dist/cloud-flow-source.js.map +1 -0
- package/dist/cloud-mirror-transport.d.ts +2 -0
- package/dist/cloud-mirror-transport.d.ts.map +1 -1
- package/dist/cloud-mirror-transport.js +2 -0
- package/dist/cloud-mirror-transport.js.map +1 -1
- package/dist/cloud-mirror.d.ts.map +1 -1
- package/dist/cloud-mirror.js +10 -1
- package/dist/cloud-mirror.js.map +1 -1
- package/dist/communication/broker-preflight.d.ts +3 -0
- package/dist/communication/broker-preflight.d.ts.map +1 -0
- package/dist/communication/broker-preflight.js +22 -0
- package/dist/communication/broker-preflight.js.map +1 -0
- package/dist/communication/broker-spec.d.ts +20 -0
- package/dist/communication/broker-spec.d.ts.map +1 -0
- package/dist/communication/broker-spec.js +46 -0
- package/dist/communication/broker-spec.js.map +1 -0
- package/dist/communication/broker-worker.d.ts +17 -0
- package/dist/communication/broker-worker.d.ts.map +1 -0
- package/dist/communication/broker-worker.js +214 -0
- package/dist/communication/broker-worker.js.map +1 -0
- package/dist/communication/environment.d.ts.map +1 -1
- package/dist/communication/environment.js +1 -0
- package/dist/communication/environment.js.map +1 -1
- package/dist/communication/pty.d.ts +29 -0
- package/dist/communication/pty.d.ts.map +1 -0
- package/dist/communication/pty.js +44 -0
- package/dist/communication/pty.js.map +1 -0
- package/dist/communication/relay.d.ts +18 -0
- package/dist/communication/relay.d.ts.map +1 -1
- package/dist/communication/relay.js +4 -0
- package/dist/communication/relay.js.map +1 -1
- package/dist/communication/skills.d.ts +3 -0
- package/dist/communication/skills.d.ts.map +1 -0
- package/dist/communication/skills.js +18 -0
- package/dist/communication/skills.js.map +1 -0
- package/dist/communication/worker.d.ts.map +1 -1
- package/dist/communication/worker.js +9 -26
- package/dist/communication/worker.js.map +1 -1
- package/dist/devin-runtime.d.ts +10 -0
- package/dist/devin-runtime.d.ts.map +1 -0
- package/dist/devin-runtime.js +37 -0
- package/dist/devin-runtime.js.map +1 -0
- package/dist/failure-kinds.d.ts +1 -1
- package/dist/failure-kinds.d.ts.map +1 -1
- package/dist/failure-kinds.js +2 -0
- package/dist/failure-kinds.js.map +1 -1
- package/dist/plugin-manifest.js +1 -1
- package/dist/plugin-manifest.js.map +1 -1
- package/dist/preflight.d.ts +1 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +14 -4
- package/dist/preflight.js.map +1 -1
- package/dist/step-secrets.d.ts.map +1 -1
- package/dist/step-secrets.js +1 -0
- package/dist/step-secrets.js.map +1 -1
- package/dist/worker-cli.d.ts.map +1 -1
- package/dist/worker-cli.js +15 -3
- package/dist/worker-cli.js.map +1 -1
- package/dist/worker.d.ts +1 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +12 -0
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/base.ts +2 -0
- package/src/adapters/devin.ts +22 -0
- package/src/adapters/index.ts +5 -2
- package/src/authored-broker.ts +73 -0
- package/src/authored-budget.ts +6 -2
- package/src/authored-flow-error.ts +6 -0
- package/src/authored-flow-executor.ts +115 -20
- package/src/authored-flow-loader.ts +43 -3
- package/src/authored-helper-effect.ts +2 -1
- package/src/authored-hooks.ts +4 -3
- package/src/authored-human.ts +35 -4
- package/src/authored-mcp.ts +7 -2
- package/src/authored-node-entry.ts +4 -1
- package/src/authored-node-runner.ts +1 -0
- package/src/authored-plugin-effect.ts +7 -2
- package/src/authored-preflight.ts +2 -1
- package/src/authored-root.ts +13 -3
- package/src/authored-serial-ordinals.ts +76 -0
- package/src/authored-worker-step.ts +17 -2
- package/src/budget-preflight.ts +16 -2
- package/src/bundle-transport.ts +4 -0
- package/src/cached-flow-runtime.ts +32 -0
- package/src/cli/check.ts +11 -1
- package/src/cli/cli-probe.ts +65 -11
- package/src/cli/cloud-deploy.ts +5 -5
- package/src/cli/cloud-mirror-session.ts +23 -29
- package/src/cli/direct-run.ts +2 -1
- package/src/cli/run.ts +18 -3
- package/src/cli.ts +31 -13
- package/src/cloud-deploy.ts +19 -14
- package/src/cloud-flow-source.ts +58 -0
- package/src/cloud-mirror-transport.ts +4 -0
- package/src/cloud-mirror.ts +9 -1
- package/src/communication/broker-preflight.ts +22 -0
- package/src/communication/broker-spec.ts +48 -0
- package/src/communication/broker-worker.ts +198 -0
- package/src/communication/environment.ts +1 -0
- package/src/communication/pty.ts +51 -0
- package/src/communication/relay.ts +12 -1
- package/src/communication/skills.ts +17 -0
- package/src/communication/worker.ts +9 -26
- package/src/devin-runtime.ts +33 -0
- package/src/failure-kinds.ts +2 -0
- package/src/plugin-manifest.ts +1 -1
- package/src/preflight.ts +15 -4
- package/src/step-secrets.ts +1 -0
- package/src/worker-cli.ts +13 -5
- package/src/worker.ts +11 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { basename } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const execute = promisify(execFile);
|
|
6
|
+
/** Install full skill directories (including references) using the canonical package. */
|
|
7
|
+
export async function installBrokerSkills(skills: string[], cli: string, cwd: string, signal: AbortSignal): Promise<void> {
|
|
8
|
+
if (!skills.length) return;
|
|
9
|
+
const identity = basename(cli).replace(/\.exe$/i, '');
|
|
10
|
+
// Devin reads .windsurf/skills, which the canonical skills installer supports.
|
|
11
|
+
const harness = ({ claude: 'claude-code', codex: 'codex', gemini: 'gemini-cli',
|
|
12
|
+
'cursor-agent': 'cursor', opencode: 'opencode', devin: 'windsurf' } as Record<string, string>)[identity];
|
|
13
|
+
if (!harness) throw new Error(`Skill installation is unsupported for broker CLI ${identity}`);
|
|
14
|
+
await execute('npx', ['--yes', 'skills', 'add', 'https://github.com/AgentWorkforce/skills',
|
|
15
|
+
'--skill', ...skills, '--agent', harness, '--yes', '--copy'],
|
|
16
|
+
{ cwd, signal, timeout: 120_000, maxBuffer: 1024 * 1024 });
|
|
17
|
+
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { communicationHistory } from './history.js';
|
|
2
|
-
import { mkdir, mkdtemp, rm, symlink } from 'node:fs/promises';
|
|
3
|
-
import { basename, join } from 'node:path';
|
|
4
2
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
3
|
import type { JournalClient } from '../journal-client.js';
|
|
6
4
|
import type { StepDispatchEvent } from '../protocol.js';
|
|
@@ -8,13 +6,12 @@ import type { ResolvedKernelAgentStep } from '../resolved-cli-identity.js';
|
|
|
8
6
|
import { isLeaseLost, withWorkerLease } from '../worker-lease.js';
|
|
9
7
|
import { isReadInterruptionError } from '../journal-read-policy.js';
|
|
10
8
|
import { workerInstruction } from '../worker-input.js';
|
|
11
|
-
import { resolveCliModel } from '../cli-adapter.js';
|
|
12
9
|
import { resolveAgentCwd } from '../agent-cwd.js';
|
|
13
10
|
import { channelName, type CommunicationInstruction } from './spec.js';
|
|
14
11
|
import { acquireRelayRuntime, type RelayHandle } from './relay.js';
|
|
15
12
|
import { CommunicationSession } from './session.js';
|
|
16
13
|
import { openCommunicationTools } from './tools.js';
|
|
17
|
-
import {
|
|
14
|
+
import { spawnFlowPty, shellQuote } from './pty.js';
|
|
18
15
|
|
|
19
16
|
export function requireCommunicationCli(cli: string | undefined): void {
|
|
20
17
|
if (!cli?.trim()) throw new Error('Agent communication requires a declared CLI executable');
|
|
@@ -62,7 +59,7 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
|
|
|
62
59
|
let pumping: Promise<void> | undefined;
|
|
63
60
|
let receipts = Promise.resolve();
|
|
64
61
|
let unsubscribe: (() => void) | undefined;
|
|
65
|
-
let
|
|
62
|
+
let pty: Awaited<ReturnType<typeof spawnFlowPty>> | undefined;
|
|
66
63
|
try {
|
|
67
64
|
let resolve!: (value: unknown) => void;
|
|
68
65
|
let reject!: (reason: unknown) => void;
|
|
@@ -74,8 +71,7 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
|
|
|
74
71
|
if (signal.aborted) abort();
|
|
75
72
|
const session = new CommunicationSession(client, dispatch, instruction, relay, summary => resolve({ summary }), reject);
|
|
76
73
|
tools = await openCommunicationTools(request => session.invoke(request));
|
|
77
|
-
const
|
|
78
|
-
const helper = `node ${quote(tools.helperPath)}`;
|
|
74
|
+
const helper = `node ${shellQuote(tools.helperPath)}`;
|
|
79
75
|
const history = await communicationHistory(client, dispatch, instruction);
|
|
80
76
|
const prompt = `${workerInstruction(instruction.instruction, dispatch)}${history}\n\nYou are flow agent ${dispatch.step_id}. Concurrent peers send messages automatically into your session through Relay. Do not poll for messages. Use your shell/terminal execution tool with ${helper} for flow communication:\n- send PEER STABLE_ID 'message text' (outgoing peers: ${instruction.outgoing.join(', ') || 'none'})\n- ack PEER DELIVERY_SEQ only after processing that message (incoming peers: ${instruction.incoming.join(', ') || 'none'})\n- complete 'summary' when your task and conversation are finished.\nUse these helpers for all peer communication; do not use Relay MCP messaging tools. Stay available for injected messages until the conversation is complete. Use stable semantic message IDs so retries deduplicate sends.`;
|
|
81
77
|
const name = `${relay.prefix}-${dispatch.step_id}`;
|
|
@@ -89,24 +85,11 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
|
|
|
89
85
|
});
|
|
90
86
|
void receipts.catch(reject);
|
|
91
87
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const linkRoot = join(dataDir, 'communication');
|
|
98
|
-
await mkdir(linkRoot, { recursive: true, mode: 0o700 });
|
|
99
|
-
cliLinkDirectory = await mkdtemp(join(linkRoot, 'cli-'));
|
|
100
|
-
command = join(cliLinkDirectory, argv0);
|
|
101
|
-
await symlink(spec.cli!, command, 'file');
|
|
102
|
-
}
|
|
103
|
-
handle = await relay.broker.spawnPty({ name, cli: basename(cliIdentity).replace(/\.exe$/i, ''), task: prompt, channels: [], skipRelayPrompt: true,
|
|
104
|
-
model: resolveCliModel(cliIdentity, spec.model), cwd: directory,
|
|
105
|
-
harnessConfig: { runtime: 'pty', command: quote(command), args: [],
|
|
106
|
-
cwd: directory, env: { ...agentEnvironment(cliIdentity, environment ?? process.env,
|
|
107
|
-
spec.secrets, client.supportsFeature?.('step_secret_scoping') ?? true),
|
|
108
|
-
RELAYFLOW_COMMUNICATION_SOCKET: tools.path, RELAYFLOW_COMMUNICATION_TOKEN: tools.token },
|
|
109
|
-
delivery: { mode: 'pty-injection', format: 'relay-block' } } });
|
|
88
|
+
pty = await spawnFlowPty(relay.broker, { name, cli: spec.cli!, identity: spec.cli_identity,
|
|
89
|
+
model: spec.model, task: prompt, channels: [], cwd: directory, dataDir,
|
|
90
|
+
environment, socket: tools.path, token: tools.token,
|
|
91
|
+
secrets: spec.secrets, scopeSecrets: client.supportsFeature?.('step_secret_scoping') ?? true });
|
|
92
|
+
handle = pty.handle;
|
|
110
93
|
const ready = await handle.waitForReady(Math.min(instruction.timeoutMs, 90_000));
|
|
111
94
|
if (ready.reason !== 'ready') throw new Error(`Communication agent did not become ready: ${ready.reason}`);
|
|
112
95
|
pumping = (async () => {
|
|
@@ -123,7 +106,7 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
|
|
|
123
106
|
finally { try { await handle?.release('Flow communication attempt ended', { deleteIdentity: true }); }
|
|
124
107
|
finally { try { await tools?.close(); } finally {
|
|
125
108
|
try { await relay.close(); }
|
|
126
|
-
finally {
|
|
109
|
+
finally { await pty?.close(); }
|
|
127
110
|
} } }
|
|
128
111
|
}
|
|
129
112
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import type { CliInvocation } from './adapters/base.js';
|
|
5
|
+
|
|
6
|
+
/** Private attempt/probe state, never the user's interactive Devin login. */
|
|
7
|
+
export function prepareDevinRuntime(environment: NodeJS.ProcessEnv, invocation?: CliInvocation) {
|
|
8
|
+
let root: string | undefined;
|
|
9
|
+
const env = { ...environment };
|
|
10
|
+
const args = [...(invocation?.args ?? [])];
|
|
11
|
+
const directory = () => root ??= mkdtempSync(join(environment.TMPDIR ?? tmpdir(), 'relayflows-devin-'));
|
|
12
|
+
const close = () => { if (root !== undefined) rmSync(root, { recursive: true, force: true }); };
|
|
13
|
+
try {
|
|
14
|
+
if (env.DEVIN_API_KEY !== undefined) {
|
|
15
|
+
const key = env.DEVIN_API_KEY.trim();
|
|
16
|
+
if (!key) throw new Error('DEVIN_API_KEY must not be empty');
|
|
17
|
+
const data = join(directory(), 'data');
|
|
18
|
+
mkdirSync(join(data, 'devin'), { recursive: true, mode: 0o700 });
|
|
19
|
+
writeFileSync(join(data, 'devin', 'credentials.toml'),
|
|
20
|
+
`windsurf_api_key = ${JSON.stringify(key)}\n`, { mode: 0o600 });
|
|
21
|
+
env.XDG_DATA_HOME = data;
|
|
22
|
+
env.XDG_CONFIG_HOME = join(directory(), 'config');
|
|
23
|
+
env.XDG_CACHE_HOME = join(directory(), 'cache');
|
|
24
|
+
delete env.DEVIN_API_KEY;
|
|
25
|
+
}
|
|
26
|
+
if (invocation?.promptFile !== undefined) {
|
|
27
|
+
const path = join(directory(), 'prompt.txt');
|
|
28
|
+
writeFileSync(path, invocation.promptFile, { mode: 0o600 });
|
|
29
|
+
args.push('--prompt-file', path);
|
|
30
|
+
}
|
|
31
|
+
return { env, args, close };
|
|
32
|
+
} catch (error) { close(); throw error; }
|
|
33
|
+
}
|
package/src/failure-kinds.ts
CHANGED
|
@@ -138,6 +138,8 @@ export const RUN_FAILURE_KINDS = [
|
|
|
138
138
|
'unsafe_parallel_runtime',
|
|
139
139
|
/** A predicate `.gate(fn)` judged false; the verdict is journaled as `<step>.gate`. */
|
|
140
140
|
'gate_failed',
|
|
141
|
+
/** A resumed serial body reached a journaled step id with a different call; refused, not executed. */
|
|
142
|
+
'serial_replay_order_mismatch',
|
|
141
143
|
/** `flows answer` named a wait the run is not asking: unknown, or already answered. */
|
|
142
144
|
'human_wait_unknown',
|
|
143
145
|
/** `f.memory.learn` was given a finding without non-empty string fields. */
|
package/src/plugin-manifest.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface PluginManifest {
|
|
|
39
39
|
preflight: { credentials: readonly string[]; servers: readonly string[] };
|
|
40
40
|
}
|
|
41
41
|
const identifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
42
|
-
const reserved = new Set(['run', 'llm', 'agent', 'human', 'dispatch', 'done', 'cloud', 'memory', 'mcp', 'slack', '__proto__', 'constructor', 'prototype', 'then']);
|
|
42
|
+
const reserved = new Set(['run', 'llm', 'agent', 'message', 'release', 'human', 'dispatch', 'done', 'cloud', 'memory', 'mcp', 'slack', '__proto__', 'constructor', 'prototype', 'then']);
|
|
43
43
|
const object = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
44
44
|
const strings = (v: unknown): v is string[] => Array.isArray(v) && v.every(s => typeof s === 'string' && s.trim().length > 0);
|
|
45
45
|
export function pluginPackageName(name: string): string {
|
package/src/preflight.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brokerInstruction } from './communication/broker-spec.js';
|
|
1
2
|
import { communicationInstruction } from './communication/spec.js';
|
|
2
3
|
import { loadPlugins, type LoadedPlugin } from './plugin-loader.js';
|
|
3
4
|
import { PluginError } from './plugin-manifest.js';
|
|
@@ -10,7 +11,7 @@ import type { TriggerSource } from '@relayflows/surface';
|
|
|
10
11
|
import { acceptsAnyOutput, inspectStepGate, type StepGateInspection } from './gate-contract.js';
|
|
11
12
|
import { compileSpec, CompileError } from './compile.js';
|
|
12
13
|
import { helperCall } from './yaml-helpers.js';
|
|
13
|
-
import { resolveCliModelSelection, type CliModelSource } from './cli-adapter.js';
|
|
14
|
+
import { cliAdapterKind, resolveCliModelSelection, type CliModelSource } from './cli-adapter.js';
|
|
14
15
|
import { isNamedGate, NAMED_GATE_FAILURE_KINDS, type NamedGateFailureKind } from './named-gates.js';
|
|
15
16
|
import { namedGateScanCoverageDiagnostics } from './named-gate-preflight.js';
|
|
16
17
|
import { compileScopes, type ScopeInput, type MountRegistry } from './scope-compiler.js';
|
|
@@ -64,6 +65,7 @@ export interface CliProbeResult {
|
|
|
64
65
|
|
|
65
66
|
export type CliProbeFailureDetail =
|
|
66
67
|
| 'spawn_failed'
|
|
68
|
+
| 'devin_api_key_empty'
|
|
67
69
|
| `timeout:${number}ms`
|
|
68
70
|
| `signal:${string}`;
|
|
69
71
|
|
|
@@ -285,6 +287,10 @@ export function resolvePreflight(flow: unknown, options: ResolutionOptions): Pre
|
|
|
285
287
|
message: unresolvedCliMessage(step.id, options),
|
|
286
288
|
});
|
|
287
289
|
} else {
|
|
290
|
+
if (step.type === 'llm' && cliAdapterKind(resolution.cli) === 'devin') {
|
|
291
|
+
cliResolutionDiagnostics.push({ severity: 'refusal', kind: 'cli_unsupported', stepId: step.id,
|
|
292
|
+
cli: resolution.cli, message: `Step "${step.id}" uses Devin for an llm step; Devin supports agent steps only.` });
|
|
293
|
+
}
|
|
288
294
|
resolutions.push(resolution);
|
|
289
295
|
resolutionByStep.set(step.id, resolution);
|
|
290
296
|
}
|
|
@@ -340,7 +346,8 @@ function preflightSync(flow: unknown, options: PreflightOptions): PreflightResul
|
|
|
340
346
|
}
|
|
341
347
|
const resolution = resolutionByStep.get(step.id)!;
|
|
342
348
|
probeResolvedCli(resolution, options.probes, cliProbeResults, diagnostics,
|
|
343
|
-
step.type === 'agent' && communicationInstruction(step.instruction) !== undefined
|
|
349
|
+
step.type === 'agent' && (communicationInstruction(step.instruction) !== undefined || brokerInstruction(step.instruction) !== undefined),
|
|
350
|
+
step.secrets);
|
|
344
351
|
}
|
|
345
352
|
|
|
346
353
|
for (const trigger of compiled.triggers ?? []) {
|
|
@@ -592,13 +599,16 @@ function probeResolvedCli(
|
|
|
592
599
|
kind: 'cli_unsupported',
|
|
593
600
|
stepId: resolution.stepId,
|
|
594
601
|
cli: resolution.cli,
|
|
595
|
-
message: `Step "${resolution.stepId}" declares CLI "${resolution.cli}", but it is neither a supported raw Claude/Codex executable nor a conforming Relayflows wrapper; custom wrappers must identify with the relayflows-agent-cli-v1 contract.`,
|
|
602
|
+
message: `Step "${resolution.stepId}" declares CLI "${resolution.cli}", but it is neither a supported raw Claude/Codex/Devin executable nor a conforming Relayflows wrapper; custom wrappers must identify with the relayflows-agent-cli-v1 contract.`,
|
|
596
603
|
});
|
|
597
604
|
} else if (managed && result.authenticated === 'unverified') {
|
|
598
605
|
diagnostics.push({ severity: 'warning', kind: 'managed_cli_unverified', stepId: resolution.stepId,
|
|
599
606
|
message: `Step "${resolution.stepId}" uses Relay's interactive CLI transport. The executable exists, but authentication${resolution.model ? ' and access to model "' + resolution.model + '"' : ''} could not be verified before startup. The managed session must execute its task and report completion; startup or task failure fails the step.` });
|
|
600
607
|
} else if (result.authenticated !== true) {
|
|
601
608
|
const command = result.authCommand ?? `${resolution.cli} auth status`;
|
|
609
|
+
const loginHint = cliAdapterKind(resolution.cli) === 'devin'
|
|
610
|
+
? ' For hosted flows, set FLOW_DEVIN_API_KEY to a Devin CLI login token; local runs use DEVIN_API_KEY. A REST API key is a different credential.'
|
|
611
|
+
: '';
|
|
602
612
|
// Say what the probe reported. A refusal that names only the command turns
|
|
603
613
|
// a transient provider rejection and a genuinely unauthenticated CLI into
|
|
604
614
|
// the same message, and the difference decides whether retrying is
|
|
@@ -614,7 +624,7 @@ function probeResolvedCli(
|
|
|
614
624
|
kind: 'cli_unauthenticated',
|
|
615
625
|
stepId: resolution.stepId,
|
|
616
626
|
cli: resolution.cli,
|
|
617
|
-
message: `Step "${resolution.stepId}" declares CLI "${resolution.cli}", but "${command}" exited non-zero${exitCode}; authenticate it or repair that adapter's authentication probe.${detail}`,
|
|
627
|
+
message: `Step "${resolution.stepId}" declares CLI "${resolution.cli}", but "${command}" exited non-zero${exitCode}; authenticate it or repair that adapter's authentication probe.${detail}${loginHint}`,
|
|
618
628
|
});
|
|
619
629
|
} else if (resolution.model !== undefined && result.modelAvailable !== true) {
|
|
620
630
|
diagnostics.push(modelProbeRefusal(resolution as CliResolution & { model: string }, result));
|
|
@@ -666,6 +676,7 @@ function probeFailedMessage(
|
|
|
666
676
|
): string {
|
|
667
677
|
const prefix = `Could not verify CLI "${resolution.cli}" for step "${resolution.stepId}"`;
|
|
668
678
|
if (detail === undefined) return `${prefix}.`;
|
|
679
|
+
if (detail === 'devin_api_key_empty') return `${prefix}: DEVIN_API_KEY must contain a non-empty Devin CLI login token. For hosted flows, set FLOW_DEVIN_API_KEY.`;
|
|
669
680
|
if (detail === 'spawn_failed') return `${prefix}: the probe process could not be started.`;
|
|
670
681
|
if (detail.startsWith('signal:')) {
|
|
671
682
|
return `${prefix}: the probe was terminated by signal "${detail.slice('signal:'.length)}".`;
|
package/src/step-secrets.ts
CHANGED
|
@@ -47,6 +47,7 @@ const CLI_LOGIN: Record<CliAdapterKind, readonly string[]> = {
|
|
|
47
47
|
'AWS_SESSION_TOKEN', 'AWS_REGION', 'AWS_DEFAULT_REGION',
|
|
48
48
|
],
|
|
49
49
|
codex: ['CODEX_HOME', 'OPENAI_API_KEY', 'OPENAI_BASE_URL', 'OPENAI_API_BASE', 'OPENROUTER_API_KEY'],
|
|
50
|
+
devin: ['DEVIN_API_KEY', 'DEVIN_MODEL'],
|
|
50
51
|
'relayflows-wrapper-v1': [],
|
|
51
52
|
};
|
|
52
53
|
|
package/src/worker-cli.ts
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
type AgentTransport,
|
|
46
46
|
} from './agent-relay-transport.js';
|
|
47
47
|
import { pinCliAlias } from './cli/pinned-cli-alias.js';
|
|
48
|
+
import { prepareDevinRuntime } from './devin-runtime.js';
|
|
48
49
|
import { promptOperand } from './adapters/base.js';
|
|
49
50
|
|
|
50
51
|
export type { CliTransportCause, CliTransportEvidence, CliTransportPhase } from './cli-transport-evidence.js';
|
|
@@ -277,15 +278,22 @@ export async function runAgentCli(
|
|
|
277
278
|
// The flags go before the `--` that ends the task operand (promptOperand):
|
|
278
279
|
// after it they would reach the CLI as task text, not options.
|
|
279
280
|
const args = [...invocation.args];
|
|
280
|
-
args.splice(args.length - promptOperand('').length, 0, ...(kind === 'claude' ? ['--output-format', 'stream-json', '--verbose'] : ['--json']));
|
|
281
|
+
if (kind !== 'devin') args.splice(args.length - promptOperand('').length, 0, ...(kind === 'claude' ? ['--output-format', 'stream-json', '--verbose'] : ['--json']));
|
|
281
282
|
const completion = kind === 'claude' ? claudeResultOutcome : undefined;
|
|
282
283
|
// Retain the source values for output redaction even though the child
|
|
283
284
|
// receives only its filtered environment. A provider can echo a value from
|
|
284
285
|
// its prompt or fixture that was not granted as an environment variable.
|
|
285
|
-
const redactionEnvironment = { ...processEnvironment, ...env
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
286
|
+
const redactionEnvironment = { ...processEnvironment, ...env,
|
|
287
|
+
...(kind === 'devin' && env.DEVIN_API_KEY !== undefined ? { DEVIN_API_KEY: env.DEVIN_API_KEY.trim() } : {}) };
|
|
288
|
+
const prepared = kind === 'devin' ? prepareDevinRuntime(env, { ...invocation, args }) : undefined;
|
|
289
|
+
try {
|
|
290
|
+
const result = await spawnInvocation(cli,
|
|
291
|
+
{ ...invocation, args: prepared?.args ?? args }, prepared?.env ?? env,
|
|
292
|
+
signal, sidechannel, cwd, completion, argv0, kind, redactionEnvironment);
|
|
293
|
+
// Print mode has neither provider frames nor authoritative token counts.
|
|
294
|
+
return kind === 'devin' ? { ...result, stdout_tail: redactText(result.stdout_tail, redactionEnvironment) }
|
|
295
|
+
: requirePricedUsage(decodeProviderResult(result, kind, redactionEnvironment), effectiveModel);
|
|
296
|
+
} finally { prepared?.close(); }
|
|
289
297
|
}
|
|
290
298
|
}
|
|
291
299
|
|
package/src/worker.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { brokerInstruction } from './communication/broker-spec.js';
|
|
2
|
+
import { BrokerSessions } from './communication/broker-worker.js';
|
|
1
3
|
import { communicationInstruction } from './communication/spec.js';
|
|
2
4
|
import { workerSpend } from './worker-spend.js';
|
|
3
5
|
import { reportedCost } from './reported-cost.js';
|
|
@@ -60,6 +62,7 @@ export interface AgentWorkerOptions {
|
|
|
60
62
|
* shutdown.
|
|
61
63
|
*/
|
|
62
64
|
export class AgentWorker extends EventEmitter {
|
|
65
|
+
private readonly brokerSessions: BrokerSessions;
|
|
63
66
|
private attached = false;
|
|
64
67
|
private closing = false;
|
|
65
68
|
private readonly inFlight: Set<Promise<void>> = new Set();
|
|
@@ -69,6 +72,7 @@ export class AgentWorker extends EventEmitter {
|
|
|
69
72
|
private readonly options: AgentWorkerOptions,
|
|
70
73
|
) {
|
|
71
74
|
super();
|
|
75
|
+
this.brokerSessions = new BrokerSessions(options.runRoot, options.environment);
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
async attach(): Promise<void> {
|
|
@@ -107,6 +111,7 @@ export class AgentWorker extends EventEmitter {
|
|
|
107
111
|
if (pending.length > 0) {
|
|
108
112
|
await Promise.allSettled(pending);
|
|
109
113
|
}
|
|
114
|
+
await this.brokerSessions.close();
|
|
110
115
|
this.attached = false;
|
|
111
116
|
}
|
|
112
117
|
|
|
@@ -128,6 +133,12 @@ export class AgentWorker extends EventEmitter {
|
|
|
128
133
|
await completeHelperDispatch(this.client, dispatch, helper, this.options.dataDir);
|
|
129
134
|
return;
|
|
130
135
|
}
|
|
136
|
+
const broker = brokerInstruction(spec.instruction);
|
|
137
|
+
if (broker) {
|
|
138
|
+
if (!this.options.dataDir) throw new Error('Broker agents require a worker data directory');
|
|
139
|
+
await this.brokerSessions.complete(this.client, dispatch, broker, this.options.dataDir);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
131
142
|
const communication = communicationInstruction(spec.instruction);
|
|
132
143
|
if (communication) {
|
|
133
144
|
if (!this.options.dataDir) throw new Error('Agent communication requires a worker data directory');
|