@relayflows/sdk 2.0.51 → 2.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/authored-budget.d.ts +1 -1
  2. package/dist/authored-budget.d.ts.map +1 -1
  3. package/dist/authored-budget.js +3 -3
  4. package/dist/authored-budget.js.map +1 -1
  5. package/dist/authored-flow-error.d.ts +3 -1
  6. package/dist/authored-flow-error.d.ts.map +1 -1
  7. package/dist/authored-flow-error.js.map +1 -1
  8. package/dist/authored-flow-executor.d.ts +2 -0
  9. package/dist/authored-flow-executor.d.ts.map +1 -1
  10. package/dist/authored-flow-executor.js +93 -12
  11. package/dist/authored-flow-executor.js.map +1 -1
  12. package/dist/authored-flow-lifecycle.d.ts +28 -3
  13. package/dist/authored-flow-lifecycle.d.ts.map +1 -1
  14. package/dist/authored-flow-lifecycle.js +206 -10
  15. package/dist/authored-flow-lifecycle.js.map +1 -1
  16. package/dist/authored-node-entry.js +2 -0
  17. package/dist/authored-node-entry.js.map +1 -1
  18. package/dist/authored-parallel-body.d.ts +10 -0
  19. package/dist/authored-parallel-body.d.ts.map +1 -0
  20. package/dist/authored-parallel-body.js +68 -0
  21. package/dist/authored-parallel-body.js.map +1 -0
  22. package/dist/authored-parallel-eligibility.d.ts +8 -0
  23. package/dist/authored-parallel-eligibility.d.ts.map +1 -0
  24. package/dist/authored-parallel-eligibility.js +287 -0
  25. package/dist/authored-parallel-eligibility.js.map +1 -0
  26. package/dist/authored-promise-graph.d.ts +16 -0
  27. package/dist/authored-promise-graph.d.ts.map +1 -1
  28. package/dist/authored-promise-graph.js +36 -0
  29. package/dist/authored-promise-graph.js.map +1 -1
  30. package/dist/authored-root-readback.d.ts +1 -2
  31. package/dist/authored-root-readback.d.ts.map +1 -1
  32. package/dist/authored-root-readback.js +1 -1
  33. package/dist/authored-root-readback.js.map +1 -1
  34. package/dist/authored-root.d.ts +6 -1
  35. package/dist/authored-root.d.ts.map +1 -1
  36. package/dist/authored-root.js +55 -8
  37. package/dist/authored-root.js.map +1 -1
  38. package/dist/authored-step-graph.d.ts +19 -4
  39. package/dist/authored-step-graph.d.ts.map +1 -1
  40. package/dist/authored-step-graph.js +49 -6
  41. package/dist/authored-step-graph.js.map +1 -1
  42. package/dist/authored-step-index.d.ts +2 -1
  43. package/dist/authored-step-index.d.ts.map +1 -1
  44. package/dist/authored-step-index.js +2 -1
  45. package/dist/authored-step-index.js.map +1 -1
  46. package/dist/authored-verdict.d.ts +2 -0
  47. package/dist/authored-verdict.d.ts.map +1 -1
  48. package/dist/authored-verdict.js +4 -1
  49. package/dist/authored-verdict.js.map +1 -1
  50. package/dist/authored-worker-step.d.ts +2 -1
  51. package/dist/authored-worker-step.d.ts.map +1 -1
  52. package/dist/authored-worker-step.js +12 -5
  53. package/dist/authored-worker-step.js.map +1 -1
  54. package/dist/cli/run.d.ts.map +1 -1
  55. package/dist/cli/run.js +4 -2
  56. package/dist/cli/run.js.map +1 -1
  57. package/dist/failure-kinds.d.ts +1 -1
  58. package/dist/failure-kinds.d.ts.map +1 -1
  59. package/dist/failure-kinds.js +2 -0
  60. package/dist/failure-kinds.js.map +1 -1
  61. package/dist/journal-client.d.ts +3 -1
  62. package/dist/journal-client.d.ts.map +1 -1
  63. package/dist/journal-client.js +18 -1
  64. package/dist/journal-client.js.map +1 -1
  65. package/package.json +3 -2
  66. package/src/authored-budget.ts +4 -3
  67. package/src/authored-flow-error.ts +2 -0
  68. package/src/authored-flow-executor.ts +85 -11
  69. package/src/authored-flow-lifecycle.ts +215 -9
  70. package/src/authored-node-entry.ts +2 -0
  71. package/src/authored-parallel-body.ts +67 -0
  72. package/src/authored-parallel-eligibility.ts +245 -0
  73. package/src/authored-promise-graph.ts +35 -0
  74. package/src/authored-root-readback.ts +1 -2
  75. package/src/authored-root.ts +62 -9
  76. package/src/authored-step-graph.ts +50 -5
  77. package/src/authored-step-index.ts +2 -1
  78. package/src/authored-verdict.ts +4 -1
  79. package/src/authored-worker-step.ts +12 -3
  80. package/src/cli/run.ts +4 -2
  81. package/src/failure-kinds.ts +2 -0
  82. package/src/journal-client.ts +19 -0
