larkin 0.4.18 → 0.4.19
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/runtime/pi-inline-extensions.mjs +5 -0
- package/dist/runtime/pi-subagent-ledger.cjs +632 -0
- package/dist/runtime/pi-subagent-ledger.mjs +594 -0
- package/dist/runtime/pi-subagent-record-watchdog-injection.mjs +43 -0
- package/dist/runtime/pi-subagent-record-watchdog.bundle.js +183 -0
- package/dist/runtime/pi-subagent-record-watchdog.mjs +46 -0
- package/dist/runtime/pi-subagents-notification.mjs +14 -0
- package/dist/runtime/runtime-adapters.mjs +52 -10
- package/dist/runtime/runtime-host.cjs +187 -14
- package/dist/runtime/runtime-host.mjs +187 -14
- package/package.json +1 -1
|
@@ -17,6 +17,7 @@ const runtime_readiness_js_1 = require("./runtime-readiness.cjs");
|
|
|
17
17
|
const official_lark_cli_js_1 = require("../app/official-lark-cli.cjs");
|
|
18
18
|
const agent_lark_cli_workspace_js_1 = require("../app/agent-lark-cli-workspace.cjs");
|
|
19
19
|
const pi_compaction_recovery_js_1 = require("./pi-compaction-recovery.cjs");
|
|
20
|
+
const pi_subagent_ledger_js_1 = require("./pi-subagent-ledger.cjs");
|
|
20
21
|
class RuntimeSessionResetError extends Error {
|
|
21
22
|
code;
|
|
22
23
|
pendingCount;
|
|
@@ -41,6 +42,7 @@ class RuntimeSessionRecoveryError extends Error {
|
|
|
41
42
|
exports.RuntimeSessionRecoveryError = RuntimeSessionRecoveryError;
|
|
42
43
|
const MAX_DELIVERIES = 2048;
|
|
43
44
|
const BACKGROUND_COMPLETION_IMMEDIATE_RETRY_LIMIT = 5;
|
|
45
|
+
const DEFAULT_SUBAGENT_RECONCILE_INTERVAL_MS = 15_000;
|
|
44
46
|
const now = () => new Date().toISOString();
|
|
45
47
|
const isActiveDelivery = (status) => ["pending", "submitting", "accepted"].includes(status);
|
|
46
48
|
const REPLAY_FAILURE_CODES = new Set([
|
|
@@ -150,6 +152,10 @@ function createRuntimeHost(options) {
|
|
|
150
152
|
maxAttempts: options.retryPolicy?.maxAttempts ?? 6,
|
|
151
153
|
stableWindowMs: options.retryPolicy?.stableWindowMs ?? 30_000,
|
|
152
154
|
};
|
|
155
|
+
const subagentReconcileIntervalMs = options.subagentReconcileIntervalMs ?? DEFAULT_SUBAGENT_RECONCILE_INTERVAL_MS;
|
|
156
|
+
if (!Number.isFinite(subagentReconcileIntervalMs) || subagentReconcileIntervalMs < 0) {
|
|
157
|
+
throw new Error("subagentReconcileIntervalMs must be >= 0");
|
|
158
|
+
}
|
|
153
159
|
const emit = (event) => {
|
|
154
160
|
if (event.type === "delivery")
|
|
155
161
|
telemetry?.delivery(event.agentId, event.messageId, event.status);
|
|
@@ -159,10 +165,14 @@ function createRuntimeHost(options) {
|
|
|
159
165
|
const runtimeEnv = (config, generation) => {
|
|
160
166
|
const base = {
|
|
161
167
|
LARKIN_AGENT_ID: config.agentId,
|
|
162
|
-
...(generation ? {
|
|
168
|
+
...(generation ? {
|
|
169
|
+
LARKIN_RUNTIME_OBSERVATION_GENERATION: generation,
|
|
170
|
+
[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV]: generation,
|
|
171
|
+
} : {}),
|
|
163
172
|
LARKIN_CONFIG_DIR: process.env.LARKIN_CONFIG_DIR,
|
|
164
173
|
LARKIN_HOME: process.env.LARKIN_HOME,
|
|
165
174
|
...(config.piDistribution ? { LARKIN_PI_DISTRIBUTION: config.piDistribution } : {}),
|
|
175
|
+
LARKIN_STATE_DIR: (0, pi_subagent_ledger_js_1.effectivePiStateDir)(config),
|
|
166
176
|
...(process.env.HOME ? { HOME: process.env.HOME } : {}),
|
|
167
177
|
...(process.env.SHELL ? { SHELL: process.env.SHELL } : {}),
|
|
168
178
|
...(process.env.ZDOTDIR ? { ZDOTDIR: process.env.ZDOTDIR } : {}),
|
|
@@ -648,6 +658,7 @@ function createRuntimeHost(options) {
|
|
|
648
658
|
}
|
|
649
659
|
agent.backgroundCompletionRejectStreak = 0;
|
|
650
660
|
clearBackgroundCompletionRetryTimer(agent);
|
|
661
|
+
// Leave the durable wake pending so a later restart can requeue it.
|
|
651
662
|
};
|
|
652
663
|
const scheduleBackgroundCompletionRetry = (agent) => {
|
|
653
664
|
const streak = agent.backgroundCompletionRejectStreak;
|
|
@@ -714,6 +725,7 @@ function createRuntimeHost(options) {
|
|
|
714
725
|
agent.backgroundCompletionWakeInputId = null;
|
|
715
726
|
agent.backgroundCompletionRejectStreak = 0;
|
|
716
727
|
clearBackgroundCompletionRetryTimer(agent);
|
|
728
|
+
acknowledgeSubagentWake(agent, completionKey);
|
|
717
729
|
};
|
|
718
730
|
const wakeOnBackgroundCompletion = async (agent) => {
|
|
719
731
|
// Caller already reserved submitting/busy so a concurrent idle drain cannot
|
|
@@ -722,7 +734,15 @@ function createRuntimeHost(options) {
|
|
|
722
734
|
const session = await ensureSession(agent);
|
|
723
735
|
if (agent.session !== session || agent.stopped)
|
|
724
736
|
return { status: "dropped" };
|
|
725
|
-
const
|
|
737
|
+
const headKey = agent.backgroundCompletionQueue[0];
|
|
738
|
+
const tasks = (0, pi_subagent_ledger_js_1.taskIdsFromCompletionKey)(headKey ?? "")
|
|
739
|
+
.map((taskId) => (0, pi_subagent_ledger_js_1.getDispatchedSubagent)(agent.subagentLedger, taskId))
|
|
740
|
+
.filter((task) => Boolean(task));
|
|
741
|
+
const details = tasks.map((task) => `task ${task.taskId} status=${task.status}`).join("; ");
|
|
742
|
+
const wakeReason = details
|
|
743
|
+
? `background subagent completed; ${details}`
|
|
744
|
+
: "background subagent completed";
|
|
745
|
+
const input = options.promptBuilder.buildRuntimeInput("wake", node_crypto_1.default.randomUUID(), { wakeReason });
|
|
726
746
|
const result = await session.prompt(input);
|
|
727
747
|
if (agent.session !== session || agent.stopped)
|
|
728
748
|
return { status: "dropped" };
|
|
@@ -800,10 +820,62 @@ function createRuntimeHost(options) {
|
|
|
800
820
|
agent.submitting = false;
|
|
801
821
|
}
|
|
802
822
|
};
|
|
823
|
+
const persistSubagentLedger = (agent) => {
|
|
824
|
+
(0, pi_subagent_ledger_js_1.writeDispatchedSubagentLedger)((0, pi_subagent_ledger_js_1.ledgerFilePath)((0, pi_subagent_ledger_js_1.effectivePiStateDir)(agent.config)), agent.subagentLedger);
|
|
825
|
+
};
|
|
826
|
+
const dropQueuedBackgroundCompletionFallback = (agent, completionKey) => {
|
|
827
|
+
const keys = new Set([completionKey, ...(0, pi_subagent_ledger_js_1.taskIdsFromCompletionKey)(completionKey)]);
|
|
828
|
+
for (let index = agent.backgroundCompletionQueue.length - 1; index >= 0; index -= 1) {
|
|
829
|
+
const queued = agent.backgroundCompletionQueue[index];
|
|
830
|
+
if (queued && keys.has(queued))
|
|
831
|
+
agent.backgroundCompletionQueue.splice(index, 1);
|
|
832
|
+
}
|
|
833
|
+
for (const key of keys)
|
|
834
|
+
agent.backgroundCompletionKeys.add(key);
|
|
835
|
+
};
|
|
836
|
+
const acknowledgeSubagentWake = (agent, completionKey) => {
|
|
837
|
+
const next = (0, pi_subagent_ledger_js_1.noteDispatchedSubagentWakeAcknowledged)(agent.subagentLedger, { completionKey });
|
|
838
|
+
if (next === agent.subagentLedger)
|
|
839
|
+
return;
|
|
840
|
+
agent.subagentLedger = next;
|
|
841
|
+
persistSubagentLedger(agent);
|
|
842
|
+
for (const taskId of (0, pi_subagent_ledger_js_1.taskIdsFromCompletionKey)(completionKey)) {
|
|
843
|
+
(0, pi_subagent_ledger_js_1.retirePiSubagentRecord)((0, pi_subagent_ledger_js_1.getDispatchedSubagent)(agent.subagentLedger, taskId)?.recordFile);
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
const enqueueUndeliveredTerminalWakes = (agent) => {
|
|
847
|
+
for (const key of (0, pi_subagent_ledger_js_1.undeliveredTerminalWakeKeys)(agent.subagentLedger)) {
|
|
848
|
+
agent.backgroundCompletionKeys.delete(key);
|
|
849
|
+
for (const taskId of (0, pi_subagent_ledger_js_1.taskIdsFromCompletionKey)(key))
|
|
850
|
+
agent.backgroundCompletionKeys.delete(taskId);
|
|
851
|
+
noteBackgroundCompletion(agent, key);
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
const reconcileAfterSessionSwap = (agent) => {
|
|
855
|
+
reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "pi session gone" });
|
|
856
|
+
enqueueUndeliveredTerminalWakes(agent);
|
|
857
|
+
if (agent.backgroundCompletionQueue.length > 0)
|
|
858
|
+
scheduleBackgroundCompletionDrain(agent);
|
|
859
|
+
};
|
|
860
|
+
const loadSubagentLedger = (config) => {
|
|
861
|
+
return (0, pi_subagent_ledger_js_1.readDispatchedSubagentLedger)((0, pi_subagent_ledger_js_1.ledgerFilePath)((0, pi_subagent_ledger_js_1.effectivePiStateDir)(config)));
|
|
862
|
+
};
|
|
863
|
+
const clearSubagentReconcileTimer = (agent) => {
|
|
864
|
+
if (!agent.subagentReconcileTimer)
|
|
865
|
+
return;
|
|
866
|
+
clearInterval(agent.subagentReconcileTimer);
|
|
867
|
+
agent.subagentReconcileTimer = null;
|
|
868
|
+
};
|
|
869
|
+
const hasActiveDispatchedSubagent = (agent) => agent.subagentLedger.tasks.some((task) => task.status === "dispatched");
|
|
803
870
|
const noteBackgroundCompletion = (agent, completionKey) => {
|
|
871
|
+
const taskIds = (0, pi_subagent_ledger_js_1.taskIdsFromCompletionKey)(completionKey);
|
|
804
872
|
if (agent.backgroundCompletionKeys.has(completionKey))
|
|
805
873
|
return;
|
|
874
|
+
if (taskIds.length > 0 && taskIds.every((taskId) => agent.backgroundCompletionKeys.has(taskId)))
|
|
875
|
+
return;
|
|
806
876
|
agent.backgroundCompletionKeys.add(completionKey);
|
|
877
|
+
for (const taskId of taskIds)
|
|
878
|
+
agent.backgroundCompletionKeys.add(taskId);
|
|
807
879
|
if (!agent.backgroundCompletionQueue.includes(completionKey)) {
|
|
808
880
|
agent.backgroundCompletionQueue.push(completionKey);
|
|
809
881
|
}
|
|
@@ -811,6 +883,62 @@ function createRuntimeHost(options) {
|
|
|
811
883
|
scheduleBackgroundCompletionDrain(agent);
|
|
812
884
|
}
|
|
813
885
|
};
|
|
886
|
+
const recordDispatchedSubagent = (agent, taskId, outputFile) => {
|
|
887
|
+
const recordFile = (0, pi_subagent_ledger_js_1.writeDispatchedSubagentRecordFile)((0, pi_subagent_ledger_js_1.effectivePiStateDir)(agent.config), taskId, agent.piSessionOwner);
|
|
888
|
+
agent.subagentLedger = (0, pi_subagent_ledger_js_1.noteDispatchedSubagent)(agent.subagentLedger, { taskId, outputFile, recordFile });
|
|
889
|
+
persistSubagentLedger(agent);
|
|
890
|
+
armSubagentReconcileTimer(agent);
|
|
891
|
+
};
|
|
892
|
+
const recordTerminalSubagentNotification = (agent, completionKey, statuses) => {
|
|
893
|
+
let next = agent.subagentLedger;
|
|
894
|
+
for (const taskId of (0, pi_subagent_ledger_js_1.taskIdsFromCompletionKey)(completionKey)) {
|
|
895
|
+
const status = statuses?.[taskId];
|
|
896
|
+
next = (0, pi_subagent_ledger_js_1.noteDispatchedSubagentTerminal)(next, {
|
|
897
|
+
taskId,
|
|
898
|
+
status: status && status !== "dispatched" && status !== "orphaned" ? status : "completed",
|
|
899
|
+
wakeKey: completionKey,
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
agent.subagentLedger = next;
|
|
903
|
+
persistSubagentLedger(agent);
|
|
904
|
+
if (!hasActiveDispatchedSubagent(agent))
|
|
905
|
+
clearSubagentReconcileTimer(agent);
|
|
906
|
+
};
|
|
907
|
+
const reconcileSubagentLedger = (agent, input = {}) => {
|
|
908
|
+
if (agent.stopped)
|
|
909
|
+
return;
|
|
910
|
+
const result = (0, pi_subagent_ledger_js_1.reconcileDispatchedSubagents)(agent.subagentLedger, {
|
|
911
|
+
probe: options.subagentRecordProbe ?? pi_subagent_ledger_js_1.probePiSubagentRecord,
|
|
912
|
+
forceMissing: input.forceMissing === true,
|
|
913
|
+
missingReason: input.missingReason,
|
|
914
|
+
});
|
|
915
|
+
const previousLedger = agent.subagentLedger;
|
|
916
|
+
agent.subagentLedger = result.ledger;
|
|
917
|
+
if (result.orphaned.length > 0 || result.terminals.length > 0 || result.ledger !== previousLedger) {
|
|
918
|
+
persistSubagentLedger(agent);
|
|
919
|
+
}
|
|
920
|
+
for (const record of [...result.orphaned, ...result.terminals]) {
|
|
921
|
+
noteBackgroundCompletion(agent, record.wakeKey ?? record.taskId);
|
|
922
|
+
}
|
|
923
|
+
if (!hasActiveDispatchedSubagent(agent))
|
|
924
|
+
clearSubagentReconcileTimer(agent);
|
|
925
|
+
else
|
|
926
|
+
armSubagentReconcileTimer(agent);
|
|
927
|
+
};
|
|
928
|
+
const armSubagentReconcileTimer = (agent) => {
|
|
929
|
+
if (agent.stopped || agent.subagentReconcileTimer || subagentReconcileIntervalMs === 0)
|
|
930
|
+
return;
|
|
931
|
+
if (!hasActiveDispatchedSubagent(agent))
|
|
932
|
+
return;
|
|
933
|
+
agent.subagentReconcileTimer = setInterval(() => {
|
|
934
|
+
if (agent.stopped) {
|
|
935
|
+
clearSubagentReconcileTimer(agent);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
reconcileSubagentLedger(agent);
|
|
939
|
+
}, subagentReconcileIntervalMs);
|
|
940
|
+
agent.subagentReconcileTimer.unref?.();
|
|
941
|
+
};
|
|
814
942
|
const scheduleRecreate = (agent, reason) => {
|
|
815
943
|
if (agent.stopped || agent.retryTimer || agent.starting || agent.session)
|
|
816
944
|
return;
|
|
@@ -832,6 +960,8 @@ function createRuntimeHost(options) {
|
|
|
832
960
|
const proactive = session ? proactivelyCompactPiAtIdle(agent, session) : null;
|
|
833
961
|
if (proactive && await proactive === "failed")
|
|
834
962
|
return;
|
|
963
|
+
reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "pi session gone" });
|
|
964
|
+
enqueueUndeliveredTerminalWakes(agent);
|
|
835
965
|
await retryPending(agent);
|
|
836
966
|
if (agent.backgroundCompletionQueue.length > 0)
|
|
837
967
|
scheduleBackgroundCompletionDrain(agent);
|
|
@@ -862,13 +992,21 @@ function createRuntimeHost(options) {
|
|
|
862
992
|
if (agent.stabilityTimer)
|
|
863
993
|
clearTimeout(agent.stabilityTimer);
|
|
864
994
|
agent.stabilityTimer = null;
|
|
865
|
-
void session.close(`replace after ${reason}`).catch((error) => log("runtime close after replacement failed", String(error)));
|
|
866
995
|
for (const record of agent.records.values())
|
|
867
996
|
if (isActiveDelivery(record.status))
|
|
868
997
|
record.status = "pending";
|
|
869
998
|
emitConsumed(agent, persist(agent));
|
|
870
999
|
emit({ type: "agent-status", agentId: agent.config.agentId, status: "error", error: reason });
|
|
871
|
-
|
|
1000
|
+
// Close first so the shutdown sweep can bridge consumed or terminal state
|
|
1001
|
+
// before force-missing reconcile treats a plain sidecar as orphaned.
|
|
1002
|
+
void session.close(`replace after ${reason}`)
|
|
1003
|
+
.catch((error) => log("runtime close after replacement failed", String(error)))
|
|
1004
|
+
.finally(() => {
|
|
1005
|
+
if (agent.stopped)
|
|
1006
|
+
return;
|
|
1007
|
+
reconcileAfterSessionSwap(agent);
|
|
1008
|
+
scheduleRecreate(agent, reason);
|
|
1009
|
+
});
|
|
872
1010
|
};
|
|
873
1011
|
const recoverConfiguration = (agent, session, message) => {
|
|
874
1012
|
if (agent.session !== session || agent.stopped)
|
|
@@ -893,6 +1031,8 @@ function createRuntimeHost(options) {
|
|
|
893
1031
|
const recovery = (async () => {
|
|
894
1032
|
try {
|
|
895
1033
|
await session.close("runtime configuration error");
|
|
1034
|
+
if (!agent.stopped)
|
|
1035
|
+
reconcileAfterSessionSwap(agent);
|
|
896
1036
|
const result = agent.adapter.recoverConfigurationError
|
|
897
1037
|
? await agent.adapter.recoverConfigurationError(message)
|
|
898
1038
|
: { recovered: false, reason: "runtime adapter does not support automatic configuration recovery" };
|
|
@@ -1180,6 +1320,7 @@ function createRuntimeHost(options) {
|
|
|
1180
1320
|
if (agent.backgroundCompletionQueue.length > 0 && !agent.backgroundCompletionRetryTimer) {
|
|
1181
1321
|
scheduleBackgroundCompletionDrain(agent);
|
|
1182
1322
|
}
|
|
1323
|
+
reconcileSubagentLedger(agent);
|
|
1183
1324
|
telemetry?.runtimeEvent(agent.config.agentId, event);
|
|
1184
1325
|
if (recoveredAuthentication) {
|
|
1185
1326
|
agent.authFailureActive = false;
|
|
@@ -1200,8 +1341,18 @@ function createRuntimeHost(options) {
|
|
|
1200
1341
|
queueMicrotask(() => { void scanAndPromoteAcceptedInboxUpdates(agent); });
|
|
1201
1342
|
}
|
|
1202
1343
|
else if (event.type === "runtime-observation") {
|
|
1203
|
-
if (event.phase === "
|
|
1204
|
-
|
|
1344
|
+
if (event.phase === "background_dispatched" && typeof event.taskId === "string" && event.taskId) {
|
|
1345
|
+
recordDispatchedSubagent(agent, event.taskId, typeof event.outputFile === "string" ? event.outputFile : undefined);
|
|
1346
|
+
}
|
|
1347
|
+
else if (event.phase === "completed" && typeof event.completionKey === "string") {
|
|
1348
|
+
recordTerminalSubagentNotification(agent, event.completionKey, event.completionStatuses);
|
|
1349
|
+
if (event.handledInTurn === true) {
|
|
1350
|
+
acknowledgeSubagentWake(agent, event.completionKey);
|
|
1351
|
+
dropQueuedBackgroundCompletionFallback(agent, event.completionKey);
|
|
1352
|
+
}
|
|
1353
|
+
else {
|
|
1354
|
+
noteBackgroundCompletion(agent, event.completionKey);
|
|
1355
|
+
}
|
|
1205
1356
|
}
|
|
1206
1357
|
}
|
|
1207
1358
|
else if (event.type === "activity") {
|
|
@@ -1319,11 +1470,12 @@ function createRuntimeHost(options) {
|
|
|
1319
1470
|
});
|
|
1320
1471
|
const generation = ++agent.generation;
|
|
1321
1472
|
let completedSession = null;
|
|
1473
|
+
const sessionEnv = runtimeEnv(agent.config, `${agent.launchId}:${generation}`);
|
|
1322
1474
|
agent.starting = agent.adapter.createSession({
|
|
1323
1475
|
agentId: agent.config.agentId, model: agent.config.model, reasoningEffort: agent.config.effort || null,
|
|
1324
1476
|
workspaceDir: agent.config.workspaceDir, stateDir: agent.config.stateDir,
|
|
1325
1477
|
resumeSessionId: agent.config.sessionId || null, standingPrompt,
|
|
1326
|
-
env:
|
|
1478
|
+
env: sessionEnv,
|
|
1327
1479
|
}).then((session) => {
|
|
1328
1480
|
completedSession = session;
|
|
1329
1481
|
if (agent.stopped || generation !== agent.generation) {
|
|
@@ -1331,6 +1483,7 @@ function createRuntimeHost(options) {
|
|
|
1331
1483
|
throw new Error("stale runtime session creation");
|
|
1332
1484
|
}
|
|
1333
1485
|
agent.session = session;
|
|
1486
|
+
agent.piSessionOwner = sessionEnv[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
1334
1487
|
session.subscribe((event) => observe(agent, session, event));
|
|
1335
1488
|
if (agent.session !== session)
|
|
1336
1489
|
return session;
|
|
@@ -1470,7 +1623,9 @@ function createRuntimeHost(options) {
|
|
|
1470
1623
|
...(fresh.effectiveReasoningEffort ? { reasoningEffort: fresh.effectiveReasoningEffort } : {}) });
|
|
1471
1624
|
}
|
|
1472
1625
|
emit({ type: "agent-status", agentId, status: "active", readiness });
|
|
1626
|
+
agent.piSessionOwner = probeEnv[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
1473
1627
|
await oldSession.close("Pi context fallback committed").catch((error) => log("previous Pi session close after fallback failed", String(error)));
|
|
1628
|
+
reconcileAfterSessionSwap(agent);
|
|
1474
1629
|
const proactive = proactivelyCompactPiAtIdle(agent, fresh);
|
|
1475
1630
|
void (proactive ?? Promise.resolve("noop")).then((outcome) => {
|
|
1476
1631
|
if (outcome !== "failed")
|
|
@@ -1545,11 +1700,13 @@ function createRuntimeHost(options) {
|
|
|
1545
1700
|
clearTimeout(previous.stabilityTimer);
|
|
1546
1701
|
if (previous.backgroundCompletionRetryTimer)
|
|
1547
1702
|
clearTimeout(previous.backgroundCompletionRetryTimer);
|
|
1703
|
+
if (previous.subagentReconcileTimer)
|
|
1704
|
+
clearInterval(previous.subagentReconcileTimer);
|
|
1548
1705
|
const candidate = {
|
|
1549
1706
|
...previous, config, adapter, session, launchId: node_crypto_1.default.randomUUID(), busy: false, submitting: false,
|
|
1550
1707
|
starting: null, retryAfterSubmit: false, generation: 0, poller: null, retryTimer: null,
|
|
1551
1708
|
recreateAttempts: 0, stabilityTimer: null, recreateReason: null, stopped: false,
|
|
1552
|
-
backgroundCompletionRetryTimer: null,
|
|
1709
|
+
backgroundCompletionRetryTimer: null, subagentReconcileTimer: null,
|
|
1553
1710
|
disabledReason: null, configurationRecovery: null,
|
|
1554
1711
|
readiness: previous.authFailureActive ? previous.readiness : readiness,
|
|
1555
1712
|
};
|
|
@@ -1566,13 +1723,14 @@ function createRuntimeHost(options) {
|
|
|
1566
1723
|
if (!candidate.authFailureActive) {
|
|
1567
1724
|
emit({ type: "agent-status", agentId: config.agentId, status: "active", readiness: candidate.readiness ?? readiness });
|
|
1568
1725
|
}
|
|
1569
|
-
|
|
1570
|
-
//
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1726
|
+
candidate.piSessionOwner = stageEnv[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
1727
|
+
// Close first so the old session's shutdown sweep can bridge consumed
|
|
1728
|
+
// terminals before force-missing reconcile. Commit also clears any
|
|
1729
|
+
// inherited backoff timer; reschedule so a queued completion still
|
|
1730
|
+
// drains on the new session.
|
|
1574
1731
|
await previous.session?.close("runtime candidate committed")
|
|
1575
1732
|
.catch((error) => log("previous runtime close after candidate commit failed", String(error)));
|
|
1733
|
+
reconcileAfterSessionSwap(candidate);
|
|
1576
1734
|
},
|
|
1577
1735
|
rollback,
|
|
1578
1736
|
};
|
|
@@ -1665,8 +1823,10 @@ function createRuntimeHost(options) {
|
|
|
1665
1823
|
...(fresh.effectiveReasoningEffort ? { reasoningEffort: fresh.effectiveReasoningEffort } : {}) });
|
|
1666
1824
|
}
|
|
1667
1825
|
emit({ type: "agent-status", agentId, status: "active", readiness });
|
|
1826
|
+
agent.piSessionOwner = probeEnv[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
1668
1827
|
await oldSession.close("fresh session reset committed")
|
|
1669
1828
|
.catch((error) => log("previous runtime close after fresh reset failed", String(error)));
|
|
1829
|
+
reconcileAfterSessionSwap(agent);
|
|
1670
1830
|
return { generationChanged: true, sessionChanged: oldSessionId !== fresh.sessionId, turns: 0,
|
|
1671
1831
|
runtimeReady: true, pendingCount: 0, sessionId: fresh.sessionId };
|
|
1672
1832
|
},
|
|
@@ -1815,8 +1975,10 @@ function createRuntimeHost(options) {
|
|
|
1815
1975
|
}
|
|
1816
1976
|
emit({ type: "agent-status", agentId, status: "active", readiness });
|
|
1817
1977
|
durableRollback = null;
|
|
1978
|
+
agent.piSessionOwner = probeEnv[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
1818
1979
|
await oldSession.close("context-window recovery committed")
|
|
1819
1980
|
.catch((error) => log("previous runtime close after context-window recovery failed", String(error)));
|
|
1981
|
+
reconcileAfterSessionSwap(agent);
|
|
1820
1982
|
const proactive = proactivelyCompactPiAtIdle(agent, fresh);
|
|
1821
1983
|
void (proactive ?? Promise.resolve("noop")).then((outcome) => {
|
|
1822
1984
|
if (outcome !== "failed")
|
|
@@ -1906,7 +2068,8 @@ function createRuntimeHost(options) {
|
|
|
1906
2068
|
promotedInboxUpdateIds: new Set(),
|
|
1907
2069
|
backgroundCompletionQueue: [], backgroundCompletionKeys: new Set(),
|
|
1908
2070
|
backgroundCompletionInFlight: null, backgroundCompletionWakeInputId: null,
|
|
1909
|
-
backgroundCompletionRejectStreak: 0, backgroundCompletionRetryTimer: null
|
|
2071
|
+
backgroundCompletionRejectStreak: 0, backgroundCompletionRetryTimer: null,
|
|
2072
|
+
subagentLedger: loadSubagentLedger(config), subagentReconcileTimer: null };
|
|
1910
2073
|
const startupConsumed = [];
|
|
1911
2074
|
const startupQuarantined = [];
|
|
1912
2075
|
for (const record of agent.records.values()) {
|
|
@@ -1937,6 +2100,8 @@ function createRuntimeHost(options) {
|
|
|
1937
2100
|
emitConsumed(agent, [...startupConsumed, ...persist(agent)]);
|
|
1938
2101
|
try {
|
|
1939
2102
|
await ensureSession(agent);
|
|
2103
|
+
reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "runtime restarted" });
|
|
2104
|
+
enqueueUndeliveredTerminalWakes(agent);
|
|
1940
2105
|
await recoverStalePiCompaction(agent);
|
|
1941
2106
|
const startupSession = agent.session;
|
|
1942
2107
|
const proactive = startupSession ? proactivelyCompactPiAtIdle(agent, startupSession) : null;
|
|
@@ -1949,6 +2114,8 @@ function createRuntimeHost(options) {
|
|
|
1949
2114
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1950
2115
|
const transient = error instanceof runtime_readiness_js_1.RuntimePrerequisiteError && error.readiness.state === "unavailable";
|
|
1951
2116
|
agent.disabledReason = transient ? null : reason;
|
|
2117
|
+
reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "runtime restarted" });
|
|
2118
|
+
enqueueUndeliveredTerminalWakes(agent);
|
|
1952
2119
|
if (transient) {
|
|
1953
2120
|
recoveringCount += 1;
|
|
1954
2121
|
scheduleRecreate(agent, reason);
|
|
@@ -2059,6 +2226,8 @@ function createRuntimeHost(options) {
|
|
|
2059
2226
|
clearTimeout(agent.stabilityTimer);
|
|
2060
2227
|
if (agent.backgroundCompletionRetryTimer)
|
|
2061
2228
|
clearTimeout(agent.backgroundCompletionRetryTimer);
|
|
2229
|
+
if (agent.subagentReconcileTimer)
|
|
2230
|
+
clearInterval(agent.subagentReconcileTimer);
|
|
2062
2231
|
if (agent.busy)
|
|
2063
2232
|
await agent.session?.cancel(reason);
|
|
2064
2233
|
await agent.session?.close(reason);
|
|
@@ -2067,5 +2236,9 @@ function createRuntimeHost(options) {
|
|
|
2067
2236
|
},
|
|
2068
2237
|
async shutdown(reason) { await Promise.allSettled([...managed.keys()].map((id) => this.stop(id, reason))); },
|
|
2069
2238
|
subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); },
|
|
2239
|
+
getDispatchedSubagent(agentId, taskId) {
|
|
2240
|
+
const agent = managed.get(agentId);
|
|
2241
|
+
return agent ? (0, pi_subagent_ledger_js_1.getDispatchedSubagent)(agent.subagentLedger, taskId) : null;
|
|
2242
|
+
},
|
|
2070
2243
|
};
|
|
2071
2244
|
}
|