@hunterzhu/pulse-runtime 0.1.5 → 0.1.7
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/context/builder.d.ts +6 -1
- package/dist/context/builder.js +37 -4
- package/dist/core/inbox.d.ts +6 -0
- package/dist/core/inbox.js +31 -7
- package/dist/core/mutations.d.ts +5 -1
- package/dist/core/mutations.js +5 -1
- package/dist/core/types.d.ts +26 -1
- package/dist/core/types.js +1 -1
- package/dist/dsl/program.d.ts +4 -1
- package/dist/dsl/program.js +147 -24
- package/dist/dsl/session.d.ts +7 -0
- package/dist/dsl/session.js +37 -10
- package/dist/dsl/templates.d.ts +2 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/scheduler/human-arbitration.d.ts +59 -0
- package/dist/scheduler/human-arbitration.js +62 -0
- package/dist/scheduler/runtime.d.ts +51 -3
- package/dist/scheduler/runtime.js +445 -40
- package/dist/scheduler/worker.d.ts +2 -0
- package/dist/scheduler/worker.js +24 -4
- package/dist/storage/session.d.ts +2 -1
- package/dist/storage/session.js +14 -0
- package/dist/transitions/validate.js +14 -4
- package/package.json +1 -1
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import type { AgentRecord, ArtifactRef, LaneRecord, LLMRequestProjection, PrivacyLabel, PrivacyTaint, ResultRef, RuntimeState, JsonValue, ContextDelta } from '../core/types.js';
|
|
1
|
+
import type { AgentRecord, ArtifactRef, ConversationMessage, LaneRecord, LLMRequestProjection, PrivacyLabel, PrivacyTaint, ResultRef, RuntimeState, JsonValue, ContextDelta } from '../core/types.js';
|
|
2
2
|
export interface ContextBuildInput {
|
|
3
3
|
agent: AgentRecord;
|
|
4
4
|
lane: LaneRecord;
|
|
5
5
|
resultRefs?: ResultRef[];
|
|
6
6
|
artifactRefs?: ArtifactRef[];
|
|
7
7
|
eventIds?: string[];
|
|
8
|
+
conversation?: ConversationMessage[];
|
|
8
9
|
instruction: string;
|
|
9
10
|
system?: string;
|
|
10
11
|
policy?: JsonValue;
|
|
11
12
|
tools?: JsonValue;
|
|
12
13
|
toolSetId: string;
|
|
13
14
|
}
|
|
15
|
+
export declare const MAX_DSL_INSTRUCTION_BYTES = 2048;
|
|
16
|
+
/** Keep large tool results from crowding out the actual task and conversation. */
|
|
17
|
+
export declare const MAX_INLINE_RESULT_BYTES = 4096;
|
|
18
|
+
export declare function assertDslInstructionSize(value: string): string;
|
|
14
19
|
export declare class ContextBuilder {
|
|
15
20
|
private readonly state;
|
|
16
21
|
readonly version = "1";
|
package/dist/context/builder.js
CHANGED
|
@@ -8,6 +8,35 @@ function stable(value) {
|
|
|
8
8
|
return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${stable(item)}`).join(',')}}`;
|
|
9
9
|
}
|
|
10
10
|
function hash(value) { return createHash('sha256').update(stable(value)).digest('hex'); }
|
|
11
|
+
export const MAX_DSL_INSTRUCTION_BYTES = 2_048;
|
|
12
|
+
/** Keep large tool results from crowding out the actual task and conversation. */
|
|
13
|
+
export const MAX_INLINE_RESULT_BYTES = 4_096;
|
|
14
|
+
function utf8Prefix(value, maxBytes) {
|
|
15
|
+
return Buffer.from(value, 'utf8').subarray(0, maxBytes).toString('utf8');
|
|
16
|
+
}
|
|
17
|
+
function projectResultValue(result) {
|
|
18
|
+
const value = result.value ?? null;
|
|
19
|
+
const originalBytes = Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
20
|
+
if (originalBytes <= MAX_INLINE_RESULT_BYTES)
|
|
21
|
+
return { value, summarized: false };
|
|
22
|
+
if (result.summary !== undefined)
|
|
23
|
+
return { value: result.summary, summarized: true, originalBytes };
|
|
24
|
+
return {
|
|
25
|
+
value: {
|
|
26
|
+
truncated: true,
|
|
27
|
+
originalBytes,
|
|
28
|
+
preview: utf8Prefix(JSON.stringify(value), MAX_INLINE_RESULT_BYTES),
|
|
29
|
+
note: 'The full result is retained by the runtime. Use this preview. Do not re-run the producing tool.',
|
|
30
|
+
},
|
|
31
|
+
summarized: true,
|
|
32
|
+
originalBytes,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function assertDslInstructionSize(value) {
|
|
36
|
+
if (Buffer.byteLength(value, 'utf8') > MAX_DSL_INSTRUCTION_BYTES)
|
|
37
|
+
throw Object.assign(new Error('Instruction exceeds the 2 KB DSL limit.'), { code: 'INSTRUCTION_TOO_LARGE', retryable: false });
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
11
40
|
export class ContextBuilder {
|
|
12
41
|
state;
|
|
13
42
|
version = '1';
|
|
@@ -15,8 +44,7 @@ export class ContextBuilder {
|
|
|
15
44
|
this.state = state;
|
|
16
45
|
}
|
|
17
46
|
build(input) {
|
|
18
|
-
|
|
19
|
-
throw new Error('INSTRUCTION_TOO_LARGE');
|
|
47
|
+
assertDslInstructionSize(input.instruction);
|
|
20
48
|
const global = input.agent.globalVersions.get(input.lane.contextSnapshotVersion);
|
|
21
49
|
if (global === undefined)
|
|
22
50
|
throw new Error('UNKNOWN_CONTEXT_VERSION');
|
|
@@ -55,15 +83,20 @@ export class ContextBuilder {
|
|
|
55
83
|
...effectiveResults.flatMap((item) => (item.result.privacyTaints ?? []).map((taint) => ({ path: [item.result.id, ...taint.path], privacy: taint.privacy }))),
|
|
56
84
|
...effectiveArtifacts.flatMap((item) => (item.artifact.privacyTaints ?? []).map((taint) => ({ path: [item.artifact.ref, ...taint.path], privacy: taint.privacy }))),
|
|
57
85
|
];
|
|
58
|
-
const
|
|
86
|
+
const conversation = input.conversation ?? [];
|
|
87
|
+
const contextSpec = { globalSnapshotVersion: input.lane.contextSnapshotVersion, laneSnapshotVersion: input.lane.context.version, resultRefs, ...(artifactRefs.length ? { artifactRefs } : {}), eventIds: input.eventIds ?? [], toolSetId: input.toolSetId, instruction: input.instruction, ...(conversation.length ? { conversation: structuredClone(conversation) } : {}), privacy, privacyRefs, ...(privacyTaints.length ? { privacyTaints } : {}) };
|
|
59
88
|
const prefixBlocks = [
|
|
60
89
|
{ kind: 'system', content: input.system ?? '' },
|
|
61
90
|
{ kind: 'policy', content: input.policy ?? {} },
|
|
62
91
|
{ kind: 'tools', content: input.tools ?? {} },
|
|
63
92
|
{ kind: 'global', content: global },
|
|
93
|
+
...(conversation.length ? [{ kind: 'conversation', content: structuredClone(conversation) }] : []),
|
|
64
94
|
{ kind: 'history', content: input.lane.context.history.map((record) => ({ seq: record.seq, ...(record.effectId === undefined ? {} : { effectId: record.effectId }), instruction: record.instruction, resultRefs: record.resultRefs, ...(record.resultSelection === undefined ? {} : { resultSelection: record.resultSelection }), ...(record.result === undefined ? {} : { result: record.result }), ...(record.findings === undefined ? {} : { findings: record.findings }), output: record.output, privacy: record.privacy, ...(record.privacyTaints === undefined ? {} : { privacyTaints: record.privacyTaints }) })) },
|
|
65
95
|
];
|
|
66
|
-
const blocks = [...prefixBlocks, { kind: 'lane', content: input.lane.context.state }, { kind: 'events', content: input.eventIds ?? [] }, { kind: 'results', content: results.map((result) =>
|
|
96
|
+
const blocks = [...prefixBlocks, { kind: 'lane', content: input.lane.context.state }, { kind: 'events', content: input.eventIds ?? [] }, { kind: 'results', content: results.map((result) => {
|
|
97
|
+
const projected = projectResultValue(result);
|
|
98
|
+
return { id: result.id, value: projected.value, ...(projected.summarized ? { summarized: true, ...(projected.originalBytes === undefined ? {} : { originalBytes: projected.originalBytes }) } : {}), ...(result.privacyTaints === undefined ? {} : { privacyTaints: result.privacyTaints.map((taint) => ({ path: [...taint.path], privacy: taint.privacy })) }) };
|
|
99
|
+
}) }, { kind: 'artifacts', content: artifacts.map((artifact) => ({ ref: artifact.ref, mediaType: artifact.mediaType, sizeBytes: artifact.sizeBytes, contentHash: artifact.contentHash })) }, { kind: 'instruction', content: input.instruction }];
|
|
67
100
|
return { contextSpec, blocks, prefixHash: hash(prefixBlocks), projectionHash: hash(blocks), builderVersion: this.version, policyVersion: '1', toolSetVersion: input.toolSetId, privacy, privacyRefs, ...(privacyTaints.length ? { privacyTaints } : {}) };
|
|
68
101
|
}
|
|
69
102
|
}
|
package/dist/core/inbox.d.ts
CHANGED
|
@@ -57,6 +57,8 @@ export interface FactInboxSnapshot<T extends JsonValue = JsonValue> {
|
|
|
57
57
|
nextSeq: number;
|
|
58
58
|
seen: string[];
|
|
59
59
|
queue: FactEnvelope<T>[];
|
|
60
|
+
/** Number of queued urgent entries at the head of `queue` (v2+). */
|
|
61
|
+
urgentCount?: number;
|
|
60
62
|
dedupeLedger?: FactInboxDedupeLedgerSnapshot;
|
|
61
63
|
}
|
|
62
64
|
export declare class FactInbox<T extends JsonValue = JsonValue> {
|
|
@@ -64,13 +66,17 @@ export declare class FactInbox<T extends JsonValue = JsonValue> {
|
|
|
64
66
|
private readonly seen;
|
|
65
67
|
private nextSeq;
|
|
66
68
|
private archivedThrough;
|
|
69
|
+
private urgentCount;
|
|
67
70
|
private dedupeArchive;
|
|
68
71
|
constructor(options?: {
|
|
69
72
|
dedupeArchive?: FactInboxDedupeArchive;
|
|
70
73
|
});
|
|
71
74
|
/** Attach a backend-provided archive before the first compaction. */
|
|
72
75
|
attachDedupeArchive(archive: FactInboxDedupeArchive): void;
|
|
76
|
+
private enqueueInternal;
|
|
73
77
|
enqueue(fact: T, eventId: string): FactEnvelope<T> | undefined;
|
|
78
|
+
/** Insert a host control fact ahead of already queued background work. */
|
|
79
|
+
enqueueUrgent(fact: T, eventId: string): FactEnvelope<T> | undefined;
|
|
74
80
|
drain(limit?: number): FactEnvelope<T>[];
|
|
75
81
|
get size(): number;
|
|
76
82
|
get dedupeWatermark(): number;
|
package/dist/core/inbox.js
CHANGED
|
@@ -14,6 +14,7 @@ export class FactInbox {
|
|
|
14
14
|
seen = new Map();
|
|
15
15
|
nextSeq = 1;
|
|
16
16
|
archivedThrough = 0;
|
|
17
|
+
urgentCount = 0;
|
|
17
18
|
dedupeArchive;
|
|
18
19
|
constructor(options = {}) {
|
|
19
20
|
this.dedupeArchive = options.dedupeArchive;
|
|
@@ -26,18 +27,28 @@ export class FactInbox {
|
|
|
26
27
|
throw new Error('FACT_INBOX_DEDUPE_ARCHIVE_ALREADY_COMPACTED');
|
|
27
28
|
this.dedupeArchive = archive;
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
enqueueInternal(fact, eventId, urgent) {
|
|
30
31
|
if (!eventId || this.seen.has(eventId) || this.dedupeArchive?.contains(eventId))
|
|
31
32
|
return undefined;
|
|
32
33
|
const envelope = { eventId, receivedSeq: this.nextSeq++, fact: structuredClone(fact) };
|
|
33
34
|
this.seen.set(eventId, envelope.receivedSeq);
|
|
34
|
-
|
|
35
|
+
if (urgent) {
|
|
36
|
+
this.queue.splice(this.urgentCount, 0, envelope);
|
|
37
|
+
this.urgentCount++;
|
|
38
|
+
}
|
|
39
|
+
else
|
|
40
|
+
this.queue.push(envelope);
|
|
35
41
|
return structuredClone(envelope);
|
|
36
42
|
}
|
|
43
|
+
enqueue(fact, eventId) { return this.enqueueInternal(fact, eventId, false); }
|
|
44
|
+
/** Insert a host control fact ahead of already queued background work. */
|
|
45
|
+
enqueueUrgent(fact, eventId) { return this.enqueueInternal(fact, eventId, true); }
|
|
37
46
|
drain(limit = Number.POSITIVE_INFINITY) {
|
|
38
47
|
if (limit !== Number.POSITIVE_INFINITY && (!Number.isInteger(limit) || limit < 0))
|
|
39
48
|
throw new Error('INVALID_FACT_DRAIN_LIMIT');
|
|
40
|
-
|
|
49
|
+
const drained = this.queue.splice(0, limit);
|
|
50
|
+
this.urgentCount = Math.max(0, this.urgentCount - drained.length);
|
|
51
|
+
return drained.map((envelope) => structuredClone(envelope));
|
|
41
52
|
}
|
|
42
53
|
get size() { return this.queue.length; }
|
|
43
54
|
get dedupeWatermark() { return this.archivedThrough; }
|
|
@@ -50,6 +61,7 @@ export class FactInbox {
|
|
|
50
61
|
nextSeq: this.nextSeq,
|
|
51
62
|
seen: [...this.seen.keys()],
|
|
52
63
|
queue: this.queue.map((envelope) => structuredClone(envelope)),
|
|
64
|
+
...(this.urgentCount === 0 ? {} : { urgentCount: this.urgentCount }),
|
|
53
65
|
dedupeLedger: {
|
|
54
66
|
schemaVersion: 1,
|
|
55
67
|
archivedThrough: this.archivedThrough,
|
|
@@ -117,10 +129,11 @@ export class FactInbox {
|
|
|
117
129
|
this.seen.set(eventId, receivedSeq);
|
|
118
130
|
this.nextSeq = restored.nextSeq;
|
|
119
131
|
this.archivedThrough = restored.archivedThrough;
|
|
132
|
+
this.urgentCount = restored.urgentCount;
|
|
120
133
|
}
|
|
121
134
|
static fromSnapshot(snapshot, options = {}) {
|
|
122
135
|
const value = snapshot;
|
|
123
|
-
if (!value || (value.schemaVersion !== 1 && value.schemaVersion !== 2) || !Number.isInteger(value.nextSeq) || value.nextSeq < 1 || !Array.isArray(value.seen) || value.seen.some((eventId) => typeof eventId !== 'string' || eventId.length === 0) || !Array.isArray(value.queue))
|
|
136
|
+
if (!value || (value.schemaVersion !== 1 && value.schemaVersion !== 2) || !Number.isInteger(value.nextSeq) || value.nextSeq < 1 || !Array.isArray(value.seen) || value.seen.some((eventId) => typeof eventId !== 'string' || eventId.length === 0) || !Array.isArray(value.queue) || (value.urgentCount !== undefined && (!Number.isInteger(value.urgentCount) || value.urgentCount < 0 || value.urgentCount > value.queue.length)))
|
|
124
137
|
throw new Error('INVALID_FACT_INBOX_SNAPSHOT');
|
|
125
138
|
if (new Set(value.seen).size !== value.seen.length)
|
|
126
139
|
throw new Error('INVALID_FACT_INBOX_SNAPSHOT');
|
|
@@ -151,8 +164,8 @@ export class FactInbox {
|
|
|
151
164
|
throw new Error('INVALID_FACT_INBOX_SNAPSHOT');
|
|
152
165
|
if (inbox.queue.some((candidate) => candidate.eventId === envelope.eventId || candidate.receivedSeq === envelope.receivedSeq))
|
|
153
166
|
throw new Error('INVALID_FACT_INBOX_SNAPSHOT');
|
|
154
|
-
|
|
155
|
-
|
|
167
|
+
// Urgent host facts may be inserted ahead of older entries, so queue
|
|
168
|
+
// order is intentionally different from received sequence order.
|
|
156
169
|
const knownSeq = seen.get(envelope.eventId);
|
|
157
170
|
if (knownSeq !== undefined && knownSeq !== envelope.receivedSeq)
|
|
158
171
|
throw new Error('INVALID_FACT_INBOX_SNAPSHOT');
|
|
@@ -161,14 +174,25 @@ export class FactInbox {
|
|
|
161
174
|
inbox.queue.push({ eventId: envelope.eventId, receivedSeq: envelope.receivedSeq, fact: structuredClone(envelope.fact) });
|
|
162
175
|
maxReceivedSeq = Math.max(maxReceivedSeq, envelope.receivedSeq);
|
|
163
176
|
}
|
|
177
|
+
// Preserve the serialized queue order. Urgent entries are allowed to have
|
|
178
|
+
// newer receivedSeq values ahead of background entries, but each priority
|
|
179
|
+
// band must retain its own FIFO order. This rejects ambiguous snapshots
|
|
180
|
+
// instead of silently normalizing a caller-provided reorder.
|
|
181
|
+
const urgentCount = value.urgentCount ?? 0;
|
|
182
|
+
const urgentSeqs = value.queue.slice(0, urgentCount).map((entry) => entry.receivedSeq);
|
|
183
|
+
const backgroundSeqs = value.queue.slice(urgentCount).map((entry) => entry.receivedSeq);
|
|
184
|
+
const isAscending = (seqs) => seqs.every((seq, index) => index === 0 || seq > seqs[index - 1]);
|
|
185
|
+
if (!isAscending(urgentSeqs) || !isAscending(backgroundSeqs))
|
|
186
|
+
throw new Error('INVALID_FACT_INBOX_SNAPSHOT');
|
|
164
187
|
if (value.nextSeq <= maxReceivedSeq)
|
|
165
188
|
throw new Error('INVALID_FACT_INBOX_SNAPSHOT');
|
|
166
189
|
for (const [eventId, receivedSeq] of seen)
|
|
167
190
|
inbox.seen.set(eventId, receivedSeq);
|
|
168
191
|
inbox.nextSeq = value.nextSeq;
|
|
192
|
+
inbox.urgentCount = urgentCount;
|
|
169
193
|
return inbox;
|
|
170
194
|
}
|
|
171
|
-
clear() { this.queue.length = 0; }
|
|
195
|
+
clear() { this.queue.length = 0; this.urgentCount = 0; }
|
|
172
196
|
}
|
|
173
197
|
export class ObservationInbox {
|
|
174
198
|
maxEntries;
|
package/dist/core/mutations.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RuntimeEventInput, RuntimeState, RuntimeError, ContextVersion, JsonValue, AgentRecord, LaneRecord, EffectRecord, WaitRecord, ResultRecord, FindingRecord, ArtifactRecord, LaneId, WaitId, EffectId, HistoryRecord, MergeProposal, ToolCallCorrelation, PrivacyMetadata } from './types.js';
|
|
1
|
+
import type { RuntimeEventInput, RuntimeState, RuntimeError, ContextVersion, JsonValue, AgentRecord, LaneRecord, EffectRecord, WaitRecord, ResultRecord, FindingRecord, ArtifactRecord, LaneId, WaitId, EffectId, HistoryRecord, MergeProposal, ToolCallCorrelation, PrivacyMetadata, HumanInputRecord } from './types.js';
|
|
2
2
|
export type Mutation = {
|
|
3
3
|
op: 'setAgent';
|
|
4
4
|
agentId: string;
|
|
@@ -36,6 +36,10 @@ export type Mutation = {
|
|
|
36
36
|
} | {
|
|
37
37
|
op: 'setToolCallCorrelation';
|
|
38
38
|
record: ToolCallCorrelation;
|
|
39
|
+
} | {
|
|
40
|
+
op: 'setHumanInput';
|
|
41
|
+
inputId: string;
|
|
42
|
+
record: HumanInputRecord;
|
|
39
43
|
} | {
|
|
40
44
|
op: 'insertMergeProposal';
|
|
41
45
|
proposal: MergeProposal;
|
package/dist/core/mutations.js
CHANGED
|
@@ -21,7 +21,7 @@ export function forkRuntimeStateForAdmission(state, mutations) {
|
|
|
21
21
|
for (const id of dirtyLanes) {
|
|
22
22
|
const lane = lanes.get(id);
|
|
23
23
|
if (lane)
|
|
24
|
-
lanes.set(id, { ...lane, ...(lane.visibleResultRefs === undefined ? {} : { visibleResultRefs: new Set(lane.visibleResultRefs) }) });
|
|
24
|
+
lanes.set(id, { ...lane, ...(lane.visibleResultRefs === undefined ? {} : { visibleResultRefs: new Set(lane.visibleResultRefs) }), ...(lane.pendingHumanInputs === undefined ? {} : { pendingHumanInputs: structuredClone(lane.pendingHumanInputs) }) });
|
|
25
25
|
}
|
|
26
26
|
return {
|
|
27
27
|
...state,
|
|
@@ -33,6 +33,7 @@ export function forkRuntimeStateForAdmission(state, mutations) {
|
|
|
33
33
|
artifacts: new Map(state.artifacts),
|
|
34
34
|
toolCallCorrelations: new Map(state.toolCallCorrelations),
|
|
35
35
|
mergeProposals: new Map(state.mergeProposals),
|
|
36
|
+
humanInputs: new Map(state.humanInputs),
|
|
36
37
|
events: state.events.slice(),
|
|
37
38
|
nextIds: { ...state.nextIds },
|
|
38
39
|
trustedSanitizerIds: new Set(state.trustedSanitizerIds),
|
|
@@ -91,6 +92,9 @@ export function apply(state, mutations, defaults = {}) {
|
|
|
91
92
|
case 'setToolCallCorrelation':
|
|
92
93
|
state.toolCallCorrelations.set(mutation.record.toolCallId, mutation.record);
|
|
93
94
|
break;
|
|
95
|
+
case 'setHumanInput':
|
|
96
|
+
state.humanInputs.set(mutation.inputId, mutation.record);
|
|
97
|
+
break;
|
|
94
98
|
case 'insertMergeProposal':
|
|
95
99
|
state.mergeProposals.set(mutation.proposal.id, mutation.proposal);
|
|
96
100
|
break;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -123,6 +123,8 @@ export interface LaneRecord {
|
|
|
123
123
|
resume: ResumePoint;
|
|
124
124
|
series?: SeriesLaneSpec;
|
|
125
125
|
pendingResumeInput?: ResumeInput;
|
|
126
|
+
/** Human messages steered into this lane while it was executing or waiting. */
|
|
127
|
+
pendingHumanInputs?: HumanInputRecord[];
|
|
126
128
|
/** Control proposals that arrived while another ResumeInput occupied the slot. */
|
|
127
129
|
pendingControlProposals?: ControlProposal[];
|
|
128
130
|
contextSnapshotVersion: ContextVersion;
|
|
@@ -319,6 +321,9 @@ export type ResumeInput = {
|
|
|
319
321
|
} | {
|
|
320
322
|
type: 'submitted';
|
|
321
323
|
targets: Record<string, TargetRef>;
|
|
324
|
+
} | {
|
|
325
|
+
type: 'human';
|
|
326
|
+
input: HumanInputRecord;
|
|
322
327
|
} | {
|
|
323
328
|
type: 'control_error';
|
|
324
329
|
error: RuntimeError;
|
|
@@ -499,14 +504,19 @@ export interface LLMContextSpec {
|
|
|
499
504
|
eventIds: string[];
|
|
500
505
|
toolSetId: string;
|
|
501
506
|
instruction: string;
|
|
507
|
+
conversation?: ConversationMessage[];
|
|
502
508
|
privacy: PrivacyLabel;
|
|
503
509
|
privacyRefs: ProvenanceRef[];
|
|
504
510
|
privacyTaints?: PrivacyTaint[];
|
|
505
511
|
}
|
|
512
|
+
export interface ConversationMessage {
|
|
513
|
+
role: 'system' | 'user' | 'assistant';
|
|
514
|
+
content: string;
|
|
515
|
+
}
|
|
506
516
|
export interface LLMRequestProjection {
|
|
507
517
|
contextSpec: LLMContextSpec;
|
|
508
518
|
blocks: Array<{
|
|
509
|
-
kind: 'system' | 'policy' | 'tools' | 'global' | 'history' | 'lane' | 'events' | 'results' | 'artifacts' | 'instruction';
|
|
519
|
+
kind: 'system' | 'policy' | 'tools' | 'global' | 'conversation' | 'history' | 'lane' | 'events' | 'results' | 'artifacts' | 'instruction';
|
|
510
520
|
content: JsonValue;
|
|
511
521
|
}>;
|
|
512
522
|
prefixHash: string;
|
|
@@ -559,6 +569,7 @@ export interface RuntimeState {
|
|
|
559
569
|
artifacts: Map<ArtifactRef, ArtifactRecord>;
|
|
560
570
|
toolCallCorrelations: Map<string, ToolCallCorrelation>;
|
|
561
571
|
mergeProposals: Map<string, MergeProposal>;
|
|
572
|
+
humanInputs: Map<string, HumanInputRecord>;
|
|
562
573
|
events: RuntimeEvent[];
|
|
563
574
|
eventsCompactedThrough?: number;
|
|
564
575
|
nextIds: {
|
|
@@ -598,6 +609,20 @@ export interface ToolCallCorrelation {
|
|
|
598
609
|
toolEffectId: EffectId;
|
|
599
610
|
resultRef?: ResultRef;
|
|
600
611
|
}
|
|
612
|
+
/** Durable external input accepted while other Effects are still running. */
|
|
613
|
+
export interface HumanInputRecord {
|
|
614
|
+
id: string;
|
|
615
|
+
agentId: AgentId;
|
|
616
|
+
value: JsonValue;
|
|
617
|
+
receivedAt: number;
|
|
618
|
+
status: 'pending' | 'consumed' | 'deferred';
|
|
619
|
+
targetEffectId?: EffectId;
|
|
620
|
+
handledByLaneId?: LaneId;
|
|
621
|
+
decision?: 'respond' | 'steer' | 'spawn' | 'defer' | 'cancel';
|
|
622
|
+
decisionReason?: string;
|
|
623
|
+
decisionModelId?: string;
|
|
624
|
+
decidedAt?: number;
|
|
625
|
+
}
|
|
601
626
|
export declare function createRuntimeState(maxTotalLanes?: number, options?: {
|
|
602
627
|
maxQueuedEffects?: number;
|
|
603
628
|
maxRunning?: Partial<Record<ConcurrencyClass, number>>;
|
package/dist/core/types.js
CHANGED
|
@@ -84,7 +84,7 @@ export function privacyTaintsForDerivedRefs(state, lane, refs) {
|
|
|
84
84
|
return output;
|
|
85
85
|
}
|
|
86
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 ?? []) };
|
|
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(), humanInputs: 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
88
|
}
|
|
89
89
|
export function privacyRank(label) { return label === 'public' ? 0 : label === 'cloud_allowed' ? 1 : 2; }
|
|
90
90
|
export function strictestPrivacy(labels) { return labels.reduce((current, next) => privacyRank(next) > privacyRank(current) ? next : current, 'public'); }
|
package/dist/dsl/program.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z, type ZodTypeAny } from 'zod';
|
|
2
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';
|
|
3
|
+
import type { ContextDelta, ConversationMessage, JsonValue, LaneRecord, ResultRef, ProvenanceRef, RuntimeAction, ResumeInput, ProgressWatchdogState, ContextOp, LaneId, PrivacyLabel, RuntimeError, MergeProposal, ResourceLockSpec, Outcome, WaitResolution, HumanInputRecord } from '../core/types.js';
|
|
4
4
|
import type { ProgramRef } from './templates.js';
|
|
5
5
|
import type { RuntimeToolDiscoveryQuery } from '../tools/registry.js';
|
|
6
6
|
export type NextStepTarget<TState = unknown> = string | {
|
|
@@ -33,11 +33,13 @@ export interface StepInputs {
|
|
|
33
33
|
findings?: ResultRef[];
|
|
34
34
|
artifacts?: string[];
|
|
35
35
|
events?: string[];
|
|
36
|
+
conversation?: ConversationMessage[];
|
|
36
37
|
toolDiscovery?: RuntimeToolDiscoveryQuery;
|
|
37
38
|
}
|
|
38
39
|
export interface HistoryCompactionOptions {
|
|
39
40
|
summarizeTask: string;
|
|
40
41
|
keepRecentRounds: number;
|
|
42
|
+
instruction?: string;
|
|
41
43
|
}
|
|
42
44
|
export interface HistoryRecordMeta {
|
|
43
45
|
seq: number;
|
|
@@ -76,6 +78,7 @@ export interface StepContext<TState = JsonValue> {
|
|
|
76
78
|
now: number;
|
|
77
79
|
watchdog?: ProgressWatchdogState;
|
|
78
80
|
resumeInput?: ResumeInput;
|
|
81
|
+
humanInputs?: readonly HumanInputRecord[];
|
|
79
82
|
results: {
|
|
80
83
|
meta(ref: ResultRef): ResultMeta | undefined;
|
|
81
84
|
summary(ref: ResultRef): JsonValue | undefined;
|