@@ -21,6 +21,8 @@ import type { JournalEvent } from './journal-reader.js';
21
21
 
22
22
  /** The `instruction` discriminator `authored-root.ts` writes into the root step. */
23
23
  export const AUTHORED_ROOT_KIND = 'relayflows.authored-root.v1';
24
+ /** Older CLIs do not recognize this root and must refuse a parallel rollback. */
25
+ export const AUTHORED_PARALLEL_ROOT_KIND = 'relayflows.authored-root.parallel.v2';
24
26
 
25
27
  export interface AuthoredVerdict {
26
28
  readonly reason: LoweredCompletionReason;
@@ -76,7 +78,8 @@ export function declaresAuthoredRoot(spawned: JournalEvent): boolean {
76
78
  if (step === null || step['id'] !== 'authored-root') return false;
77
79
  if (typeof step['instruction'] !== 'string') return false;
78
80
  try {
79
- return record(JSON.parse(step['instruction']))?.['kind'] === AUTHORED_ROOT_KIND;
81
+ const kind = record(JSON.parse(step['instruction']))?.['kind'];
82
+ return kind === AUTHORED_ROOT_KIND || kind === AUTHORED_PARALLEL_ROOT_KIND;
80
83
  } catch {
81
84
  return false;
82
85
  }
@@ -19,7 +19,7 @@ import { authoredChildAdmissionKey } from './authored-admission.js';
19
19
  import { alsoRecord, recordAuthoredChild } from './authored-step-index.js';
20
20
  import type { StepFailedDetails } from './failure-kinds.js';
21
21
  import { LOCAL_AGENT_WORKSPACE } from './local-agent-pins.js';
22
- import { authoredWorkerSlots, type AuthoredWorkerSlots } from './worker-slots.js';
22
+ import { authoredWorkerSlots, WorkerSlots, type AuthoredWorkerSlots } from './worker-slots.js';
23
23
  import { validateStepSecrets } from './step-secrets.js';
24
24
 
25
25
  const AGENT_TIMEOUT = Symbol('declared agent timeout');
@@ -356,8 +356,14 @@ export function authoredWorkerRunner(
356
356
  export function authoredDeterministicRunner(
357
357
  name: string, journal: JournalClient, journalSteps: AuthoredFlowJournalStep[], budget: AuthoredBudget,
358
358
  context: AuthoredStepContext = {},
359
+ parallel = false,
359
360
  ) {
360
361
  const rootRunId = context.rootRunId;
362
+ // run.start drives a deterministic child to completion before replying. The
363
+ // daemon reads one connection in order, so sharing the body's connection
364
+ // would serialize Promise.all. Each admitted child gets its own connection;
365
+ // this limit bounds both daemon threads and shell commands per authored body.
366
+ const slots = new WorkerSlots(8);
361
367
  // One lowering, two readers. `record` changes what the child run's verdict
362
368
  // MEANS, so the reader changes with it; everything before the read — spec,
363
369
  // admission key, budget, child index — is deliberately shared, because a
@@ -386,8 +392,10 @@ export function authoredDeterministicRunner(
386
392
  });
387
393
  return read(outcome);
388
394
  };
389
- if (terminal) return journal.runStart(spec, undefined, admissionKey).then(consume);
390
- return budget.execute(journal, spec, consume, admissionKey);
395
+ const admit = async () => terminal
396
+ ? consume(await journal.runStart(spec, undefined, admissionKey, false, parallel))
397
+ : budget.execute(journal, spec, consume, admissionKey, parallel);
398
+ return parallel ? slots.run(admit) : admit();
391
399
  }
392
400
  const lowerRun = (id: string, command: string, terminal = false, leaseMs?: number, verification?: NamedGate,
393
401
  secrets?: string[]): Promise<string> =>
@@ -398,6 +406,7 @@ export function authoredDeterministicRunner(
398
406
  secrets?: string[]): Promise<RecordedRunOutcome> =>
399
407
  lower(id, command, false, leaseMs, verification, 'record', secrets,
400
408
  outcome => readRecordedOutcome(journal, outcome, id, journalSteps, context));
409
+ lowerRun.stop = (reason: unknown): void => slots.close(reason);
401
410
  return lowerRun;
402
411
  }
