@llblab/pi-kit 0.6.0 → 0.7.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.
- package/CHANGELOG.md +4 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-actors/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-actors/CHANGELOG.md +6 -0
- package/node_modules/@llblab/pi-actors/README.md +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +3 -0
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +14 -1
- package/node_modules/@llblab/pi-actors/dist/lib/command-templates.js +45 -3
- package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +16 -3
- package/node_modules/@llblab/pi-actors/dist/lib/observability.js +92 -7
- package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +0 -1
- package/node_modules/@llblab/pi-actors/dist/lib/pi.js +15 -24
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.d.ts +17 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.js +44 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.d.ts +4 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.js +102 -4
- package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +58 -39
- package/node_modules/@llblab/pi-actors/dist/lib/runtime.js +14 -6
- package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/async-runs.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +18 -23
- package/node_modules/@llblab/pi-actors/lib/async-runs.ts +18 -1
- package/node_modules/@llblab/pi-actors/lib/command-templates.ts +41 -3
- package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +1 -1
- package/node_modules/@llblab/pi-actors/lib/observability.ts +119 -5
- package/node_modules/@llblab/pi-actors/lib/pi.ts +15 -28
- package/node_modules/@llblab/pi-actors/lib/run-delivery-lineage.ts +68 -0
- package/node_modules/@llblab/pi-actors/lib/run-delivery.ts +120 -4
- package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +69 -44
- package/node_modules/@llblab/pi-actors/lib/runtime.ts +17 -6
- package/node_modules/@llblab/pi-actors/package.json +1 -1
- package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +33 -12
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +125 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +87 -45
- package/node_modules/@llblab/pi-state-flow/README.md +203 -107
- package/node_modules/@llblab/pi-state-flow/docs/README.md +4 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +188 -0
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +42 -0
- package/node_modules/@llblab/pi-state-flow/index.ts +164 -2
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +138 -0
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +273 -0
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +48 -0
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +18 -5
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +268 -0
- package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +117 -0
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +562 -0
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +24 -12
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +606 -70
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +666 -0
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +95 -0
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/maintenance.ts +141 -0
- package/node_modules/@llblab/pi-state-flow/lib/memory.ts +52 -0
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +88 -0
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +296 -0
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +23 -7
- package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +79 -0
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +264 -0
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +6 -0
- package/node_modules/@llblab/pi-state-flow/lib/skills.ts +99 -7
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +305 -48
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +74 -7
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +125 -6
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +196 -0
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +233 -0
- package/node_modules/@llblab/pi-state-flow/lib/terminal.ts +70 -24
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +254 -29
- package/node_modules/@llblab/pi-state-flow/package.json +8 -2
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +128 -0
- package/node_modules/@llblab/pi-telegram/AGENTS.md +13 -8
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +20 -3
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
- package/node_modules/@llblab/pi-telegram/README.md +12 -8
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +220 -18
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +70 -19
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +6 -6
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +12 -5
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +3 -1
- package/node_modules/@llblab/pi-telegram/index.ts +4 -1418
- package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +6 -3
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +28 -1
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +600 -135
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +962 -55
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +350 -26
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +544 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +234 -11
- package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -24
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1792 -0
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +20 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +2184 -126
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +38 -1
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +152 -13
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +51 -10
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +29 -0
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +85 -17
- package/node_modules/@llblab/pi-telegram/lib/prompts.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +52 -18
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +305 -112
- package/node_modules/@llblab/pi-telegram/lib/status.ts +10 -0
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +308 -39
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +279 -4
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +664 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +226 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +118 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1686 -129
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1319 -97
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +1643 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +968 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +84 -0
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/screenshot.png +0 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-bus.mjs +83 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +101 -0
- package/node_modules/@llblab/{skills → pi-telegram/skills}/show-me/SKILL.md +28 -6
- package/node_modules/@llblab/pi-telegram/skills/show-me/references/telegram-surfaces.md +43 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/delivery-and-threads.md +1 -1
- package/node_modules/@llblab/skills/package.json +2 -3
- package/package.json +6 -5
|
@@ -11,6 +11,10 @@ import * as Limits from "./limits.js";
|
|
|
11
11
|
const RUN_DELIVERY_SCHEMA = "run-delivery-v1";
|
|
12
12
|
const MEMBER_FIELDS = new Set([
|
|
13
13
|
"artifacts",
|
|
14
|
+
"parent_run",
|
|
15
|
+
"parent_run_instance_id",
|
|
16
|
+
"parent_state_dir",
|
|
17
|
+
"output",
|
|
14
18
|
"run",
|
|
15
19
|
"run_instance_id",
|
|
16
20
|
"state_dir",
|
|
@@ -107,8 +111,32 @@ function parseMember(value) {
|
|
|
107
111
|
const record = asRecord(value, "Completion batch member");
|
|
108
112
|
assertExactFields(record, MEMBER_FIELDS, "Completion batch member");
|
|
109
113
|
const parsedArtifacts = artifacts(record.artifacts);
|
|
114
|
+
const parentRun = record.parent_run === undefined
|
|
115
|
+
? undefined
|
|
116
|
+
: boundedString(record.parent_run, "Completion batch member parent_run", 120);
|
|
117
|
+
const parentRunInstanceId = record.parent_run_instance_id === undefined
|
|
118
|
+
? undefined
|
|
119
|
+
: boundedString(record.parent_run_instance_id, "Completion batch member parent_run_instance_id", 256);
|
|
120
|
+
const parentStateDir = record.parent_state_dir === undefined
|
|
121
|
+
? undefined
|
|
122
|
+
: boundedString(record.parent_state_dir, "Completion batch member parent_state_dir", 4_096);
|
|
123
|
+
if (Boolean(parentRun) !== Boolean(parentRunInstanceId) ||
|
|
124
|
+
Boolean(parentRun) !== Boolean(parentStateDir)) {
|
|
125
|
+
throw new Error("Completion batch member parent lineage is incomplete");
|
|
126
|
+
}
|
|
127
|
+
const output = record.output === undefined
|
|
128
|
+
? undefined
|
|
129
|
+
: boundedString(record.output, "Completion batch member output", 4_000);
|
|
110
130
|
return {
|
|
111
131
|
...(parsedArtifacts ? { artifacts: parsedArtifacts } : {}),
|
|
132
|
+
...(output ? { output } : {}),
|
|
133
|
+
...(parentRun
|
|
134
|
+
? {
|
|
135
|
+
parent_run: parentRun,
|
|
136
|
+
parent_run_instance_id: parentRunInstanceId,
|
|
137
|
+
parent_state_dir: parentStateDir,
|
|
138
|
+
}
|
|
139
|
+
: {}),
|
|
112
140
|
run: boundedString(record.run, "Completion batch member run", 120),
|
|
113
141
|
run_instance_id: boundedString(record.run_instance_id, "Completion batch member run_instance_id", 256),
|
|
114
142
|
state_dir: boundedString(record.state_dir, "Completion batch member state_dir", 4_096),
|
|
@@ -566,13 +594,79 @@ function compactModelText(value, limit) {
|
|
|
566
594
|
const compact = value.replaceAll(/\s+/g, " ").replaceAll("`", "'").trim();
|
|
567
595
|
return compact.length > limit ? `${compact.slice(0, limit - 1)}…` : compact;
|
|
568
596
|
}
|
|
569
|
-
function
|
|
597
|
+
function memberIdentity(member) {
|
|
598
|
+
return `${member.state_dir}\0${member.run_instance_id}`;
|
|
599
|
+
}
|
|
600
|
+
function completionDepth(member, byIdentity) {
|
|
601
|
+
const visited = new Set();
|
|
602
|
+
let current = member;
|
|
603
|
+
let depth = 0;
|
|
604
|
+
while (current.parent_state_dir && current.parent_run_instance_id) {
|
|
605
|
+
const identity = `${current.parent_state_dir}\0${current.parent_run_instance_id}`;
|
|
606
|
+
if (visited.has(identity))
|
|
607
|
+
break;
|
|
608
|
+
visited.add(identity);
|
|
609
|
+
const parent = byIdentity.get(identity);
|
|
610
|
+
if (!parent)
|
|
611
|
+
break;
|
|
612
|
+
depth += 1;
|
|
613
|
+
current = parent;
|
|
614
|
+
}
|
|
615
|
+
return depth;
|
|
616
|
+
}
|
|
617
|
+
function orderCompletionForest(members) {
|
|
618
|
+
const byIdentity = new Map(members.map((member) => [memberIdentity(member), member]));
|
|
619
|
+
const children = new Map();
|
|
620
|
+
const roots = [];
|
|
621
|
+
for (const member of members) {
|
|
622
|
+
const parentIdentity = member.parent_state_dir && member.parent_run_instance_id
|
|
623
|
+
? `${member.parent_state_dir}\0${member.parent_run_instance_id}`
|
|
624
|
+
: undefined;
|
|
625
|
+
if (!parentIdentity || !byIdentity.has(parentIdentity)) {
|
|
626
|
+
roots.push(member);
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
const siblings = children.get(parentIdentity) ?? [];
|
|
630
|
+
siblings.push(member);
|
|
631
|
+
children.set(parentIdentity, siblings);
|
|
632
|
+
}
|
|
633
|
+
const ordered = [];
|
|
634
|
+
const visited = new Set();
|
|
635
|
+
const visit = (member) => {
|
|
636
|
+
const identity = memberIdentity(member);
|
|
637
|
+
if (visited.has(identity))
|
|
638
|
+
return;
|
|
639
|
+
visited.add(identity);
|
|
640
|
+
ordered.push(member);
|
|
641
|
+
for (const child of children.get(identity) ?? [])
|
|
642
|
+
visit(child);
|
|
643
|
+
};
|
|
644
|
+
for (const root of roots)
|
|
645
|
+
visit(root);
|
|
646
|
+
for (const member of members)
|
|
647
|
+
visit(member);
|
|
648
|
+
return ordered;
|
|
649
|
+
}
|
|
650
|
+
function formatModelOutput(value, indent) {
|
|
651
|
+
return value
|
|
652
|
+
.replaceAll("`", "'")
|
|
653
|
+
.split(/\r?\n/u)
|
|
654
|
+
.map((line) => line.trimEnd())
|
|
655
|
+
.filter((line, index, lines) => line || (index > 0 && index < lines.length - 1))
|
|
656
|
+
.map((line) => `${indent} ${line}`)
|
|
657
|
+
.join("\n");
|
|
658
|
+
}
|
|
659
|
+
function formatCompletionMember(member, byIdentity) {
|
|
570
660
|
const summary = compactModelText(member.summary, 200);
|
|
571
661
|
const artifactEntries = Object.entries(member.artifacts ?? {}).slice(0, 4);
|
|
572
662
|
const artifactText = artifactEntries.length === 0
|
|
573
663
|
? ""
|
|
574
664
|
: `; artifacts: ${artifactEntries.map(([name, path]) => `${compactModelText(name, 120)}=\`${compactModelText(path, 320)}\``).join(", ")}`;
|
|
575
|
-
|
|
665
|
+
const indent = " ".repeat(Math.min(completionDepth(member, byIdentity), 8));
|
|
666
|
+
const output = member.output
|
|
667
|
+
? `\n${indent} Output:\n${formatModelOutput(member.output, indent)}`
|
|
668
|
+
: "";
|
|
669
|
+
return `${indent}- \`${compactModelText(member.run, 120)}\` — \`${member.status}\`: ${summary}${artifactText}${output}`;
|
|
576
670
|
}
|
|
577
671
|
function formatStatusCounts(members) {
|
|
578
672
|
const counts = new Map();
|
|
@@ -594,9 +688,13 @@ export function formatRunCompletionBatchMessage(batch) {
|
|
|
594
688
|
`Window: \`${terminalTimes[0]}\` → \`${terminalTimes.at(-1)}\``,
|
|
595
689
|
`Statuses: \`${formatStatusCounts(safe.members)}\``,
|
|
596
690
|
];
|
|
597
|
-
const
|
|
691
|
+
const byIdentity = new Map(safe.members.map((member) => [
|
|
692
|
+
memberIdentity(member),
|
|
693
|
+
member,
|
|
694
|
+
]));
|
|
695
|
+
const candidateRows = orderCompletionForest(safe.members)
|
|
598
696
|
.slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)
|
|
599
|
-
.map(formatCompletionMember);
|
|
697
|
+
.map((member) => formatCompletionMember(member, byIdentity));
|
|
600
698
|
const rows = [];
|
|
601
699
|
for (const row of candidateRows) {
|
|
602
700
|
const omitted = safe.members.length - rows.length - 1;
|
|
@@ -9,18 +9,18 @@ import * as Observability from "./observability.js";
|
|
|
9
9
|
import * as Paths from "./paths.js";
|
|
10
10
|
import * as Pi from "./pi.js";
|
|
11
11
|
import * as RunDelivery from "./run-delivery.js";
|
|
12
|
+
import * as RunDeliveryLineage from "./run-delivery-lineage.js";
|
|
12
13
|
export function createRunUiRuntime(deps) {
|
|
13
14
|
let activeContext;
|
|
14
15
|
let activeOwnerId;
|
|
15
16
|
let animationInterval;
|
|
16
17
|
let deliveryTimeout;
|
|
17
18
|
let notifyTimeout;
|
|
18
|
-
let recoverQueuedBatch = false;
|
|
19
19
|
let recoverQueuedSteers = false;
|
|
20
20
|
let running = false;
|
|
21
21
|
let lastWatcherDiagnosticId = 0;
|
|
22
22
|
const observation = Observability.createRunUiObservationState();
|
|
23
|
-
const
|
|
23
|
+
const deliveryObservation = Observability.createRunUiObservationState();
|
|
24
24
|
const retirementAttempts = new Set();
|
|
25
25
|
const steerDiagnostics = new Set();
|
|
26
26
|
const close = () => {
|
|
@@ -45,9 +45,7 @@ export function createRunUiRuntime(deps) {
|
|
|
45
45
|
if (deliveryTimeout)
|
|
46
46
|
clearTimeout(deliveryTimeout);
|
|
47
47
|
deliveryTimeout = undefined;
|
|
48
|
-
recoverQueuedBatch = false;
|
|
49
48
|
recoverQueuedSteers = false;
|
|
50
|
-
deliveryRecoveryDiagnostics.clear();
|
|
51
49
|
steerDiagnostics.clear();
|
|
52
50
|
if (animationInterval)
|
|
53
51
|
clearInterval(animationInterval);
|
|
@@ -173,12 +171,11 @@ export function createRunUiRuntime(deps) {
|
|
|
173
171
|
}
|
|
174
172
|
}
|
|
175
173
|
};
|
|
176
|
-
const admitCompletionTransitions = (ownerId,
|
|
174
|
+
const admitCompletionTransitions = (ownerId, snapshot) => {
|
|
177
175
|
const existing = journal(ownerId).completion_batch;
|
|
178
176
|
if (existing)
|
|
179
177
|
return true;
|
|
180
|
-
const members = Observability.collectRunCompletionBatchMembers(transitions)
|
|
181
|
-
.slice(0, Limits.RUN_DELIVERY_BATCH_MAX_MEMBERS);
|
|
178
|
+
const members = Observability.collectRunCompletionBatchMembers(snapshot.transitions, snapshot.summary.runs).slice(0, Limits.RUN_DELIVERY_BATCH_MAX_MEMBERS);
|
|
182
179
|
if (members.length === 0)
|
|
183
180
|
return false;
|
|
184
181
|
RunDelivery.admitRunCompletionBatch({
|
|
@@ -189,6 +186,10 @@ export function createRunUiRuntime(deps) {
|
|
|
189
186
|
return true;
|
|
190
187
|
};
|
|
191
188
|
const isIdle = (ctx) => typeof ctx.isIdle !== "function" || ctx.isIdle();
|
|
189
|
+
const isDeliveryRoot = (ownerId) => {
|
|
190
|
+
const inheritedOwner = RunDeliveryLineage.getProcessDeliveryOwnerId();
|
|
191
|
+
return inheritedOwner === undefined || inheritedOwner === ownerId;
|
|
192
|
+
};
|
|
192
193
|
let flushCompletionBatch = (_ctx) => false;
|
|
193
194
|
const flushSteers = (ctx, ownerId) => {
|
|
194
195
|
const recovering = recoverQueuedSteers;
|
|
@@ -273,10 +274,19 @@ export function createRunUiRuntime(deps) {
|
|
|
273
274
|
return false;
|
|
274
275
|
const sink = Pi.createNotificationSink(deps.pi, ctx);
|
|
275
276
|
retireCandidateRuns(ctx, snapshot.summary);
|
|
276
|
-
const
|
|
277
|
-
|
|
277
|
+
const deliverySnapshot = isDeliveryRoot(ownerId)
|
|
278
|
+
? Observability.readRunDeliverySnapshot(deliveryObservation, ownerId)
|
|
279
|
+
: undefined;
|
|
280
|
+
const hasCompletionCandidates = deliverySnapshot
|
|
281
|
+
? Observability.collectRunCompletionBatchMembers(deliverySnapshot.transitions, deliverySnapshot.summary.runs).length > 0
|
|
282
|
+
: false;
|
|
283
|
+
const hasCompletionBatch = isDeliveryRoot(ownerId) &&
|
|
284
|
+
Boolean(journal(ownerId).completion_batch);
|
|
278
285
|
admitSteerEvents(ctx, ownerId, snapshot.attentionEvents);
|
|
279
286
|
Observability.pruneRunUiObservationState(observation, snapshot);
|
|
287
|
+
if (deliverySnapshot) {
|
|
288
|
+
Observability.pruneRunUiObservationState(deliveryObservation, deliverySnapshot);
|
|
289
|
+
}
|
|
280
290
|
if (!terminalOnly) {
|
|
281
291
|
Observability.deliverRunAttentionNotifications(snapshot.attentionEvents.filter((event) => !Observability.isRunSteerAttentionEvent(event)), sink);
|
|
282
292
|
}
|
|
@@ -333,27 +343,34 @@ export function createRunUiRuntime(deps) {
|
|
|
333
343
|
},
|
|
334
344
|
});
|
|
335
345
|
flushCompletionBatch = (ctx) => {
|
|
336
|
-
if (!running || activeContext !== ctx || deps.getActiveContext() !== ctx)
|
|
337
|
-
return false;
|
|
338
346
|
const ownerId = activeOwnerId;
|
|
339
|
-
if (!ownerId)
|
|
347
|
+
if (!running || !ownerId)
|
|
348
|
+
return false;
|
|
349
|
+
try {
|
|
350
|
+
if (Pi.getSessionId(ctx) !== ownerId)
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
340
354
|
return false;
|
|
355
|
+
}
|
|
341
356
|
if (flushSteers(ctx, ownerId))
|
|
342
357
|
return true;
|
|
358
|
+
if (!isDeliveryRoot(ownerId))
|
|
359
|
+
return false;
|
|
343
360
|
let batch = journal(ownerId).completion_batch;
|
|
344
361
|
if (!batch) {
|
|
345
|
-
const snapshot = Observability.
|
|
346
|
-
admitCompletionTransitions(ownerId, snapshot
|
|
347
|
-
Observability.pruneRunUiObservationState(
|
|
362
|
+
const snapshot = Observability.readRunDeliverySnapshot(deliveryObservation, ownerId);
|
|
363
|
+
admitCompletionTransitions(ownerId, snapshot);
|
|
364
|
+
Observability.pruneRunUiObservationState(deliveryObservation, snapshot);
|
|
348
365
|
batch = journal(ownerId).completion_batch;
|
|
349
366
|
}
|
|
350
367
|
if (!batch)
|
|
351
368
|
return false;
|
|
352
369
|
if (batch.phase === "presented") {
|
|
353
370
|
finishPresentedBatch(ownerId, batch);
|
|
354
|
-
const snapshot = Observability.
|
|
355
|
-
admitCompletionTransitions(ownerId, snapshot
|
|
356
|
-
Observability.pruneRunUiObservationState(
|
|
371
|
+
const snapshot = Observability.readRunDeliverySnapshot(deliveryObservation, ownerId);
|
|
372
|
+
admitCompletionTransitions(ownerId, snapshot);
|
|
373
|
+
Observability.pruneRunUiObservationState(deliveryObservation, snapshot);
|
|
357
374
|
batch = journal(ownerId).completion_batch;
|
|
358
375
|
if (!batch)
|
|
359
376
|
return false;
|
|
@@ -362,27 +379,16 @@ export function createRunUiRuntime(deps) {
|
|
|
362
379
|
return true;
|
|
363
380
|
const content = RunDelivery.formatRunCompletionBatchMessage(batch);
|
|
364
381
|
if (batch.phase === "queued") {
|
|
365
|
-
if (!
|
|
366
|
-
return true;
|
|
367
|
-
recoverQueuedBatch = false;
|
|
368
|
-
const evidence = Pi.inspectRunCompletionBatchSessionEvidence(ctx, batch.batch_id, content);
|
|
369
|
-
if (evidence.status === "present")
|
|
370
|
-
return true;
|
|
371
|
-
if (evidence.status !== "absent") {
|
|
372
|
-
const diagnosticKey = `${batch.batch_id}:${evidence.status}:${evidence.reason ?? ""}`;
|
|
373
|
-
if (!deliveryRecoveryDiagnostics.has(diagnosticKey)) {
|
|
374
|
-
deliveryRecoveryDiagnostics.add(diagnosticKey);
|
|
375
|
-
ctx.ui.notify(`Actor completion recovery is ${evidence.status}: ${evidence.reason ?? "conflicting session evidence"}. Batch ${batch.batch_id} remains queued.`, "warning");
|
|
376
|
-
}
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
379
|
-
if (!RunDelivery.resetRunCompletionBatchPending({
|
|
382
|
+
if (!RunDelivery.markRunCompletionBatchPresented({
|
|
380
383
|
batchId: batch.batch_id,
|
|
381
384
|
ownerId,
|
|
382
385
|
tempDir: Paths.EXTENSION_RUNTIME_PATHS.tempDir,
|
|
383
386
|
}))
|
|
384
387
|
return true;
|
|
385
|
-
|
|
388
|
+
const accepted = journal(ownerId).completion_batch;
|
|
389
|
+
if (accepted)
|
|
390
|
+
finishPresentedBatch(ownerId, accepted);
|
|
391
|
+
return flushCompletionBatch(ctx);
|
|
386
392
|
}
|
|
387
393
|
if (!isIdle(ctx))
|
|
388
394
|
return true;
|
|
@@ -405,19 +411,33 @@ export function createRunUiRuntime(deps) {
|
|
|
405
411
|
})) {
|
|
406
412
|
throw new Error("Completion batch changed before queue acknowledgment");
|
|
407
413
|
}
|
|
408
|
-
|
|
414
|
+
if (!RunDelivery.markRunCompletionBatchPresented({
|
|
415
|
+
batchId: batch.batch_id,
|
|
416
|
+
ownerId,
|
|
417
|
+
tempDir: Paths.EXTENSION_RUNTIME_PATHS.tempDir,
|
|
418
|
+
})) {
|
|
419
|
+
throw new Error("Completion batch changed before durable acceptance");
|
|
420
|
+
}
|
|
421
|
+
const accepted = journal(ownerId).completion_batch;
|
|
422
|
+
if (accepted)
|
|
423
|
+
finishPresentedBatch(ownerId, accepted);
|
|
424
|
+
ctx.ui.notify(`Actor completions ready: ${batch.members.length}`, "info");
|
|
409
425
|
return true;
|
|
410
426
|
};
|
|
411
427
|
return {
|
|
412
428
|
close,
|
|
413
429
|
flushCompletionBatch,
|
|
414
430
|
projectContext(messages, ctx) {
|
|
415
|
-
|
|
431
|
+
const ownerId = activeOwnerId;
|
|
432
|
+
if (!running || !ownerId)
|
|
416
433
|
return messages;
|
|
434
|
+
try {
|
|
435
|
+
if (Pi.getSessionId(ctx) !== ownerId)
|
|
436
|
+
return messages;
|
|
417
437
|
}
|
|
418
|
-
|
|
419
|
-
if (!ownerId)
|
|
438
|
+
catch {
|
|
420
439
|
return messages;
|
|
440
|
+
}
|
|
421
441
|
const steerContext = Pi.dedupeRunSteerContext(messages);
|
|
422
442
|
let contextMessages = steerContext.messages;
|
|
423
443
|
for (const steer of journal(ownerId).steers) {
|
|
@@ -486,7 +506,6 @@ export function createRunUiRuntime(deps) {
|
|
|
486
506
|
close();
|
|
487
507
|
activeContext = ctx;
|
|
488
508
|
activeOwnerId = ownerId;
|
|
489
|
-
recoverQueuedBatch = true;
|
|
490
509
|
recoverQueuedSteers = true;
|
|
491
510
|
running = true;
|
|
492
511
|
try {
|
|
@@ -223,6 +223,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
223
223
|
let rootWatcher;
|
|
224
224
|
let parentWatcher;
|
|
225
225
|
let failureNotified = false;
|
|
226
|
+
let notifyAfterReload = false;
|
|
226
227
|
const setWatchStatus = (watchStatus) => runtime.setWatchStatus?.(watchStatus);
|
|
227
228
|
const close = () => {
|
|
228
229
|
const closingRoot = rootWatcher;
|
|
@@ -234,6 +235,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
234
235
|
if (reloadTimeout)
|
|
235
236
|
clearTimeout(reloadTimeout);
|
|
236
237
|
reloadTimeout = undefined;
|
|
238
|
+
notifyAfterReload = false;
|
|
237
239
|
setWatchStatus("closed");
|
|
238
240
|
};
|
|
239
241
|
const reportCallbackError = (error) => {
|
|
@@ -256,15 +258,19 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
256
258
|
reportCallbackError(error);
|
|
257
259
|
}
|
|
258
260
|
};
|
|
259
|
-
const scheduleReload = (ctx) => {
|
|
261
|
+
const scheduleReload = (ctx, notifyActiveChange = false) => {
|
|
260
262
|
failureNotified = false;
|
|
263
|
+
notifyAfterReload ||= notifyActiveChange;
|
|
261
264
|
if (reloadTimeout)
|
|
262
265
|
clearTimeout(reloadTimeout);
|
|
263
266
|
reloadTimeout = setTimeout(() => {
|
|
264
267
|
reloadTimeout = undefined;
|
|
265
268
|
try {
|
|
266
269
|
runtime.loadTools(ctx, deps.getResolutionContext?.());
|
|
267
|
-
|
|
270
|
+
if (notifyAfterReload) {
|
|
271
|
+
ctx.ui.notify("Recipe tools refreshed from ~/.pi/agent/recipes", "info");
|
|
272
|
+
}
|
|
273
|
+
notifyAfterReload = false;
|
|
268
274
|
}
|
|
269
275
|
catch (error) {
|
|
270
276
|
notifyFailure(ctx);
|
|
@@ -295,7 +301,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
295
301
|
parentWatcher = undefined;
|
|
296
302
|
watcher.close();
|
|
297
303
|
watchRoot(ctx, recipeRoot);
|
|
298
|
-
scheduleReload(ctx);
|
|
304
|
+
scheduleReload(ctx, true);
|
|
299
305
|
});
|
|
300
306
|
parentWatcher = watcher;
|
|
301
307
|
setWatchStatus("watching_parent");
|
|
@@ -319,17 +325,19 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
319
325
|
return;
|
|
320
326
|
}
|
|
321
327
|
try {
|
|
322
|
-
const watcher = watchPath(recipeRoot, () => {
|
|
328
|
+
const watcher = watchPath(recipeRoot, (_event, changedFile) => {
|
|
323
329
|
if (rootWatcher !== watcher)
|
|
324
330
|
return;
|
|
325
331
|
if (!pathExists(recipeRoot)) {
|
|
326
332
|
rootWatcher = undefined;
|
|
327
333
|
watcher.close();
|
|
328
|
-
scheduleReload(ctx);
|
|
334
|
+
scheduleReload(ctx, true);
|
|
329
335
|
watchParent(ctx, recipeRoot);
|
|
330
336
|
return;
|
|
331
337
|
}
|
|
332
|
-
|
|
338
|
+
const relativeChange = changedFile ? String(changedFile) : "";
|
|
339
|
+
const firstSegment = relativeChange.split(/[\\/]/u)[0];
|
|
340
|
+
scheduleReload(ctx, Boolean(relativeChange) && firstSegment !== "drafts");
|
|
333
341
|
});
|
|
334
342
|
rootWatcher = watcher;
|
|
335
343
|
setWatchStatus("watching_root");
|
|
@@ -107,7 +107,7 @@ Run = Recipe + Trace + Control
|
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
1. Spawn with the exact logical Recipe identity and caller-owned values.
|
|
110
|
-
2. Retain the returned `run:<id>` and normally wait for
|
|
110
|
+
2. Retain the returned `run:<id>` and normally wait for the root coordinator's settled completion batch instead of polling. Nested actor completions accumulate under their containing top-level Run and arrive in that one tree-compressed batch; descendant sessions do not need notification options.
|
|
111
111
|
3. Inspect `view=trace` when retained observations or attention matter.
|
|
112
112
|
4. Inspect `view=control` before diagnosing service readiness, stale work, or saturation.
|
|
113
113
|
5. Send `message` only for an action declared and consumed by that controlled Recipe.
|
|
@@ -110,7 +110,7 @@ Statuses include `running`, `done`, `failed`, `exited`, `cancelled`, and `killed
|
|
|
110
110
|
|
|
111
111
|
Ambient observation detects root terminal transitions and explicit retained Trace attention. Terminal transitions reconcile before semantic attention. Canonical attention is an in-memory wake hint, not a durable queue: observers prime retained ids at startup, deliver each later retained unseen id once, and bound memory to the current retained set across compaction. Persist durable recovery state or an artifact before emitting attention; compaction may discard older hints and its marker makes that history loss explicit. Terminal follow-up delivery persists handled/failure evidence so reloads retry unhandled transitions without duplicating completed notifications.
|
|
112
112
|
|
|
113
|
-
Ordinary finite Runs project
|
|
113
|
+
Ordinary finite Runs project terminal results through one root-coordinator completion scheduler. Every detached runner passes its delivery-root owner and exact parent Run generation to nested actor processes. A descendant terminal remains authoritative but ineligible until its containing top-level Run is terminal; descendant Pi sessions never schedule completion follow-ups. After `agent_settled`, session recovery, or an idle debounce, the root scheduler snapshots the ready trees plus concurrently completed top-level Runs into one owner-fenced immutable batch of at most 256 exact generations. One batch causes one automatic agent turn, renders one visible gray operator card, exposes at most 64 bounded parent-child rows with semantic outputs to the model, and marks member terminals handled after Pi durably accepts the exact batch. Pending send failures retain bounded retry evidence. The narrow crash boundary between acceptance and finalization remains recoverable from bounded active-session evidence, but normal hot delivery never waits on a later context callback and therefore cannot block subsequent epochs. Duplicate exact context envelopes collapse before presentation.
|
|
114
114
|
|
|
115
115
|
Sequence, parallel, repeat, and imported branches are internal execution topology and never own branch-level turns. Each separately launched Run owns its own generation and terminal lifecycle; compatible singleton reuse is not a new launch. Explicit semantic attention may intentionally add a checkpoint turn; Runs marked silent and synchronously acknowledged stop outcomes suppress automatic projection. Large semantic results stay outside compact completion rows and remain available in structured details, execution captures, or artifacts.
|
|
116
116
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# Coordinator Delivery Scheduler
|
|
2
2
|
|
|
3
|
-
Status:
|
|
3
|
+
Status: Implemented coordinator-delivery contract.
|
|
4
4
|
|
|
5
5
|
## Goal
|
|
6
6
|
|
|
7
7
|
Separate durable Run completion truth from the scheduling of model turns:
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
|
-
one Run generation -> one
|
|
11
|
-
one
|
|
10
|
+
one Run generation -> one terminal record with inherited tree lineage
|
|
11
|
+
one ready completion forest per bounded epoch -> one root-coordinator turn
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Ordinary
|
|
14
|
+
Ordinary terminals accumulate across nested actor sessions while Pi is active. Descendants wait for their containing top-level Run, then all ready trees and concurrently completed top-level Runs reach only the root coordinator in one bounded batch after Pi settles. Only an explicitly actor-authored urgent semantic checkpoint may steer an active agent loop.
|
|
15
15
|
|
|
16
16
|
## Non-Goals
|
|
17
17
|
|
|
@@ -27,7 +27,7 @@ Ordinary root terminals accumulate while Pi is active and reach the coordinator
|
|
|
27
27
|
- `attention: "notify"` remains visible UI status without a model turn.
|
|
28
28
|
- `attention: "followup"` retains its existing explicit semantic follow-up behavior.
|
|
29
29
|
- New `attention: "steer"` requests urgent semantic delivery at Pi's next safe assistant/tool boundary.
|
|
30
|
-
-
|
|
30
|
+
- Terminal transitions inherit exact parent-generation and root-owner lineage; only the root session batches ready completion trees instead of sending one follow-up per Run or descendant session.
|
|
31
31
|
|
|
32
32
|
`command.done` remains non-projectable even if malformed or legacy Trace attaches any attention value.
|
|
33
33
|
|
|
@@ -81,11 +81,11 @@ Phases are monotonic:
|
|
|
81
81
|
|
|
82
82
|
Every journal mutation uses the canonical token-owned lock, expected-phase fencing, owner and generation validation, and atomic replacement. Repeated transitions are idempotent. Corrupt, oversized, foreign-owner, or stale-generation state fails closed with bounded diagnostics.
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Completion and steer acknowledgments intentionally differ. Successful `sendMessage()` acceptance finalizes an ordinary completion batch immediately and marks its members through the existing terminal-handled authority; this prevents a missed context callback from blocking every later epoch. Urgent steer still requires exact model-context presentation. If a completion member was synchronously archived or pruned after queueing, the bounded delivery snapshot remains sufficient and the missing state write becomes a diagnostic rather than invalidating acceptance.
|
|
85
85
|
|
|
86
86
|
## Completion Collection
|
|
87
87
|
|
|
88
|
-
Reconciliation admits unhandled
|
|
88
|
+
Reconciliation admits unhandled terminal generations with status `done`, `failed`, `killed`, or `exited` that belong to the active root delivery owner. A descendant becomes eligible only when its exact parent chain reaches a terminal top-level Run.
|
|
89
89
|
|
|
90
90
|
It excludes:
|
|
91
91
|
|
|
@@ -97,7 +97,7 @@ It excludes:
|
|
|
97
97
|
|
|
98
98
|
Candidates sort by terminal timestamp, then stable Run identity, then `run_instance_id`. Replacement generations with the same logical Run id remain distinct internal members.
|
|
99
99
|
|
|
100
|
-
While `ctx.isIdle()` is false, candidates remain durable in their Run state and no terminal follow-up is sent. A flush snapshots eligible candidates into one immutable batch. While
|
|
100
|
+
While `ctx.isIdle()` is false, candidates remain durable in their Run state and no terminal follow-up is sent. A flush snapshots eligible candidates into one immutable batch. While transport acceptance is pending, newer terminals stay unhandled for the next bounded completion epoch.
|
|
101
101
|
|
|
102
102
|
## Batch Flush
|
|
103
103
|
|
|
@@ -107,25 +107,20 @@ Flush one batch when:
|
|
|
107
107
|
2. terminals arrive while Pi is already idle and survive one short debounce window;
|
|
108
108
|
3. session restoration discovers unhandled terminal generations or recoverable queued delivery state.
|
|
109
109
|
|
|
110
|
-
The
|
|
110
|
+
The custom message uses `customType: "pi-actors-run-batch"`, `display: true`, `deliverAs: "followUp"`, and `triggerTurn: true`. It restores the visible gray completion card for the operator while supplying the same single tree-compressed prompt to the model. The bounded content retains the exact batch identity for session evidence and deduplication even though Pi omits private message details from model context. It includes:
|
|
111
111
|
|
|
112
112
|
- batch ID and completion window;
|
|
113
113
|
- counts by terminal status;
|
|
114
|
-
- stable Run
|
|
114
|
+
- stable parent-child Run rows with status, compact semantic output, and bounded artifacts;
|
|
115
115
|
- explicit overflow evidence and the canonical runtime Inspect route.
|
|
116
116
|
|
|
117
117
|
The journal may retain at most 256 members and 1 MiB. Model-facing content lists at most 64 exact rows within the centralized model-output bound. Additional members remain represented by exact status counts and supported Inspect guidance. More than 256 unhandled generations form a later batch rather than being discarded.
|
|
118
118
|
|
|
119
119
|
Completion member details remain redacted through existing terminal projection rules: no raw model policy, secrets, private Recipe paths, or machine-local source paths enter the message.
|
|
120
120
|
|
|
121
|
-
## Presentation
|
|
121
|
+
## Acceptance, Presentation, And Recovery
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
1. moves the envelope to `presented`;
|
|
126
|
-
2. marks every still-present member generation terminal-handled;
|
|
127
|
-
3. records a non-attention `delivery.steer_presented` marker in the exact Run generation for a presented steer;
|
|
128
|
-
4. retains a bounded owner receipt sufficient for near-term deduplication and diagnostics.
|
|
123
|
+
Ordinary completion delivery atomically advances through queued acceptance to finalization immediately after `sendMessage()` returns, marks every still-present member generation terminal-handled, and retains a bounded owner receipt. A `context` lifecycle adapter remains for exact urgent-steer presentation and harmless completion deduplication; presented steer writes `delivery.steer_presented` in the exact Run generation.
|
|
129
124
|
|
|
130
125
|
The generation-fenced Trace marker prevents a retained historical steer from replaying after bounded owner receipts rotate: suffix compaction cannot retain the older steer while discarding its newer presentation marker. Missing, archived, pruned, or replaced Run state needs no marker because it can no longer replay that original generation.
|
|
131
126
|
|
|
@@ -133,9 +128,9 @@ Recovery rules:
|
|
|
133
128
|
|
|
134
129
|
- Send failure: keep `pending`, record failure evidence, and retry.
|
|
135
130
|
- Crash after queueing: inspect existing owned Pi session evidence for the exact custom message ID.
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
- Presented
|
|
131
|
+
- Completion send accepted: finalize immediately and release the next epoch.
|
|
132
|
+
- Crash leaves a queued completion: queued itself proves `sendMessage()` acceptance, so finalize without resend or content reformatting.
|
|
133
|
+
- Presented urgent steer: never resend.
|
|
139
134
|
- Session or context replacement: close timers and callbacks; never deliver through stale context.
|
|
140
135
|
- Owner mismatch: do not inspect, acknowledge, or deliver the envelope.
|
|
141
136
|
|
|
@@ -188,8 +183,8 @@ Implementation is complete only when source and packed-extension tests prove:
|
|
|
188
183
|
2. Idle completions inside the debounce window form one batch.
|
|
189
184
|
3. Completion/settled races project every generation exactly once.
|
|
190
185
|
4. Send failure and restart before queueing retry without a handled marker.
|
|
191
|
-
5. Restart
|
|
192
|
-
6.
|
|
186
|
+
5. Restart in the narrow queue-acceptance/finalization boundary neither loses nor duplicates the batch.
|
|
187
|
+
6. Successful completion acceptance acknowledges members atomically and idempotently; urgent steer still requires exact `context` presentation.
|
|
193
188
|
7. Replacement generations sharing a Run id remain distinct.
|
|
194
189
|
8. Silent, stopped, cancelled, handled, and foreign-owner Runs remain excluded.
|
|
195
190
|
9. Legacy or malformed `command.done` attention, including `steer`, remains non-projectable.
|
|
@@ -202,6 +197,6 @@ Focused observability and delivery tests precede TypeScript/build/import checks.
|
|
|
202
197
|
|
|
203
198
|
## Rollout
|
|
204
199
|
|
|
205
|
-
This is one minor release because durable batching,
|
|
200
|
+
This is one minor release because durable batching, acceptance acknowledgment, lifecycle ordering, and explicit steer share one model-delivery invariant. Completion acceptance must release later epochs without depending on a context callback; steer must not ship before its durable presentation-deduplication path exists.
|
|
206
201
|
|
|
207
202
|
Update README and Run documentation only when implementation establishes the new public behavior. Move the accepted outcome from BACKLOG to CHANGELOG only after complete validation.
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
type CurrentPolicyProvenance,
|
|
33
33
|
} from "./model-context.ts";
|
|
34
34
|
import * as Paths from "./paths.ts";
|
|
35
|
+
import * as RunDeliveryLineage from "./run-delivery-lineage.ts";
|
|
35
36
|
import * as RecipesReferences from "./recipes-references.ts";
|
|
36
37
|
import * as RecipesUsage from "./recipes-usage.ts";
|
|
37
38
|
import * as Schema from "./schema.ts";
|
|
@@ -186,6 +187,8 @@ export interface AsyncRunMeta {
|
|
|
186
187
|
artifacts?: Record<string, RunArtifactDeclaration>;
|
|
187
188
|
control?: string[];
|
|
188
189
|
control_endpoint?: AsyncRunControlEndpoint;
|
|
190
|
+
delivery_owner_id?: string;
|
|
191
|
+
delivery_parent?: RunDeliveryLineage.RunDeliveryParent;
|
|
189
192
|
model_policy?: CurrentPolicyProvenance;
|
|
190
193
|
notification_policy?: "normal" | "silent";
|
|
191
194
|
process_identity?: RunProcessIdentity;
|
|
@@ -666,6 +669,10 @@ export function startRun(
|
|
|
666
669
|
startParams.transport_context,
|
|
667
670
|
);
|
|
668
671
|
const artifacts = resolveArtifactPaths(startParams.artifacts, outputValues);
|
|
672
|
+
const runInstanceId = randomUUID();
|
|
673
|
+
const deliveryLineage = RunDeliveryLineage.inheritedRunDeliveryLineage(
|
|
674
|
+
startParams.ownerId,
|
|
675
|
+
);
|
|
669
676
|
const meta: AsyncRunMeta = {
|
|
670
677
|
argv: [process.execPath, ...argv],
|
|
671
678
|
createdAt: new Date().toISOString(),
|
|
@@ -683,7 +690,7 @@ export function startRun(
|
|
|
683
690
|
...(recipe ? { recipe } : {}),
|
|
684
691
|
...(recipeFile ? { recipe_file: recipeFile } : {}),
|
|
685
692
|
run,
|
|
686
|
-
run_instance_id:
|
|
693
|
+
run_instance_id: runInstanceId,
|
|
687
694
|
state_dir: stateDir,
|
|
688
695
|
state_schema: RuntimeIdentity.RUN_STATE_SCHEMA,
|
|
689
696
|
status: "running",
|
|
@@ -696,6 +703,7 @@ export function startRun(
|
|
|
696
703
|
...(startParams.control_endpoint
|
|
697
704
|
? { control_endpoint: startParams.control_endpoint }
|
|
698
705
|
: {}),
|
|
706
|
+
...(deliveryLineage ?? {}),
|
|
699
707
|
...(startParams.notification_policy === "silent"
|
|
700
708
|
? { notification_policy: "silent" as const }
|
|
701
709
|
: {}),
|
|
@@ -726,6 +734,15 @@ export function startRun(
|
|
|
726
734
|
const child = spawn(process.execPath, argv, {
|
|
727
735
|
cwd,
|
|
728
736
|
detached: true,
|
|
737
|
+
...(deliveryLineage
|
|
738
|
+
? {
|
|
739
|
+
env: RunDeliveryLineage.runDeliveryChildEnv(deliveryLineage, {
|
|
740
|
+
run,
|
|
741
|
+
run_instance_id: runInstanceId,
|
|
742
|
+
state_dir: stateDir,
|
|
743
|
+
}),
|
|
744
|
+
}
|
|
745
|
+
: {}),
|
|
729
746
|
stdio: ["ignore", outFd, errFd],
|
|
730
747
|
});
|
|
731
748
|
closeSync(outFd);
|