@hunterzhu/pulse-runtime 0.1.0

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 (93) hide show
  1. package/dist/context/builder.d.ts +68 -0
  2. package/dist/context/builder.js +127 -0
  3. package/dist/context/index.d.ts +2 -0
  4. package/dist/context/index.js +2 -0
  5. package/dist/context/merger.d.ts +25 -0
  6. package/dist/context/merger.js +125 -0
  7. package/dist/core/actions.d.ts +1 -0
  8. package/dist/core/actions.js +1 -0
  9. package/dist/core/errors.d.ts +8 -0
  10. package/dist/core/errors.js +36 -0
  11. package/dist/core/events.d.ts +10 -0
  12. package/dist/core/events.js +24 -0
  13. package/dist/core/factory.d.ts +35 -0
  14. package/dist/core/factory.js +27 -0
  15. package/dist/core/inbox.d.ts +119 -0
  16. package/dist/core/inbox.js +217 -0
  17. package/dist/core/mutations.d.ts +80 -0
  18. package/dist/core/mutations.js +127 -0
  19. package/dist/core/records.d.ts +1 -0
  20. package/dist/core/records.js +1 -0
  21. package/dist/core/types.d.ts +615 -0
  22. package/dist/core/types.js +109 -0
  23. package/dist/dependencies/graph.d.ts +25 -0
  24. package/dist/dependencies/graph.js +92 -0
  25. package/dist/dependencies/index.d.ts +1 -0
  26. package/dist/dependencies/index.js +1 -0
  27. package/dist/dsl/context-proxy.d.ts +20 -0
  28. package/dist/dsl/context-proxy.js +64 -0
  29. package/dist/dsl/index.d.ts +4 -0
  30. package/dist/dsl/index.js +4 -0
  31. package/dist/dsl/program.d.ts +314 -0
  32. package/dist/dsl/program.js +756 -0
  33. package/dist/dsl/session.d.ts +45 -0
  34. package/dist/dsl/session.js +93 -0
  35. package/dist/dsl/templates-index.d.ts +1 -0
  36. package/dist/dsl/templates-index.js +1 -0
  37. package/dist/dsl/templates.d.ts +85 -0
  38. package/dist/dsl/templates.js +110 -0
  39. package/dist/index.d.ts +15 -0
  40. package/dist/index.js +15 -0
  41. package/dist/lifecycle/index.d.ts +2 -0
  42. package/dist/lifecycle/index.js +2 -0
  43. package/dist/lifecycle/scopes.d.ts +38 -0
  44. package/dist/lifecycle/scopes.js +50 -0
  45. package/dist/lifecycle/watchdog.d.ts +16 -0
  46. package/dist/lifecycle/watchdog.js +66 -0
  47. package/dist/models/actions.d.ts +10 -0
  48. package/dist/models/actions.js +68 -0
  49. package/dist/models/index.d.ts +2 -0
  50. package/dist/models/index.js +2 -0
  51. package/dist/models/router.d.ts +187 -0
  52. package/dist/models/router.js +353 -0
  53. package/dist/scheduler/clock.d.ts +45 -0
  54. package/dist/scheduler/clock.js +92 -0
  55. package/dist/scheduler/decision.d.ts +72 -0
  56. package/dist/scheduler/decision.js +63 -0
  57. package/dist/scheduler/index.d.ts +6 -0
  58. package/dist/scheduler/index.js +6 -0
  59. package/dist/scheduler/locks.d.ts +18 -0
  60. package/dist/scheduler/locks.js +106 -0
  61. package/dist/scheduler/ready-queue.d.ts +32 -0
  62. package/dist/scheduler/ready-queue.js +40 -0
  63. package/dist/scheduler/runtime.d.ts +486 -0
  64. package/dist/scheduler/runtime.js +3445 -0
  65. package/dist/scheduler/telemetry.d.ts +111 -0
  66. package/dist/scheduler/telemetry.js +177 -0
  67. package/dist/scheduler/worker.d.ts +158 -0
  68. package/dist/scheduler/worker.js +744 -0
  69. package/dist/storage/artifacts.d.ts +17 -0
  70. package/dist/storage/artifacts.js +90 -0
  71. package/dist/storage/findings.d.ts +12 -0
  72. package/dist/storage/findings.js +70 -0
  73. package/dist/storage/index.d.ts +8 -0
  74. package/dist/storage/index.js +8 -0
  75. package/dist/storage/memory.d.ts +11 -0
  76. package/dist/storage/memory.js +21 -0
  77. package/dist/storage/mutation-log.d.ts +41 -0
  78. package/dist/storage/mutation-log.js +140 -0
  79. package/dist/storage/outbox.d.ts +30 -0
  80. package/dist/storage/outbox.js +59 -0
  81. package/dist/storage/persistence.d.ts +183 -0
  82. package/dist/storage/persistence.js +999 -0
  83. package/dist/storage/policy.d.ts +80 -0
  84. package/dist/storage/policy.js +268 -0
  85. package/dist/storage/session.d.ts +140 -0
  86. package/dist/storage/session.js +447 -0
  87. package/dist/tools/registry.d.ts +125 -0
  88. package/dist/tools/registry.js +308 -0
  89. package/dist/transitions/index.d.ts +2 -0
  90. package/dist/transitions/index.js +1 -0
  91. package/dist/transitions/validate.d.ts +4 -0
  92. package/dist/transitions/validate.js +1118 -0
  93. package/package.json +21 -0