403
412
 
package/src/cli/run.ts CHANGED
@@ -415,9 +415,11 @@ export async function resumeFlow(
415
415
  return { exitCode: 2, report: { ...base, runId, socketPath,
416
416
  diagnostics: [{ severity: 'refusal', kind: 'human_influenced_run', message: error.message.replace(/^human_influenced_run: /, '') }] } };
417
417
  }
418
- if (error instanceof AuthoredFlowExecutionError && error.code === 'unsupported_promise_lifecycle') {
418
+ if (error instanceof AuthoredFlowExecutionError
419
+ && (error.code === 'unsafe_parallel_runtime' || error.code === 'unsupported_promise_lifecycle')) {
419
420
  return { exitCode: 2, report: { ...base, runId, socketPath,
420
- diagnostics: [{ severity: 'refusal', kind: 'invalid_spec', message: error.message }] } };
421
+ diagnostics: [{ severity: 'refusal',
422
+ kind: error.code === 'unsafe_parallel_runtime' ? error.code : 'invalid_spec', message: error.message }] } };
421
423
  }
422
424
  if (error instanceof AuthoredFlowExecutionError
423
425
  && (error.code === 'helper_slack.credential_missing' || error.code === 'helper_slack.mount_required'
@@ -134,6 +134,8 @@ export const RUN_FAILURE_KINDS = [
134
134
  'run_parked',
135
135
  'run_declined',
136
136
  'run_unavailable',
137
+ /** A pinned parallel root is still active, but this process cannot safely dispatch it. */
138
+ 'unsafe_parallel_runtime',
137
139
  /** A predicate `.gate(fn)` judged false; the verdict is journaled as `<step>.gate`. */
138
140
  'gate_failed',
139
141
  /** `flows answer` named a wait the run is not asking: unknown, or already answered. */
@@ -135,7 +135,12 @@ export class JournalClient extends JournalConnection {
135
135
  reuseFromRunId?: string,
136
136
  admissionKey?: string,
137
137
  watch = false,
138
+ isolated = false,
138
139
  ): Promise<VerbContract['run.start']['result']> {
140
+ if (isolated) {
141
+ if (watch) throw new Error('an isolated run.start cannot keep a watch on its temporary session');
142
+ return this.runStartIsolated(spec, reuseFromRunId, admissionKey);
143
+ }
139
144
  for (const step of spec.steps) {
140
145
  for (const name of validateStepSecrets(step.secrets, process.env)) {
141
146
  if (this.withheld.has(name)) {
@@ -155,6 +160,20 @@ export class JournalClient extends JournalConnection {
155
160
  }, null);
156
161
  }
157
162
 
163
+ /** Drive a blocking deterministic child on its own socket, then close it. */
164
+ private async runStartIsolated(
165
+ spec: KernelRunSpec, reuseFromRunId?: string, admissionKey?: string,
166
+ ): Promise<VerbContract['run.start']['result']> {
167
+ const peer = this.createPeer();
168
+ try {
169
+ await peer.connect();
170
+ await peer.hello('flows-authored-deterministic');
171
+ return await peer.runStart(spec, reuseFromRunId, admissionKey);
172
+ } finally {
173
+ peer.close();
174
+ }
175
+ }
176
+
158
177
  /** §3 memoized resume. */
159
178
  runResume(runId: string, allowHumanInfluenced = false): Promise<VerbContract['run.resume']['result']> {
160
179
  return this.request('run.resume', {