@relayflows/sdk 2.0.30 → 2.0.31
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/agent-artifacts.d.ts.map +1 -1
- package/dist/agent-artifacts.js +2 -9
- package/dist/agent-artifacts.js.map +1 -1
- package/dist/agent-cwd.d.ts +69 -0
- package/dist/agent-cwd.d.ts.map +1 -0
- package/dist/agent-cwd.js +140 -0
- package/dist/agent-cwd.js.map +1 -0
- package/dist/artifact-scan-policy.d.ts +35 -0
- package/dist/artifact-scan-policy.d.ts.map +1 -0
- package/dist/artifact-scan-policy.js +49 -0
- package/dist/artifact-scan-policy.js.map +1 -0
- package/dist/authored-completion-record.d.ts +35 -0
- package/dist/authored-completion-record.d.ts.map +1 -0
- package/dist/authored-completion-record.js +92 -0
- package/dist/authored-completion-record.js.map +1 -0
- package/dist/authored-completion.d.ts +99 -0
- package/dist/authored-completion.d.ts.map +1 -0
- package/dist/authored-completion.js +184 -0
- package/dist/authored-completion.js.map +1 -0
- package/dist/authored-flow-error.d.ts +13 -1
- package/dist/authored-flow-error.d.ts.map +1 -1
- package/dist/authored-flow-error.js +12 -0
- package/dist/authored-flow-error.js.map +1 -1
- package/dist/authored-flow-executor.d.ts +7 -30
- package/dist/authored-flow-executor.d.ts.map +1 -1
- package/dist/authored-flow-executor.js +54 -47
- package/dist/authored-flow-executor.js.map +1 -1
- package/dist/authored-human.d.ts.map +1 -1
- package/dist/authored-human.js +1 -3
- package/dist/authored-human.js.map +1 -1
- package/dist/authored-node-entry.js +4 -1
- package/dist/authored-node-entry.js.map +1 -1
- package/dist/authored-node-runner.d.ts +9 -1
- package/dist/authored-node-runner.d.ts.map +1 -1
- package/dist/authored-node-runner.js +92 -17
- package/dist/authored-node-runner.js.map +1 -1
- package/dist/authored-root.d.ts +5 -0
- package/dist/authored-root.d.ts.map +1 -1
- package/dist/authored-root.js +20 -2
- package/dist/authored-root.js.map +1 -1
- package/dist/authored-step-output.d.ts +35 -10
- package/dist/authored-step-output.d.ts.map +1 -1
- package/dist/authored-step-output.js +108 -40
- package/dist/authored-step-output.js.map +1 -1
- package/dist/authored-verdict.d.ts +28 -0
- package/dist/authored-verdict.d.ts.map +1 -0
- package/dist/authored-verdict.js +79 -0
- package/dist/authored-verdict.js.map +1 -0
- package/dist/authored-worker-step.d.ts +5 -2
- package/dist/authored-worker-step.d.ts.map +1 -1
- package/dist/authored-worker-step.js +48 -11
- package/dist/authored-worker-step.js.map +1 -1
- package/dist/cli/check-worker-surface.d.ts +27 -0
- package/dist/cli/check-worker-surface.d.ts.map +1 -0
- package/dist/cli/check-worker-surface.js +63 -0
- package/dist/cli/check-worker-surface.js.map +1 -0
- package/dist/cli/check.d.ts +19 -2
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +12 -4
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/cloud-format.d.ts +9 -0
- package/dist/cli/cloud-format.d.ts.map +1 -0
- package/dist/cli/cloud-format.js +77 -0
- package/dist/cli/cloud-format.js.map +1 -0
- package/dist/cli/cloud-live.d.ts +39 -0
- package/dist/cli/cloud-live.d.ts.map +1 -0
- package/dist/cli/cloud-live.js +325 -0
- package/dist/cli/cloud-live.js.map +1 -0
- package/dist/cli/cloud-read.d.ts +3 -7
- package/dist/cli/cloud-read.d.ts.map +1 -1
- package/dist/cli/cloud-read.js +14 -266
- package/dist/cli/cloud-read.js.map +1 -1
- package/dist/cli/cloud-refusal.d.ts +39 -0
- package/dist/cli/cloud-refusal.d.ts.map +1 -0
- package/dist/cli/cloud-refusal.js +95 -0
- package/dist/cli/cloud-refusal.js.map +1 -0
- package/dist/cli/cloud-status-view.d.ts +7 -0
- package/dist/cli/cloud-status-view.d.ts.map +1 -0
- package/dist/cli/cloud-status-view.js +214 -0
- package/dist/cli/cloud-status-view.js.map +1 -0
- package/dist/cli/direct-run.d.ts.map +1 -1
- package/dist/cli/direct-run.js +16 -2
- package/dist/cli/direct-run.js.map +1 -1
- package/dist/cli/hn-monitor.d.ts +0 -13
- package/dist/cli/hn-monitor.d.ts.map +1 -1
- package/dist/cli/hn-monitor.js +2 -1
- package/dist/cli/hn-monitor.js.map +1 -1
- package/dist/cli/local-agent-remedy.d.ts +80 -0
- package/dist/cli/local-agent-remedy.d.ts.map +1 -0
- package/dist/cli/local-agent-remedy.js +116 -0
- package/dist/cli/local-agent-remedy.js.map +1 -0
- package/dist/cli/observer-session.d.ts +41 -0
- package/dist/cli/observer-session.d.ts.map +1 -0
- package/dist/cli/observer-session.js +78 -0
- package/dist/cli/observer-session.js.map +1 -0
- package/dist/cli/run.d.ts +30 -2
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +186 -24
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/status.d.ts +9 -0
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +31 -2
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/step-evidence.d.ts +90 -0
- package/dist/cli/step-evidence.d.ts.map +1 -0
- package/dist/cli/step-evidence.js +259 -0
- package/dist/cli/step-evidence.js.map +1 -0
- package/dist/cli/step-failure.d.ts +14 -4
- package/dist/cli/step-failure.d.ts.map +1 -1
- package/dist/cli/step-failure.js +41 -76
- package/dist/cli/step-failure.js.map +1 -1
- package/dist/cli-commands.d.ts +6 -0
- package/dist/cli-commands.d.ts.map +1 -1
- package/dist/cli-commands.js +2 -0
- package/dist/cli-commands.js.map +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +53 -42
- package/dist/cli.js.map +1 -1
- package/dist/cloud-read.d.ts +15 -0
- package/dist/cloud-read.d.ts.map +1 -1
- package/dist/cloud-read.js +20 -1
- package/dist/cloud-read.js.map +1 -1
- package/dist/cloud-run-record.d.ts +27 -0
- package/dist/cloud-run-record.d.ts.map +1 -0
- package/dist/cloud-run-record.js +56 -0
- package/dist/cloud-run-record.js.map +1 -0
- package/dist/cloud-run.d.ts +2 -9
- package/dist/cloud-run.d.ts.map +1 -1
- package/dist/cloud-run.js +3 -20
- package/dist/cloud-run.js.map +1 -1
- package/dist/communication/local.d.ts.map +1 -1
- package/dist/communication/local.js +2 -1
- package/dist/communication/local.js.map +1 -1
- package/dist/communication/worker.d.ts +1 -1
- package/dist/communication/worker.d.ts.map +1 -1
- package/dist/communication/worker.js +13 -5
- package/dist/communication/worker.js.map +1 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +28 -6
- package/dist/compile.js.map +1 -1
- package/dist/direct-input.d.ts.map +1 -1
- package/dist/direct-input.js +21 -1
- package/dist/direct-input.js.map +1 -1
- package/dist/failure-kinds.d.ts +96 -6
- package/dist/failure-kinds.d.ts.map +1 -1
- package/dist/failure-kinds.js +29 -2
- package/dist/failure-kinds.js.map +1 -1
- package/dist/flow-requirements.d.ts.map +1 -1
- package/dist/flow-requirements.js +24 -9
- package/dist/flow-requirements.js.map +1 -1
- package/dist/gate-contract.d.ts +7 -0
- package/dist/gate-contract.d.ts.map +1 -1
- package/dist/gate-contract.js +6 -1
- package/dist/gate-contract.js.map +1 -1
- package/dist/helper-preflight.d.ts.map +1 -1
- package/dist/helper-preflight.js +10 -3
- package/dist/helper-preflight.js.map +1 -1
- package/dist/helper-reference.d.ts +25 -0
- package/dist/helper-reference.d.ts.map +1 -0
- package/dist/helper-reference.js +129 -0
- package/dist/helper-reference.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/journal-client.d.ts +3 -1
- package/dist/journal-client.d.ts.map +1 -1
- package/dist/journal-client.js +17 -2
- package/dist/journal-client.js.map +1 -1
- package/dist/journal-projection.d.ts +16 -0
- package/dist/journal-projection.d.ts.map +1 -0
- package/dist/journal-projection.js +108 -0
- package/dist/journal-projection.js.map +1 -0
- package/dist/llm-worker.js +1 -1
- package/dist/llm-worker.js.map +1 -1
- package/dist/local-agent.d.ts +1 -1
- package/dist/local-agent.d.ts.map +1 -1
- package/dist/local-agent.js +4 -3
- package/dist/local-agent.js.map +1 -1
- package/dist/named-gate-lowering.js +35 -5
- package/dist/named-gate-lowering.js.map +1 -1
- package/dist/named-gate-preflight.d.ts +27 -0
- package/dist/named-gate-preflight.d.ts.map +1 -0
- package/dist/named-gate-preflight.js +47 -0
- package/dist/named-gate-preflight.js.map +1 -0
- package/dist/named-gates.d.ts +1 -1
- package/dist/named-gates.d.ts.map +1 -1
- package/dist/named-gates.js +3 -0
- package/dist/named-gates.js.map +1 -1
- package/dist/observer-link.d.ts +7 -0
- package/dist/observer-link.d.ts.map +1 -1
- package/dist/observer-link.js +3 -1
- package/dist/observer-link.js.map +1 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +7 -0
- package/dist/preflight.js.map +1 -1
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/redact.d.ts +42 -0
- package/dist/redact.d.ts.map +1 -1
- package/dist/redact.js +128 -0
- package/dist/redact.js.map +1 -1
- package/dist/run-projection.d.ts +83 -0
- package/dist/run-projection.d.ts.map +1 -0
- package/dist/run-projection.js +199 -0
- package/dist/run-projection.js.map +1 -0
- package/dist/shell-word.d.ts +19 -0
- package/dist/shell-word.d.ts.map +1 -0
- package/dist/shell-word.js +23 -0
- package/dist/shell-word.js.map +1 -0
- package/dist/spec.d.ts +52 -3
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/step-fields.d.ts +1 -1
- package/dist/step-fields.js +1 -1
- package/dist/step-fields.js.map +1 -1
- package/dist/steps-green.d.ts +27 -0
- package/dist/steps-green.d.ts.map +1 -0
- package/dist/steps-green.js +164 -0
- package/dist/steps-green.js.map +1 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +39 -8
- package/dist/validate.js.map +1 -1
- package/dist/worker-lease.d.ts +8 -1
- package/dist/worker-lease.d.ts.map +1 -1
- package/dist/worker-lease.js +34 -4
- package/dist/worker-lease.js.map +1 -1
- package/dist/worker.d.ts +8 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +17 -9
- package/dist/worker.js.map +1 -1
- package/package.json +3 -7
- package/src/agent-artifacts.ts +2 -10
- package/src/agent-cwd.ts +139 -0
- package/src/artifact-scan-policy.ts +51 -0
- package/src/authored-completion-record.ts +119 -0
- package/src/authored-completion.ts +203 -0
- package/src/authored-flow-error.ts +13 -1
- package/src/authored-flow-executor.ts +91 -58
- package/src/authored-human.ts +1 -4
- package/src/authored-node-entry.ts +4 -1
- package/src/authored-node-runner.ts +96 -22
- package/src/authored-root.ts +20 -2
- package/src/authored-step-output.ts +139 -42
- package/src/authored-verdict.ts +87 -0
- package/src/authored-worker-step.ts +60 -12
- package/src/cli/check-worker-surface.ts +66 -0
- package/src/cli/check.ts +35 -4
- package/src/cli/cloud-format.ts +82 -0
- package/src/cli/cloud-live.ts +366 -0
- package/src/cli/cloud-read.ts +13 -282
- package/src/cli/cloud-refusal.ts +113 -0
- package/src/cli/cloud-status-view.ts +213 -0
- package/src/cli/direct-run.ts +19 -2
- package/src/cli/hn-monitor.ts +2 -1
- package/src/cli/local-agent-remedy.ts +160 -0
- package/src/cli/observer-session.ts +107 -0
- package/src/cli/run.ts +233 -28
- package/src/cli/status.ts +59 -3
- package/src/cli/step-evidence.ts +310 -0
- package/src/cli/step-failure.ts +57 -81
- package/src/cli-commands.ts +2 -0
- package/src/cli.ts +64 -44
- package/src/cloud-read.ts +25 -1
- package/src/cloud-run-record.ts +64 -0
- package/src/cloud-run.ts +5 -24
- package/src/communication/local.ts +2 -1
- package/src/communication/worker.ts +13 -5
- package/src/compile.ts +28 -6
- package/src/direct-input.ts +22 -1
- package/src/failure-kinds.ts +104 -2
- package/src/flow-requirements.ts +24 -9
- package/src/gate-contract.ts +13 -1
- package/src/helper-preflight.ts +10 -3
- package/src/helper-reference.ts +130 -0
- package/src/index.ts +2 -0
- package/src/journal-client.ts +19 -2
- package/src/journal-projection.ts +106 -0
- package/src/llm-worker.ts +1 -1
- package/src/local-agent.ts +4 -2
- package/src/named-gate-lowering.ts +35 -5
- package/src/named-gate-preflight.ts +47 -0
- package/src/named-gates.ts +3 -0
- package/src/observer-link.ts +10 -1
- package/src/preflight.ts +7 -0
- package/src/protocol.ts +2 -0
- package/src/redact.ts +111 -0
- package/src/run-projection.ts +247 -0
- package/src/shell-word.ts +24 -0
- package/src/spec.ts +54 -3
- package/src/step-fields.ts +1 -1
- package/src/steps-green.ts +168 -0
- package/src/validate.ts +35 -8
- package/src/worker-lease.ts +35 -5
- package/src/worker.ts +27 -11
package/dist/worker.d.ts
CHANGED
|
@@ -9,6 +9,14 @@ export interface AgentWorkerOptions {
|
|
|
9
9
|
requiredStreams?: string[];
|
|
10
10
|
dataDir?: string;
|
|
11
11
|
onPtyReady?: (path: string) => void;
|
|
12
|
+
/**
|
|
13
|
+
* The run root a step's declared `cwd` is resolved against and held inside.
|
|
14
|
+
* Defaults to this process's working directory, which is what the run root
|
|
15
|
+
* is: `flows run` attaches the worker from the directory it was invoked in,
|
|
16
|
+
* and that directory is the uploaded tree on the Cloud path. Named here so
|
|
17
|
+
* the root a dispatch is measured against is a value, not an assumption.
|
|
18
|
+
*/
|
|
19
|
+
runRoot?: string;
|
|
12
20
|
}
|
|
13
21
|
/**
|
|
14
22
|
* Executes dispatched agent steps using their declared CLI.
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,eAAe,CAAC;AAW7D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAMzC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;gBAGvC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,kBAAkB;IAKxC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB7B;;;;;;;;OAQG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAQzB;YAEY,OAAO;CA0FtB;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAa9E"}
|
package/dist/worker.js
CHANGED
|
@@ -2,6 +2,7 @@ import { communicationInstruction } from './communication/spec.js';
|
|
|
2
2
|
import { workerSpend } from './worker-spend.js';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
import { runAgentCli } from './worker-cli.js';
|
|
5
|
+
import { agentStepCwd } from './agent-cwd.js';
|
|
5
6
|
import { resolveCliModel } from './cli-adapter.js';
|
|
6
7
|
import { withWorkerLease } from './worker-lease.js';
|
|
7
8
|
import { workerInstruction } from './worker-input.js';
|
|
@@ -77,7 +78,7 @@ export class AgentWorker extends EventEmitter {
|
|
|
77
78
|
if (dispatch.step_type !== 'agent')
|
|
78
79
|
return;
|
|
79
80
|
const running = this.execute(dispatch).catch((error) => {
|
|
80
|
-
this.emit('error', error);
|
|
81
|
+
this.emit('error', error, dispatch);
|
|
81
82
|
});
|
|
82
83
|
this.inFlight.add(running);
|
|
83
84
|
void running.finally(() => { this.inFlight.delete(running); });
|
|
@@ -96,18 +97,25 @@ export class AgentWorker extends EventEmitter {
|
|
|
96
97
|
if (!this.options.dataDir)
|
|
97
98
|
throw new Error('Agent communication requires a worker data directory');
|
|
98
99
|
const { completeCommunicationDispatch } = await import('./communication/worker.js');
|
|
99
|
-
await completeCommunicationDispatch(this.client, dispatch, communication, this.options.dataDir);
|
|
100
|
+
await completeCommunicationDispatch(this.client, dispatch, communication, this.options.dataDir, this.options.runRoot);
|
|
100
101
|
return;
|
|
101
102
|
}
|
|
102
103
|
let humanIntervention = false;
|
|
103
104
|
const effectiveModel = typeof spec.cli === 'string' ? resolveCliModel(spec.cli, spec.model) : spec.model;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
// Resolved here, before the lease, because this is the process that shares
|
|
106
|
+
// the agent's filesystem. A refusal completes the step the way a missing
|
|
107
|
+
// CLI does — journaled as `worker_error` with the reason — rather than
|
|
108
|
+
// spawning into a directory nobody established is inside the run root.
|
|
109
|
+
const cwd = agentStepCwd(spec, dispatch.step_id, this.options.runRoot);
|
|
110
|
+
const completed = await withWorkerLease(this.client, dispatch, signal => 'refusal' in cwd
|
|
111
|
+
? Promise.resolve({ exit_code: null, stdout_tail: '', stderr_tail: cwd.refusal })
|
|
112
|
+
: typeof spec.cli === 'string' && typeof spec.instruction === 'string'
|
|
113
|
+
? runAgentCli(spec.cli, workerInstruction(spec.instruction, dispatch), dispatch.wake_context, effectiveModel, undefined, signal, 'agent', this.options.dataDir === undefined ? undefined : {
|
|
114
|
+
dataDir: this.options.dataDir, runId: dispatch.run_id, stepId: dispatch.step_id, attempt: dispatch.attempt,
|
|
115
|
+
onReady: this.options.onPtyReady, onDrive: () => { humanIntervention = true; },
|
|
116
|
+
}, cwd.directory ?? (spec.transport === 'relay' ? undefined : this.options.runRoot), spec.transport === 'relay' ? 'relay' : 'direct', { runId: dispatch.run_id, stepId: dispatch.step_id, idempotencyKey: dispatch.idempotency_key,
|
|
117
|
+
dataDir: this.options.dataDir, resultSchema: spec.verification?.json_schema })
|
|
118
|
+
: Promise.resolve({ exit_code: null, stdout_tail: '', stderr_tail: 'agent step has no declared CLI' }));
|
|
111
119
|
const { result, usage } = workerSpend(completed, effectiveModel);
|
|
112
120
|
const completionReason = result.exit_code === 0 ? 'success' : 'worker_error';
|
|
113
121
|
// Output shape: if the CLI's stdout parses as JSON, promote THAT
|
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAyB,MAAM,uBAAuB,CAAC;AAEzG,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayflows/sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.31",
|
|
4
4
|
"description": "TypeScript-first authoring SDK for Relayflows. Compiles specs; speaks the journal protocol.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,8 +49,9 @@
|
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
50
50
|
"@relayfile/adapter-core": "0.6.2",
|
|
51
51
|
"@relayfile/relay-helpers": "0.4.12",
|
|
52
|
-
"@relayflows/surface": "2.0.
|
|
52
|
+
"@relayflows/surface": "2.0.31",
|
|
53
53
|
"@types/js-yaml": "^4.0.9",
|
|
54
|
+
"acorn": "^8.18.0",
|
|
54
55
|
"ai-hist": "0.4.1",
|
|
55
56
|
"ajv": "^8.17.1",
|
|
56
57
|
"ajv-draft-04": "^1.0.0",
|
|
@@ -80,10 +81,5 @@
|
|
|
80
81
|
"@agent-relay/sdk": {
|
|
81
82
|
"optional": true
|
|
82
83
|
}
|
|
83
|
-
},
|
|
84
|
-
"overrides": {
|
|
85
|
-
"@relayflows/surface": {
|
|
86
|
-
"@relayfile/relay-helpers": "$@relayfile/relay-helpers"
|
|
87
|
-
}
|
|
88
84
|
}
|
|
89
85
|
}
|
package/src/agent-artifacts.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
3
3
|
import { join, relative, sep } from 'node:path';
|
|
4
|
+
import { isUnscannedEntryName } from './artifact-scan-policy.js';
|
|
4
5
|
|
|
5
6
|
function isEnoent(error: unknown): boolean {
|
|
6
7
|
return error instanceof Error && (error as NodeJS.ErrnoException).code === 'ENOENT';
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
/**
|
|
10
|
-
* Entry names never walked and never reported, matched exactly, at any depth,
|
|
11
|
-
* before the entry's type is consulted — so a `.git` *file* (a worktree's
|
|
12
|
-
* gitdir pointer) is skipped as surely as a `.git` directory. Exact names, not
|
|
13
|
-
* prefixes: `.github`, `.relayflowd-notes` and every other author-chosen name
|
|
14
|
-
* that merely starts the same way stays eligible.
|
|
15
|
-
*/
|
|
16
|
-
const SKIPPED_ENTRY_NAMES = new Set(['.git', '.relayflowd', 'node_modules']);
|
|
17
|
-
|
|
18
10
|
/**
|
|
19
11
|
* A recursive snapshot of every regular file under `dir`, keyed by its
|
|
20
12
|
* `dir`-relative POSIX path, valued by a content signature (size + sha256).
|
|
@@ -55,7 +47,7 @@ async function walk(root: string, current: string, out: Map<string, string>): Pr
|
|
|
55
47
|
throw error;
|
|
56
48
|
}
|
|
57
49
|
for (const entry of entries) {
|
|
58
|
-
if (
|
|
50
|
+
if (isUnscannedEntryName(entry.name)) continue;
|
|
59
51
|
const path = join(current, entry.name);
|
|
60
52
|
if (entry.isDirectory()) {
|
|
61
53
|
await walk(root, path, out);
|
package/src/agent-cwd.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { realpathSync, statSync } from 'node:fs';
|
|
2
|
+
import { resolve, sep } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Where an agent step runs.
|
|
6
|
+
*
|
|
7
|
+
* A declared `cwd` is a path relative to the **run root** — the working
|
|
8
|
+
* directory of the process running the agent worker, which is the directory
|
|
9
|
+
* `flows run` was invoked from and, on the Cloud path, the uploaded tree
|
|
10
|
+
* (docs/CLOUD.md, "Code sync"). Absent, a step runs in the run root itself.
|
|
11
|
+
*
|
|
12
|
+
* Two checks, in two places, because they are two different facts:
|
|
13
|
+
*
|
|
14
|
+
* - The **declaration** is lexical and filesystem-free, so `flows check`, the
|
|
15
|
+
* kernel's `validate()` and this worker all answer identically for the same
|
|
16
|
+
* spec. `testdata/agent-cwd-cases.json` is the shared corpus both dialects
|
|
17
|
+
* are tested against.
|
|
18
|
+
* - The **target** is resolved here, in the worker that spawns the CLI — the
|
|
19
|
+
* one process provably sharing the agent's filesystem. A directory that
|
|
20
|
+
* exists, and whose symlink-free path is inside the symlink-free run root.
|
|
21
|
+
* Repeated at execution even when a preflight already passed, because the
|
|
22
|
+
* filesystem moves underneath a spec.
|
|
23
|
+
*
|
|
24
|
+
* This contains a declaration; it does not sandbox an agent. Nothing stops a
|
|
25
|
+
* CLI from writing outside the directory it was started in. Per-step scoping
|
|
26
|
+
* is `permissions`, which is recorded and not enforced (gate 8 / #442).
|
|
27
|
+
*/
|
|
28
|
+
export class AgentCwdError extends Error {
|
|
29
|
+
constructor(message: string) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'AgentCwdError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Why a declared `cwd` is not a run-root-relative path, or `undefined` when it
|
|
37
|
+
* is one. Pure and lexical: the exact rule
|
|
38
|
+
* `relayflowd_core::spec::is_run_root_relative_path` applies at the kernel
|
|
39
|
+
* boundary, so a declaration that passes `flows check` is not refused later by
|
|
40
|
+
* the daemon for its shape.
|
|
41
|
+
*/
|
|
42
|
+
export function agentCwdDeclarationError(cwd: unknown): string | undefined {
|
|
43
|
+
const expected = 'expected a run-root-relative path';
|
|
44
|
+
if (typeof cwd !== 'string') return `${expected} (got ${cwd === null ? 'null' : typeof cwd})`;
|
|
45
|
+
if (cwd === '') return `${expected}, not an empty string`;
|
|
46
|
+
if (PADDING.test(cwd)) return `${expected} without surrounding whitespace`;
|
|
47
|
+
if (cwd.includes('\0')) return `${expected} without NUL`;
|
|
48
|
+
if (cwd.startsWith('/')) return `${expected}, not an absolute path`;
|
|
49
|
+
if (cwd.includes('://')) return `${expected}, not a URI-like mount identity`;
|
|
50
|
+
if (cwd.split('/').some((part) => part === '' || part === '.' || part === '..')) {
|
|
51
|
+
return `${expected} without empty, "." or ".." components`;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Whitespace at either end, spelled so both dialects refuse the same set.
|
|
58
|
+
* JavaScript's `\s` is Unicode `White_Space` without U+0085 and with U+FEFF;
|
|
59
|
+
* Rust's `char::is_whitespace` is `White_Space` exactly. Naming U+0085 here
|
|
60
|
+
* and U+FEFF there makes both `White_Space ∪ {U+FEFF}`. Left to `trim()`
|
|
61
|
+
* alone, a path padded with either character would pass `flows check` and be
|
|
62
|
+
* refused by the kernel — the accept-then-refuse split this contract closes.
|
|
63
|
+
*/
|
|
64
|
+
const PADDING = /^[\s\u0085]|[\s\u0085]$/u;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The refusal for a `cwd` declared on a step dispatched over the relay
|
|
68
|
+
* transport, or `undefined` when there is nothing to refuse.
|
|
69
|
+
*
|
|
70
|
+
* The relay agent runs on another host. This process can neither resolve that
|
|
71
|
+
* host's filesystem nor establish that the directory is inside that run's
|
|
72
|
+
* tree, and forwarding the string as `worker_cwd` would let a declaration this
|
|
73
|
+
* contract promises to contain go unchecked. Fail closed instead of pretending.
|
|
74
|
+
*/
|
|
75
|
+
export function agentCwdTransportError(cwd: unknown, transport: unknown): string | undefined {
|
|
76
|
+
if (cwd === undefined || transport !== 'relay') return undefined;
|
|
77
|
+
return 'cwd is not supported with transport "relay": the agent runs on another host, '
|
|
78
|
+
+ 'where this worker cannot resolve the directory or hold it inside the run root';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The directory to spawn the CLI in: `root` when nothing is declared, else the
|
|
83
|
+
* symlink-free directory `cwd` names beneath `root`.
|
|
84
|
+
*
|
|
85
|
+
* @throws AgentCwdError naming the declaration and what is wrong with it.
|
|
86
|
+
*/
|
|
87
|
+
export function resolveAgentCwd(root: string, cwd: unknown, transport?: unknown): string | undefined {
|
|
88
|
+
if (cwd === undefined) return undefined;
|
|
89
|
+
const refusal = (detail: string): AgentCwdError =>
|
|
90
|
+
new AgentCwdError(`cwd ${JSON.stringify(cwd)}: ${detail}`);
|
|
91
|
+
const declaration = agentCwdDeclarationError(cwd);
|
|
92
|
+
if (declaration !== undefined) throw refusal(declaration);
|
|
93
|
+
const unsupported = agentCwdTransportError(cwd, transport);
|
|
94
|
+
if (unsupported !== undefined) throw new AgentCwdError(unsupported);
|
|
95
|
+
|
|
96
|
+
let realRoot: string;
|
|
97
|
+
try {
|
|
98
|
+
realRoot = realpathSync(root);
|
|
99
|
+
} catch {
|
|
100
|
+
throw refusal(`the run root ${JSON.stringify(root)} cannot be resolved on this host`);
|
|
101
|
+
}
|
|
102
|
+
let target: string;
|
|
103
|
+
try {
|
|
104
|
+
target = realpathSync(resolve(realRoot, cwd as string));
|
|
105
|
+
} catch {
|
|
106
|
+
throw refusal(`no such directory under the run root ${JSON.stringify(realRoot)}`);
|
|
107
|
+
}
|
|
108
|
+
// Component-aware, on the symlink-free forms of both: `checkout-b-old` is
|
|
109
|
+
// not inside `checkout-b`, and a symlink pointing out of the tree is not
|
|
110
|
+
// inside it either however it is spelled.
|
|
111
|
+
const prefix = realRoot.endsWith(sep) ? realRoot : realRoot + sep;
|
|
112
|
+
if (target !== realRoot && !target.startsWith(prefix)) {
|
|
113
|
+
throw refusal(`resolves to ${JSON.stringify(target)}, outside the run root ${JSON.stringify(realRoot)}`);
|
|
114
|
+
}
|
|
115
|
+
if (!statSync(target).isDirectory()) throw refusal(`${JSON.stringify(target)} is not a directory`);
|
|
116
|
+
return target;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Either the directory an agent step runs in, or why the step is refused. */
|
|
120
|
+
export type AgentCwdOutcome = { directory: string | undefined } | { refusal: string };
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The dispatch-time form: resolve a dispatched agent step's `cwd` against the
|
|
124
|
+
* run root, or report the refusal for the worker to complete the step with.
|
|
125
|
+
* Reporting rather than throwing keeps the refusal on the step's completion —
|
|
126
|
+
* `worker_error`, with the reason journaled — instead of on the worker.
|
|
127
|
+
*/
|
|
128
|
+
export function agentStepCwd(
|
|
129
|
+
spec: { cwd?: unknown; transport?: unknown },
|
|
130
|
+
stepId: string,
|
|
131
|
+
root: string = process.cwd(),
|
|
132
|
+
): AgentCwdOutcome {
|
|
133
|
+
try {
|
|
134
|
+
return { directory: resolveAgentCwd(root, spec.cwd, spec.transport) };
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (!(error instanceof AgentCwdError)) throw error;
|
|
137
|
+
return { refusal: `agent step "${stepId}": ${error.message}` };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bundled agent worker's artifact-scan policy, as a pure predicate.
|
|
3
|
+
*
|
|
4
|
+
* `agent-artifacts.ts` walks an agent step's cwd and records every regular
|
|
5
|
+
* file it finds, except entries named exactly `.git`, `.relayflowd` or
|
|
6
|
+
* `node_modules` — files as well as directories, in both cases. Dotfiles and
|
|
7
|
+
* dot-directories ARE eligible: `.workflow-artifacts/` is the conventional
|
|
8
|
+
* place a flow tells its agents to write. What that walk records is one
|
|
9
|
+
* source of the journaled `output.artifacts` list, which is the only thing an
|
|
10
|
+
* `artifact_exists` gate reads (`named-gate-lowering.ts` tests exact
|
|
11
|
+
* membership in that list and never touches disk).
|
|
12
|
+
*
|
|
13
|
+
* Static analysis needs the same rule without importing filesystem traversal,
|
|
14
|
+
* so the rule lives here and both consume it. It bounds the *scan*, not the
|
|
15
|
+
* list: `worker.ts` journals object-shaped JSON stdout and completed Relay
|
|
16
|
+
* task output verbatim instead of the scanned wrapper, and
|
|
17
|
+
* `JournalClient.stepComplete` accepts any `output` from any worker — so a
|
|
18
|
+
* path this predicate excludes can still reach the list by those routes
|
|
19
|
+
* (`named-gate-preflight.ts` warns rather than refusing for exactly that
|
|
20
|
+
* reason).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Entry names the walk never descends into or records, matched exactly, at
|
|
25
|
+
* any depth, before the entry's type is consulted — so a `.git` *file* (a
|
|
26
|
+
* worktree's gitdir pointer) is skipped as surely as a `.git` directory.
|
|
27
|
+
* Exact names, not prefixes: `.github`, `.relayflowd-notes` and every other
|
|
28
|
+
* author-chosen name that merely starts the same way stays eligible.
|
|
29
|
+
*/
|
|
30
|
+
const SKIPPED_ENTRY_NAMES = new Set(['.git', '.relayflowd', 'node_modules']);
|
|
31
|
+
|
|
32
|
+
export function isUnscannedEntryName(name: string): boolean {
|
|
33
|
+
return SKIPPED_ENTRY_NAMES.has(name);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The shortest `/`-prefix of a relative POSIX `path` the scan excludes, or
|
|
38
|
+
* `undefined` when every segment is scanned.
|
|
39
|
+
*
|
|
40
|
+
* Segments are compared exactly, with no normalization: the gate matches the
|
|
41
|
+
* author's literal string against the worker's literal list, so `node_modules`
|
|
42
|
+
* is excluded and `node_modules-copy` is not, and a backslash is an ordinary
|
|
43
|
+
* filename character rather than a separator. The whole prefix is returned,
|
|
44
|
+
* not the offending segment alone, because that prefix is the directory an
|
|
45
|
+
* author has to move the artifact out of.
|
|
46
|
+
*/
|
|
47
|
+
export function unscannedArtifactPrefix(path: string): string | undefined {
|
|
48
|
+
const segments = path.split('/');
|
|
49
|
+
const index = segments.findIndex(isUnscannedEntryName);
|
|
50
|
+
return index === -1 ? undefined : segments.slice(0, index + 1).join('/');
|
|
51
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// The authored verdict, written to the root BEFORE the marker is admitted.
|
|
2
|
+
//
|
|
3
|
+
// The terminal marker's command embeds the detail, and the marker run is
|
|
4
|
+
// opened under a stable admission key (`authored-child:<hash(root, step)>`).
|
|
5
|
+
// Those two facts together mean the detail has to be a durable decision, not a
|
|
6
|
+
// recomputed one: normalization redacts against `process.env`, so a credential
|
|
7
|
+
// rotated or removed while this process was down makes the SAME authored
|
|
8
|
+
// sentence normalize differently. A body resumed into that environment would
|
|
9
|
+
// retry the marker's admission key with a drifted spec, and the kernel refuses
|
|
10
|
+
// that as `run_admission_conflict` — losing an explanation the root had
|
|
11
|
+
// already journaled, for a flow whose every step succeeded.
|
|
12
|
+
//
|
|
13
|
+
// So the verdict is appended to a stream on the root run before the marker run
|
|
14
|
+
// is opened, and a later attempt reaching the same terminal step reuses what
|
|
15
|
+
// is recorded. That is exactly the durability the executor already gives a
|
|
16
|
+
// predicate gate (`applyPredicateGate`): record the decision first, so the
|
|
17
|
+
// spec built from it is identical on every attempt and author code is never
|
|
18
|
+
// asked the question twice.
|
|
19
|
+
//
|
|
20
|
+
// Nothing is recorded for a `done()` with no detail. Without one the marker
|
|
21
|
+
// command is a function of the reason alone — no environment, no drift, and
|
|
22
|
+
// nothing to recover — so a flow that does not opt in writes exactly the
|
|
23
|
+
// journal it has always written.
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
isDurableCompletionDetail,
|
|
27
|
+
isLoweredCompletion,
|
|
28
|
+
type LoweredCompletionReason,
|
|
29
|
+
} from './authored-completion.js';
|
|
30
|
+
import type { JournalClient } from './journal-client.js';
|
|
31
|
+
|
|
32
|
+
export const AUTHORED_VERDICT_STREAM = 'authored-verdict';
|
|
33
|
+
|
|
34
|
+
/** Versioned so a reader can tell this record from a future one. */
|
|
35
|
+
const RECORD_KIND = 'relayflows.authored-verdict.v1';
|
|
36
|
+
|
|
37
|
+
export interface AuthoredVerdictRecord {
|
|
38
|
+
readonly reason: LoweredCompletionReason;
|
|
39
|
+
/** Recorded only when the body passed one; see the module comment. */
|
|
40
|
+
readonly detail?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Commit this body's verdict, or recover the one this root already committed.
|
|
45
|
+
*
|
|
46
|
+
* Returns the verdict the marker must be built from — the recorded one when
|
|
47
|
+
* the root holds it, this attempt's otherwise. With no root run there is
|
|
48
|
+
* nothing durable to write to (the non-durable executor seam), so that path
|
|
49
|
+
* passes straight through without a round trip. A root run is always read
|
|
50
|
+
* first: a committed record stays authoritative even for an attempt that has
|
|
51
|
+
* no detail of its own — an optional source can be present on the first
|
|
52
|
+
* execution and gone on the retry, and "this attempt has no detail" is not
|
|
53
|
+
* "this root never recorded one". Only when no record exists AND this attempt
|
|
54
|
+
* has no detail does it return without appending, preserving the no-detail
|
|
55
|
+
* marker, spec hash, and journal exactly as before.
|
|
56
|
+
*/
|
|
57
|
+
export async function commitAuthoredVerdict(
|
|
58
|
+
journal: JournalClient,
|
|
59
|
+
rootRunId: string | undefined,
|
|
60
|
+
step: string,
|
|
61
|
+
verdict: AuthoredVerdictRecord,
|
|
62
|
+
): Promise<AuthoredVerdictRecord> {
|
|
63
|
+
if (rootRunId === undefined) return verdict;
|
|
64
|
+
const committed = await readAuthoredVerdict(journal, rootRunId, step);
|
|
65
|
+
if (committed !== undefined) return committed;
|
|
66
|
+
if (verdict.detail === undefined) return verdict;
|
|
67
|
+
await journal.streamAppend(rootRunId, AUTHORED_VERDICT_STREAM, {
|
|
68
|
+
verdict: RECORD_KIND, step, reason: verdict.reason, detail: verdict.detail,
|
|
69
|
+
});
|
|
70
|
+
return verdict;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The verdict this root recorded for `step`, or `undefined`.
|
|
75
|
+
*
|
|
76
|
+
* The FIRST valid record wins: it is the one the marker was — or was about to
|
|
77
|
+
* be — admitted from, and a later attempt's differently-redacted text must not
|
|
78
|
+
* displace it. Records this version does not recognise, and details that fail
|
|
79
|
+
* the durable check every other read boundary applies, are skipped rather than
|
|
80
|
+
* trusted; the marker then drifts and the kernel refuses it loudly, which is
|
|
81
|
+
* the right outcome for a record this process cannot read.
|
|
82
|
+
*/
|
|
83
|
+
export async function readAuthoredVerdict(
|
|
84
|
+
journal: JournalClient,
|
|
85
|
+
rootRunId: string,
|
|
86
|
+
step: string,
|
|
87
|
+
): Promise<AuthoredVerdictRecord | undefined> {
|
|
88
|
+
let offset = 0;
|
|
89
|
+
for (;;) {
|
|
90
|
+
const page = await journal.streamRead(rootRunId, AUTHORED_VERDICT_STREAM, offset, 1000);
|
|
91
|
+
for (const message of page.messages) {
|
|
92
|
+
// `stream.read` returns either the envelope or the bare message,
|
|
93
|
+
// matching how the executor reads predicate verdicts.
|
|
94
|
+
const raw = (message as { message?: unknown }).message ?? message;
|
|
95
|
+
if (isVerdictRecord(raw) && raw.step === step) {
|
|
96
|
+
return Object.freeze({ reason: raw.reason, detail: raw.detail });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (page.messages.length === 0 || page.next_offset <= offset) break;
|
|
100
|
+
offset = page.next_offset;
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface StoredVerdict {
|
|
106
|
+
readonly verdict: typeof RECORD_KIND;
|
|
107
|
+
readonly step: string;
|
|
108
|
+
readonly reason: LoweredCompletionReason;
|
|
109
|
+
readonly detail: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isVerdictRecord(value: unknown): value is StoredVerdict {
|
|
113
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
|
|
114
|
+
const record = value as Partial<StoredVerdict>;
|
|
115
|
+
return record.verdict === RECORD_KIND
|
|
116
|
+
&& typeof record.step === 'string' && record.step.length > 0
|
|
117
|
+
&& isLoweredCompletion(record.reason)
|
|
118
|
+
&& isDurableCompletionDetail(record.detail);
|
|
119
|
+
}
|