@@ -0,0 +1,109 @@
1
+ export function provenanceRefId(ref) { return typeof ref === 'string' ? ref : ref.ref; }
2
+ export function provenanceRefKind(ref) { return typeof ref === 'string' ? 'legacy' : ref.kind; }
3
+ /**
4
+ * Park a cancel proposal without dropping a wait/control_error that already occupies
5
+ * `pendingResumeInput`. The parked proposals are promoted the next time the slot is free.
6
+ */
7
+ const MAX_CONTROL_PROPOSALS = 32;
8
+ function appendControlProposals(existing, extra) {
9
+ if ((existing?.length ?? 0) >= MAX_CONTROL_PROPOSALS)
10
+ return existing ?? [];
11
+ return [...(existing ?? []), ...extra].slice(0, MAX_CONTROL_PROPOSALS);
12
+ }
13
+ export function enqueueControlProposal(lane, proposal) {
14
+ if (lane.pendingResumeInput === undefined || lane.pendingResumeInput.type === 'control_proposal') {
15
+ const existing = lane.pendingResumeInput?.type === 'control_proposal' ? lane.pendingResumeInput.proposals : [];
16
+ lane.pendingResumeInput = { type: 'control_proposal', proposals: appendControlProposals(existing, [proposal]) };
17
+ return;
18
+ }
19
+ lane.pendingControlProposals = appendControlProposals(lane.pendingControlProposals, [proposal]);
20
+ }
21
+ /** Replace the resume slot. Existing control_proposal inputs are parked, then promoted if the slot ends up empty. */
22
+ export function replaceResumeInput(lane, input) {
23
+ if (lane.pendingResumeInput?.type === 'control_proposal' && input?.type !== 'control_proposal') {
24
+ lane.pendingControlProposals = appendControlProposals(lane.pendingControlProposals, lane.pendingResumeInput.proposals);
25
+ }
26
+ if (input === undefined)
27
+ delete lane.pendingResumeInput;
28
+ else
29
+ lane.pendingResumeInput = input;
30
+ if (lane.pendingResumeInput === undefined && (lane.pendingControlProposals?.length ?? 0) > 0) {
31
+ lane.pendingResumeInput = { type: 'control_proposal', proposals: lane.pendingControlProposals };
32
+ delete lane.pendingControlProposals;
33
+ }
34
+ }
35
+ export function globalContextRef(agentId, version) { return `global:${agentId}:${version}`; }
36
+ export function laneContextRef(laneId, version) { return `lane:${laneId}:${version}`; }
37
+ export function parseContextSnapshotRef(ref) {
38
+ const parts = ref.split(':');
39
+ if (parts[0] === 'global' && parts.length === 3 && parts[1] !== undefined && parts[2] !== undefined && /^\d+$/.test(parts[2]))
40
+ return { kind: 'global', agentId: parts[1], version: Number(parts[2]) };
41
+ if (parts[0] === 'global' && parts.length === 2 && parts[1] !== undefined && /^\d+$/.test(parts[1]))
42
+ return { kind: 'global', version: Number(parts[1]) };
43
+ if (parts[0] === 'lane' && parts.length === 3 && parts[1] !== undefined && parts[2] !== undefined && /^\d+$/.test(parts[2]))
44
+ return { kind: 'lane', laneId: parts[1], version: Number(parts[2]) };
45
+ return undefined;
46
+ }
47
+ export function privacyForContextSnapshot(state, lane, ref) {
48
+ const parsed = parseContextSnapshotRef(ref);
49
+ if (!parsed)
50
+ return undefined;
51
+ if (parsed.kind === 'global') {
52
+ const agent = state.agents.get(lane.agentId);
53
+ if (!agent || (parsed.agentId !== undefined && parsed.agentId !== agent.id) || !agent.globalVersions.has(parsed.version))
54
+ return undefined;
55
+ return structuredClone(agent.globalPrivacy?.get(parsed.version) ?? { privacy: 'public' });
56
+ }
57
+ if (parsed.laneId !== lane.id || parsed.version !== lane.context.version)
58
+ return undefined;
59
+ return { privacy: lane.context.privacy ?? 'public', ...(lane.context.privacyTaints === undefined ? {} : { privacyTaints: structuredClone(lane.context.privacyTaints) }) };
60
+ }
61
+ export function privacyMetadataForDerivedRef(state, lane, ref) {
62
+ const id = provenanceRefId(ref);
63
+ const kind = provenanceRefKind(ref);
64
+ const result = kind === 'artifact' ? undefined : state.results.get(id);
65
+ if (result)
66
+ return { privacy: result.privacy, ...(result.privacyTaints === undefined ? {} : { privacyTaints: structuredClone(result.privacyTaints) }) };
67
+ const artifact = kind === 'result' ? undefined : state.artifacts.get(id);
68
+ if (artifact && (artifact.agentId === undefined || artifact.agentId === lane.agentId))
69
+ return { privacy: artifact.privacy, ...(artifact.privacyTaints === undefined ? {} : { privacyTaints: structuredClone(artifact.privacyTaints) }) };
70
+ return typeof ref === 'string' ? privacyForContextSnapshot(state, lane, ref) : undefined;
71
+ }
72
+ export function privacyTaintsForDerivedRefs(state, lane, refs) {
73
+ const output = [];
74
+ const seen = new Set();
75
+ for (const ref of refs)
76
+ for (const taint of privacyMetadataForDerivedRef(state, lane, ref)?.privacyTaints ?? []) {
77
+ const value = { path: [provenanceRefId(ref), ...taint.path], privacy: taint.privacy };
78
+ const key = JSON.stringify(value);
79
+ if (!seen.has(key)) {
80
+ seen.add(key);
81
+ output.push(value);
82
+ }
83
+ }
84
+ return output;
85
+ }
86
+ export function createRuntimeState(maxTotalLanes = 64, options = {}) {
87
+ return { now: 0, agents: new Map(), lanes: new Map(), effects: new Map(), waits: new Map(), results: new Map(), artifacts: new Map(), toolCallCorrelations: new Map(), mergeProposals: new Map(), events: [], nextIds: { agent: 1, lane: 1, effect: 1, wait: 1, result: 1, artifact: 1, proposal: 1, event: 1 }, maxTotalLanes, maxQueuedEffects: options.maxQueuedEffects ?? 256, maxRunning: { llm: 4, tool: 16, agent: 4, none: Number.POSITIVE_INFINITY, ...(options.maxRunning ?? {}) }, forkAffinity: options.forkAffinity ?? 'advise', historySoftTokens: options.historySoftTokens ?? 8_000, historyHardTokens: options.historyHardTokens ?? 16_000, maxResultSummaryBytes: options.maxResultSummaryBytes ?? 4_096, trustedSanitizerIds: new Set(options.trustedSanitizerIds ?? []) };
88
+ }
89
+ export function privacyRank(label) { return label === 'public' ? 0 : label === 'cloud_allowed' ? 1 : 2; }
90
+ export function strictestPrivacy(labels) { return labels.reduce((current, next) => privacyRank(next) > privacyRank(current) ? next : current, 'public'); }
91
+ export function isSideEffectful(policy) { return policy === 'write' || policy === 'external'; }
92
+ export function privacyTaintPrivacy(taints) { return strictestPrivacy((taints ?? []).map((taint) => taint.privacy)); }
93
+ export function effectivePrivacy(base, taints) { return strictestPrivacy([base, privacyTaintPrivacy(taints)]); }
94
+ export function validatePrivacyTaints(value) {
95
+ if (value === undefined)
96
+ return undefined;
97
+ const paths = new Set();
98
+ for (const taint of value) {
99
+ if (!taint || !Array.isArray(taint.path) || taint.path.length === 0 || taint.path.some((part) => typeof part !== 'string' || part.length === 0))
100
+ return 'INVALID_PRIVACY_TAINT';
101
+ const key = JSON.stringify(taint.path);
102
+ if (paths.has(key))
103
+ return 'DUPLICATE_PRIVACY_TAINT';
104
+ paths.add(key);
105
+ if (taint.privacy !== 'public' && taint.privacy !== 'cloud_allowed' && taint.privacy !== 'local_only')
106
+ return 'INVALID_PRIVACY_TAINT';
107
+ }
108
+ return undefined;
109
+ }
@@ -0,0 +1,25 @@
1
+ import type { TargetRef } from '../core/types.js';
2
+ export interface DependencyEdge {
3
+ from: TargetRef;
4
+ to: TargetRef;
5
+ kind: 'wait' | 'ownership';
6
+ }
7
+ export declare class DependencyGraph {
8
+ private readonly edges;
9
+ private readonly reverse;
10
+ add(from: TargetRef, to: TargetRef, kind?: DependencyEdge['kind']): void;
11
+ remove(from: TargetRef, to: TargetRef): void;
12
+ hasCycle(): boolean;
13
+ stronglyConnectedComponents(): string[][];
14
+ }
15
+ export declare class WaitingIndex {
16
+ private readonly waits;
17
+ add(target: TargetRef, waitId: string): void;
18
+ remove(target: TargetRef, waitId: string): void;
19
+ waitingOn(target: TargetRef): string[];
20
+ }
21
+ export declare function detectDependencyCycle(edges: Array<{
22
+ from: TargetRef;
23
+ to: TargetRef;
24
+ kind?: 'wait' | 'ownership';
25
+ }>): boolean;
@@ -0,0 +1,92 @@
1
+ export class DependencyGraph {
2
+ edges = new Map();
3
+ reverse = new Map();
4
+ add(from, to, kind = 'wait') {
5
+ if (kind === 'ownership')
6
+ return;
7
+ const source = `${from.kind}:${from.id}`;
8
+ const target = `${to.kind}:${to.id}`;
9
+ if (!this.edges.has(source))
10
+ this.edges.set(source, new Set());
11
+ if (!this.reverse.has(target))
12
+ this.reverse.set(target, new Set());
13
+ this.edges.get(source).add(target);
14
+ this.reverse.get(target).add(source);
15
+ }
16
+ remove(from, to) {
17
+ this.edges.get(`${from.kind}:${from.id}`)?.delete(`${to.kind}:${to.id}`);
18
+ this.reverse.get(`${to.kind}:${to.id}`)?.delete(`${from.kind}:${from.id}`);
19
+ }
20
+ hasCycle() {
21
+ const visited = new Set();
22
+ const active = new Set();
23
+ const visit = (node) => {
24
+ if (active.has(node))
25
+ return true;
26
+ if (visited.has(node))
27
+ return false;
28
+ visited.add(node);
29
+ active.add(node);
30
+ for (const child of this.edges.get(node) ?? [])
31
+ if (visit(child))
32
+ return true;
33
+ active.delete(node);
34
+ return false;
35
+ };
36
+ return [...this.edges.keys()].some(visit);
37
+ }
38
+ stronglyConnectedComponents() {
39
+ let index = 0;
40
+ const indices = new Map();
41
+ const low = new Map();
42
+ const stack = [];
43
+ const onStack = new Set();
44
+ const components = [];
45
+ const visit = (node) => {
46
+ indices.set(node, index);
47
+ low.set(node, index);
48
+ index++;
49
+ stack.push(node);
50
+ onStack.add(node);
51
+ for (const child of this.edges.get(node) ?? []) {
52
+ if (!indices.has(child)) {
53
+ visit(child);
54
+ low.set(node, Math.min(low.get(node), low.get(child)));
55
+ }
56
+ else if (onStack.has(child))
57
+ low.set(node, Math.min(low.get(node), indices.get(child)));
58
+ }
59
+ if (low.get(node) === indices.get(node)) {
60
+ const component = [];
61
+ let popped = '';
62
+ do {
63
+ popped = stack.pop();
64
+ onStack.delete(popped);
65
+ component.push(popped);
66
+ } while (popped !== node);
67
+ components.push(component);
68
+ }
69
+ };
70
+ for (const node of new Set([...this.edges.keys(), ...this.reverse.keys()]))
71
+ if (!indices.has(node))
72
+ visit(node);
73
+ return components;
74
+ }
75
+ }
76
+ export class WaitingIndex {
77
+ waits = new Map();
78
+ add(target, waitId) {
79
+ const key = `${target.kind}:${target.id}`;
80
+ if (!this.waits.has(key))
81
+ this.waits.set(key, new Set());
82
+ this.waits.get(key).add(waitId);
83
+ }
84
+ remove(target, waitId) { this.waits.get(`${target.kind}:${target.id}`)?.delete(waitId); }
85
+ waitingOn(target) { return [...(this.waits.get(`${target.kind}:${target.id}`) ?? [])]; }
86
+ }
87
+ export function detectDependencyCycle(edges) {
88
+ const graph = new DependencyGraph();
89
+ for (const edge of edges)
90
+ graph.add(edge.from, edge.to, edge.kind ?? 'wait');
91
+ return graph.hasCycle();
92
+ }
@@ -0,0 +1 @@
1
+ export * from './graph.js';
@@ -0,0 +1 @@
1
+ export * from './graph.js';
@@ -0,0 +1,20 @@
1
+ import type { JsonValue } from '../core/types.js';
2
+ export interface DraftChanges {
3
+ value: JsonValue;
4
+ ops: Array<{
5
+ op: 'set';
6
+ path: string[];
7
+ value: JsonValue;
8
+ } | {
9
+ op: 'append';
10
+ path: string[];
11
+ value: JsonValue;
12
+ } | {
13
+ op: 'remove';
14
+ path: string[];
15
+ }>;
16
+ }
17
+ export declare function createDraftProxy<T extends Record<string, unknown>>(initial: T): {
18
+ draft: T;
19
+ changes(): DraftChanges;
20
+ };
@@ -0,0 +1,64 @@
1
+ import { isUnsafePathSegment } from '../context/builder.js';
2
+ /**
3
+ * Property names that would address the prototype chain are refused up front.
4
+ * Model-derived keys (e.g. `draft.byUser[modelOutput.id]`) must never be able to
5
+ * reach `Object.prototype`, neither in-process nor through the recorded ops.
6
+ */
7
+ function assertSafeKey(property) {
8
+ if (isUnsafePathSegment(property))
9
+ throw Object.assign(new Error(`UNSAFE_CONTEXT_PATH:${property}`), { code: 'UNSAFE_CONTEXT_PATH', retryable: false });
10
+ }
11
+ export function createDraftProxy(initial) {
12
+ const value = structuredClone(initial);
13
+ const ops = [];
14
+ const proxies = new WeakMap();
15
+ const wrap = (target, basePath) => {
16
+ const existing = proxies.get(target);
17
+ if (existing)
18
+ return existing;
19
+ const proxy = new Proxy(target, {
20
+ get(current, property, receiver) {
21
+ if (Array.isArray(current) && typeof property === 'string') {
22
+ if (property === 'push')
23
+ return (...items) => { for (const item of items) {
24
+ current.push(structuredClone(item));
25
+ ops.push({ op: 'append', path: basePath, value: structuredClone(item) });
26
+ } ; return current.length; };
27
+ if (property === 'splice')
28
+ return (start, deleteCount, ...items) => { const result = Array.prototype.splice.call(current, start, deleteCount ?? current.length - start, ...items.map((item) => structuredClone(item))); ops.push({ op: 'set', path: basePath, value: structuredClone(current) }); return result; };
29
+ if (property === 'sort')
30
+ return (compareFn) => { Array.prototype.sort.call(current, compareFn); ops.push({ op: 'set', path: basePath, value: structuredClone(current) }); return current; };
31
+ }
32
+ if (typeof property === 'string' && isUnsafePathSegment(property))
33
+ return undefined;
34
+ const next = Reflect.get(current, property, receiver);
35
+ return typeof property === 'string' && next !== null && typeof next === 'object' ? wrap(next, [...basePath, property]) : next;
36
+ },
37
+ set(current, property, next, receiver) {
38
+ if (typeof property === 'string') {
39
+ assertSafeKey(property);
40
+ if (Array.isArray(current) && (/^\d+$/.test(property) || property === 'length')) {
41
+ const result = Reflect.set(current, property, next, receiver);
42
+ if (property !== 'length')
43
+ ops.push({ op: 'set', path: basePath, value: structuredClone(current) });
44
+ return result;
45
+ }
46
+ ops.push({ op: 'set', path: [...basePath, property], value: structuredClone(next) });
47
+ }
48
+ return Reflect.set(current, property, next, receiver);
49
+ },
50
+ deleteProperty(current, property) {
51
+ if (typeof property === 'string')
52
+ assertSafeKey(property);
53
+ const result = Reflect.deleteProperty(current, property);
54
+ if (typeof property === 'string')
55
+ ops.push(Array.isArray(current) && /^\d+$/.test(property) ? { op: 'set', path: basePath, value: structuredClone(current) } : { op: 'remove', path: [...basePath, property] });
56
+ return result;
57
+ },
58
+ });
59
+ proxies.set(target, proxy);
60
+ return proxy;
61
+ };
62
+ const draft = wrap(value, []);
63
+ return { draft, changes: () => ({ value: value, ops: [...ops] }) };
64
+ }
@@ -0,0 +1,4 @@
1
+ export * from './program.js';
2
+ export * from './templates.js';
3
+ export * from './session.js';
4
+ export * from './context-proxy.js';
@@ -0,0 +1,4 @@
1
+ export * from './program.js';
2
+ export * from './templates.js';
3
+ export * from './session.js';
4
+ export * from './context-proxy.js';
@@ -0,0 +1,314 @@
1
+ import { z, type ZodTypeAny } from 'zod';
2
+ import type { LaneProgram } from '../scheduler/runtime.js';
3
+ import type { ContextDelta, JsonValue, LaneRecord, ResultRef, ProvenanceRef, RuntimeAction, ResumeInput, ProgressWatchdogState, ContextOp, LaneId, PrivacyLabel, RuntimeError, MergeProposal, ResourceLockSpec, Outcome, WaitResolution } from '../core/types.js';
4
+ import type { ProgramRef } from './templates.js';
5
+ export type NextStepTarget<TState = unknown> = string | {
6
+ step: string;
7
+ } | {
8
+ complete: {
9
+ value?: JsonValue;
10
+ privacy?: PrivacyLabel;
11
+ children?: 'reject_if_active' | 'cancel' | 'await';
12
+ };
13
+ } | {
14
+ fail: {
15
+ code: string;
16
+ message: string;
17
+ retryable?: boolean;
18
+ details?: JsonValue;
19
+ privacy?: PrivacyLabel;
20
+ derivedFrom?: ProvenanceRef[];
21
+ };
22
+ };
23
+ export type ScalarProjection<T> = T extends string | number | boolean | null ? T : T extends readonly unknown[] ? never : T extends object ? {
24
+ [K in keyof T]: T[K] extends string | number | boolean | null ? T[K] : never;
25
+ } : never;
26
+ export interface InstructionView<TState> {
27
+ goal: string;
28
+ state: ScalarProjection<TState>;
29
+ }
30
+ export interface StepInputs {
31
+ results?: ResultRef[];
32
+ findings?: ResultRef[];
33
+ artifacts?: string[];
34
+ events?: string[];
35
+ }
36
+ export interface HistoryCompactionOptions {
37
+ summarizeTask: string;
38
+ keepRecentRounds: number;
39
+ }
40
+ export interface HistoryRecordMeta {
41
+ seq: number;
42
+ hash: string;
43
+ effectId?: string;
44
+ resultRefs: ResultRef[];
45
+ resultSelection?: Array<{
46
+ ref: ResultRef;
47
+ rule: string;
48
+ hash: string;
49
+ }>;
50
+ result?: ResultRef;
51
+ findings?: ResultRef[];
52
+ privacy: PrivacyLabel;
53
+ privacyTaints?: import('../core/types.js').PrivacyTaint[];
54
+ }
55
+ export interface ResultMeta {
56
+ ref: ResultRef;
57
+ privacy: PrivacyLabel;
58
+ derivedFrom: ProvenanceRef[];
59
+ sizeBytes: number;
60
+ hash: string;
61
+ producer: {
62
+ kind: 'lane' | 'effect';
63
+ id: string;
64
+ };
65
+ summary?: JsonValue;
66
+ }
67
+ export interface StepContext<TState = JsonValue> {
68
+ lane: Readonly<LaneRecord>;
69
+ goal: string;
70
+ global: Readonly<JsonValue>;
71
+ globalVersion: number;
72
+ laneState: Readonly<TState>;
73
+ history: ReadonlyArray<HistoryRecordMeta>;
74
+ now: number;
75
+ watchdog?: ProgressWatchdogState;
76
+ resumeInput?: ResumeInput;
77
+ results: {
78
+ meta(ref: ResultRef): ResultMeta | undefined;
79
+ summary(ref: ResultRef): JsonValue | undefined;
80
+ };
81
+ mergeProposals: ReadonlyArray<MergeProposal>;
82
+ mutateLane(mutator: (draft: TState) => void): void;
83
+ proposeGlobal(delta: {
84
+ ops: ContextOp[] | ((draft: Record<string, JsonValue>) => void);
85
+ privacy?: PrivacyLabel;
86
+ }): void;
87
+ commitGlobal(delta: {
88
+ ops: ContextOp[] | ((draft: Record<string, JsonValue>) => void);
89
+ privacy?: PrivacyLabel;
90
+ adoptImmediately?: boolean;
91
+ }): void;
92
+ adoptContext(version: number | 'latest'): void;
93
+ cancelLane(target: LaneId, reason: 'SUPERSEDED' | 'USER_REQUESTED' | 'POLICY'): void;
94
+ proposeCancel(target: LaneId, reason: 'SUPERSEDED' | 'POLICY'): void;
95
+ trace(message: string | {
96
+ kind: string;
97
+ data?: JsonValue;
98
+ }): void;
99
+ }
100
+ export interface ForkProposalLane {
101
+ goal: string;
102
+ program: ProgramRef;
103
+ priority?: number;
104
+ contextVersion?: 'parent' | 'latest' | number;
105
+ affinityKey?: string;
106
+ resources?: ResourceLockSpec[];
107
+ inputResultRefs?: ResultRef[];
108
+ dependsOn?: Array<{
109
+ sibling: string;
110
+ condition: 'success' | 'settled';
111
+ }>;
112
+ }
113
+ export interface ForkProposal {
114
+ lanes: Record<string, ForkProposalLane>;
115
+ }
116
+ export interface ForkJoinOptions {
117
+ condition?: 'success' | 'settled';
118
+ onUnsatisfied?: 'fail_lane' | 'resume_with_error';
119
+ onCancelled?: 'unsatisfied' | 'ignore';
120
+ }
121
+ type Handler = (ctx: StepContext<any>) => {
122
+ actions?: RuntimeAction[];
123
+ next: NextStepTarget;
124
+ contextDelta?: ContextDelta | undefined;
125
+ adoptCommittedContext?: boolean;
126
+ locals?: JsonValue;
127
+ };
128
+ export interface LaneProgramDefinition extends LaneProgram {
129
+ id: string;
130
+ version: string;
131
+ system?: string;
132
+ toolSet?: string;
133
+ entry: string;
134
+ steps: string[];
135
+ debugSources?: string[];
136
+ }
137
+ type ErrorBoundaryHandler<TState> = (error: RuntimeError, ctx: StepContext<TState>) => NextStepTarget<TState> | {
138
+ fail: RuntimeError;
139
+ };
140
+ interface AffinityAdviceGroup {
141
+ keys: string[];
142
+ signals: string[];
143
+ }
144
+ export declare class StepBuilder<TState = JsonValue> {
145
+ readonly config: {
146
+ id: string;
147
+ version: string;
148
+ system?: string;
149
+ toolSet?: string;
150
+ state?: z.ZodType<TState>;
151
+ historyCompaction?: HistoryCompactionOptions;
152
+ };
153
+ readonly handlers: Map<string, Handler>;
154
+ /**
155
+ * History compaction is a macro boundary concern. Internal handlers such as
156
+ * `:submit`, `:decode`, `:join`, and `:resume` must consume the pending
157
+ * Wait input before the next compaction check can run.
158
+ */
159
+ private readonly compactionBoundaries;
160
+ private boundaryHandler?;
161
+ constructor(config: {
162
+ id: string;
163
+ version: string;
164
+ system?: string;
165
+ toolSet?: string;
166
+ state?: z.ZodType<TState>;
167
+ historyCompaction?: HistoryCompactionOptions;
168
+ });
169
+ addStep(name: string, handler: Handler): this;
170
+ onErrorBoundary(handler: ErrorBoundaryHandler<TState>): this;
171
+ addStructuredLLMStep<TOutput extends ZodTypeAny>(name: string, options: {
172
+ task: string;
173
+ instruction: string | ((view: InstructionView<TState>) => string);
174
+ schema: TOutput;
175
+ inputs?: (ctx: StepContext<TState>) => StepInputs;
176
+ requirements?: Record<string, JsonValue>;
177
+ executionPolicy?: {
178
+ duplicateExecutionPolicy: 'allow' | 'forbid';
179
+ maxUnknownAttempts: number;
180
+ };
181
+ retryPolicy?: {
182
+ maxAttempts: number;
183
+ initialBackoffMs: number;
184
+ maxBackoffMs: number;
185
+ jitter: boolean;
186
+ };
187
+ selfCorrect?: {
188
+ maxRounds: 0 | 1;
189
+ };
190
+ onSuccess: (data: z.infer<TOutput>, ctx: StepContext<TState>) => NextStepTarget<TState>;
191
+ onError?: (error: RuntimeError, ctx: StepContext<TState>) => NextStepTarget<TState>;
192
+ }): this;
193
+ addReActLoopStep(name: string, options: {
194
+ task?: string;
195
+ instruction: string | ((view: InstructionView<TState>) => string);
196
+ inputs?: (ctx: StepContext<TState>) => StepInputs;
197
+ toolAllow?: string[];
198
+ maxTurns?: number;
199
+ outputSchema?: ZodTypeAny;
200
+ requirements?: Record<string, JsonValue>;
201
+ toolApproval?: {
202
+ prompt: string | ((calls: JsonValue, ctx: StepContext<TState>) => string);
203
+ onDenied?: (reason: string, ctx: StepContext<TState>) => NextStepTarget<TState>;
204
+ };
205
+ onFinish: ((resultRef: ResultRef, ctx: StepContext<TState>) => NextStepTarget<TState>) | {
206
+ text: (resultRef: ResultRef, ctx: StepContext<TState>) => NextStepTarget<TState>;
207
+ structured?: {
208
+ schema: ZodTypeAny;
209
+ onParsed: (data: unknown, ctx: StepContext<TState>) => NextStepTarget<TState>;
210
+ };
211
+ };
212
+ onMaxTurns?: (ctx: StepContext<TState>) => NextStepTarget<TState>;
213
+ onError?: (error: RuntimeError, ctx: StepContext<TState>) => NextStepTarget<TState>;
214
+ }): this;
215
+ addParallelStep(name: string, options: {
216
+ lanes: Record<string, ForkProposalLane & {
217
+ dependsOn?: ForkProposalLane['dependsOn'] | Array<{
218
+ key: string;
219
+ target: {
220
+ local: string;
221
+ } | {
222
+ kind: 'lane' | 'effect';
223
+ id: string;
224
+ };
225
+ condition: 'success' | 'settled';
226
+ }>;
227
+ }>;
228
+ join?: ForkJoinOptions;
229
+ condition?: 'success' | 'settled';
230
+ mode?: 'all' | 'any' | 'quorum';
231
+ quorum?: number;
232
+ deadlineAt?: number;
233
+ affinity?: 'collapse' | 'ack';
234
+ next?: NextStepTarget;
235
+ onJoin?: (outcomes: Record<string, Outcome>, ctx: StepContext<TState>) => NextStepTarget;
236
+ }): this;
237
+ addDynamicForkStep(name: string, options: {
238
+ proposal?: (ctx: StepContext<TState>) => ForkProposal;
239
+ lanes?: (ctx: StepContext<TState>) => Record<string, ForkProposalLane>;
240
+ join?: ForkJoinOptions;
241
+ condition?: 'success' | 'settled';
242
+ mode?: 'all' | 'any' | 'quorum';
243
+ quorum?: number;
244
+ deadlineAt?: number;
245
+ affinity?: 'collapse' | 'ack' | ((groups: AffinityAdviceGroup[], ctx: StepContext<TState>) => 'collapse' | 'ack');
246
+ next?: NextStepTarget;
247
+ onJoin?: (outcomes: Map<string, Outcome>, ctx: StepContext<TState>) => NextStepTarget;
248
+ }): this;
249
+ addMergeStep(name: string, options: {
250
+ task?: string;
251
+ next?: NextStepTarget;
252
+ sources?: {
253
+ proposals?: 'joined' | LaneId[];
254
+ outcomes?: 'joined' | LaneId[];
255
+ };
256
+ instruction?: string | ((ctx: StepContext<TState>) => string);
257
+ schema?: ZodTypeAny;
258
+ onSynthesized?: (value: unknown, ctx: StepContext<TState>) => NextStepTarget;
259
+ onError?: (error: RuntimeError, ctx: StepContext<TState>) => NextStepTarget;
260
+ }): this;
261
+ addWaitStep(name: string, spec: {
262
+ dependencies: Array<{
263
+ key: string;
264
+ target: {
265
+ kind: 'lane' | 'effect';
266
+ id: string;
267
+ };
268
+ condition: 'success' | 'settled';
269
+ }>;
270
+ mode?: 'all' | 'any' | 'quorum';
271
+ quorum?: number;
272
+ deadlineAt?: number;
273
+ next: NextStepTarget;
274
+ } | {
275
+ targets: (ctx: StepContext<TState>) => Array<{
276
+ key: string;
277
+ target: {
278
+ kind: 'lane' | 'effect';
279
+ id: string;
280
+ };
281
+ condition: 'success' | 'settled';
282
+ }>;
283
+ mode?: 'all' | 'any' | 'quorum';
284
+ quorum?: number;
285
+ timeoutMs?: number;
286
+ onResolved: (resolution: WaitResolution, ctx: StepContext<TState>) => NextStepTarget<TState>;
287
+ onUnsatisfied?: (resolution: WaitResolution, ctx: StepContext<TState>) => NextStepTarget<TState>;
288
+ }): this;
289
+ addHumanStep<TOutput extends ZodTypeAny>(name: string, options: {
290
+ prompt: string | ((view: InstructionView<TState>) => string);
291
+ inputs?: (ctx: StepContext<TState>) => StepInputs;
292
+ schema: TOutput;
293
+ onReply: (reply: z.infer<TOutput>, ctx: StepContext<TState>) => NextStepTarget;
294
+ onTimeout?: (ctx: StepContext<TState>) => NextStepTarget;
295
+ timeoutMs?: number;
296
+ }): this;
297
+ addTimerStep(name: string, options: {
298
+ delayMs: number | ((ctx: StepContext<TState>) => number);
299
+ onFire: (ctx: StepContext<TState>) => NextStepTarget;
300
+ }): this;
301
+ build(entry?: string): LaneProgramDefinition;
302
+ }
303
+ export declare function defineLaneProgram<TState = JsonValue>(config: {
304
+ id: string;
305
+ version: string;
306
+ system?: string;
307
+ toolSet?: string;
308
+ state?: z.ZodType<TState>;
309
+ historyCompaction?: HistoryCompactionOptions;
310
+ }, define: (builder: StepBuilder<TState>) => void): LaneProgramDefinition;
311
+ /** Run a synchronous Step inside the development-only impurity boundary. */
312
+ export declare function withPureStepGuard<T>(callback: () => T): T;
313
+ export declare function assertProgramPure(program: LaneProgramDefinition | LaneProgram): void;
314
+ export {};