larkin 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +7 -1
  3. package/artifacts/release/THIRD_PARTY_NOTICES.txt +1 -100
  4. package/dist/agent/context-prompt.cjs +19 -17
  5. package/dist/agent/context-prompt.mjs +18 -16
  6. package/dist/runtime/pi-state-dir.cjs +105 -0
  7. package/dist/runtime/pi-state-dir.mjs +94 -0
  8. package/dist/runtime/pi-tmux-extension.mjs +137 -0
  9. package/dist/runtime/pi-tmux-injection.mjs +49 -0
  10. package/dist/runtime/pi-tmux.bundle.js +600 -0
  11. package/dist/runtime/pi-tmux.mjs +408 -0
  12. package/dist/runtime/runtime-adapters.mjs +34 -114
  13. package/dist/runtime/runtime-host.cjs +3 -413
  14. package/dist/runtime/runtime-host.mjs +2 -412
  15. package/package.json +2 -8
  16. package/dist/runtime/pi-bash-timeout-extension.mjs +0 -60
  17. package/dist/runtime/pi-bash-timeout-injection.mjs +0 -54
  18. package/dist/runtime/pi-bash-timeout.bundle.js +0 -43
  19. package/dist/runtime/pi-extension-api.mjs +0 -1
  20. package/dist/runtime/pi-subagent-injection.mjs +0 -290
  21. package/dist/runtime/pi-subagent-ledger.cjs +0 -632
  22. package/dist/runtime/pi-subagent-ledger.mjs +0 -594
  23. package/dist/runtime/pi-subagent-record-watchdog-injection.mjs +0 -43
  24. package/dist/runtime/pi-subagent-record-watchdog.bundle.js +0 -183
  25. package/dist/runtime/pi-subagent-record-watchdog.mjs +0 -46
  26. package/dist/runtime/pi-subagents-notification.mjs +0 -175
  27. package/dist/runtime/pi-subagents.bundle.js +0 -9110
  28. package/dist/runtime/pi-supervised-command-extension.mjs +0 -72
  29. package/dist/runtime/pi-supervised-command.bundle.js +0 -4735
  30. package/dist/runtime/pi-supervised-command.mjs +0 -420
  31. package/patches/@tintinweb%2Fpi-subagents@0.14.3.patch +0 -945
@@ -10,7 +10,7 @@ import { authFailureAppliesTo, classifyPiMissingCredentialRejection, classifyRun
10
10
  import { resolveOfficialLarkCli } from "../app/official-lark-cli.mjs";
11
11
  import { assertAgentWorkspaceBound, managedLarkCliEnv } from "../app/agent-lark-cli-workspace.mjs";
12
12
  import { isPiNativeCompactionRequired, PiCompactionBreaker, PiCompactionRecoveryMachine, } from "./pi-compaction-recovery.mjs";
13
- import { getDispatchedSubagent as lookupDispatchedSubagent, ledgerFilePath, noteDispatchedSubagent, noteDispatchedSubagentTerminal, noteDispatchedSubagentWakeAcknowledged, probePiSubagentRecord, readDispatchedSubagentLedger, reconcileDispatchedSubagents, taskIdsFromCompletionKey, undeliveredTerminalWakeKeys, PI_SUBAGENT_SESSION_OWNER_ENV, retirePiSubagentRecord, effectivePiStateDir, writeDispatchedSubagentLedger, writeDispatchedSubagentRecordFile, } from "./pi-subagent-ledger.mjs";
13
+ import { effectivePiStateDir } from "./pi-state-dir.mjs";
14
14
  export class RuntimeSessionResetError extends Error {
15
15
  code;
16
16
  pendingCount;
@@ -32,8 +32,6 @@ export class RuntimeSessionRecoveryError extends Error {
32
32
  }
33
33
  }
34
34
  const MAX_DELIVERIES = 2048;
35
- const BACKGROUND_COMPLETION_IMMEDIATE_RETRY_LIMIT = 5;
36
- const DEFAULT_SUBAGENT_RECONCILE_INTERVAL_MS = 15_000;
37
35
  const now = () => new Date().toISOString();
38
36
  const isActiveDelivery = (status) => ["pending", "submitting", "accepted"].includes(status);
39
37
  const previousSessionRefFor = (session, reason) => session.sessionId
@@ -301,10 +299,6 @@ export function createRuntimeHost(options) {
301
299
  maxAttempts: options.retryPolicy?.maxAttempts ?? 6,
302
300
  stableWindowMs: options.retryPolicy?.stableWindowMs ?? 30_000,
303
301
  };
304
- const subagentReconcileIntervalMs = options.subagentReconcileIntervalMs ?? DEFAULT_SUBAGENT_RECONCILE_INTERVAL_MS;
305
- if (!Number.isFinite(subagentReconcileIntervalMs) || subagentReconcileIntervalMs < 0) {
306
- throw new Error("subagentReconcileIntervalMs must be >= 0");
307
- }
308
302
  const emit = (event) => {
309
303
  if (event.type === "delivery")
310
304
  telemetry?.delivery(event.agentId, event.messageId, event.status);
@@ -316,7 +310,6 @@ export function createRuntimeHost(options) {
316
310
  LARKIN_AGENT_ID: config.agentId,
317
311
  ...(generation ? {
318
312
  LARKIN_RUNTIME_OBSERVATION_GENERATION: generation,
319
- [PI_SUBAGENT_SESSION_OWNER_ENV]: generation,
320
313
  } : {}),
321
314
  LARKIN_CONFIG_DIR: process.env.LARKIN_CONFIG_DIR,
322
315
  LARKIN_HOME: process.env.LARKIN_HOME,
@@ -717,12 +710,6 @@ export function createRuntimeHost(options) {
717
710
  queueMicrotask(() => { void retryPending(agent); });
718
711
  }
719
712
  queueMicrotask(() => { void scanAndPromoteAcceptedInboxUpdates(agent); });
720
- // A completion queued during this submit has no turn-end if prompt
721
- // rejected/threw before turn-start. Drain now that submitting is clear.
722
- if (!agent.busy && !agent.turnInProgress && agent.backgroundCompletionQueue.length > 0
723
- && !agent.backgroundCompletionRetryTimer) {
724
- scheduleBackgroundCompletionDrain(agent);
725
- }
726
713
  }
727
714
  };
728
715
  const retryPending = async (agent) => {
@@ -799,326 +786,6 @@ export function createRuntimeHost(options) {
799
786
  agent.retryAfterSubmit = true;
800
787
  await retryPending(agent);
801
788
  };
802
- const clearBackgroundCompletionRetryTimer = (agent) => {
803
- if (!agent.backgroundCompletionRetryTimer)
804
- return;
805
- clearTimeout(agent.backgroundCompletionRetryTimer);
806
- agent.backgroundCompletionRetryTimer = null;
807
- };
808
- const scheduleBackgroundCompletionDrain = (agent, delayMs = 0) => {
809
- if (agent.stopped)
810
- return;
811
- if (delayMs > 0) {
812
- if (agent.backgroundCompletionRetryTimer)
813
- return;
814
- agent.backgroundCompletionRetryTimer = setTimeout(() => {
815
- agent.backgroundCompletionRetryTimer = null;
816
- if (agent.stopped)
817
- return;
818
- void drainBackgroundCompletionQueue(agent);
819
- }, delayMs);
820
- agent.backgroundCompletionRetryTimer.unref?.();
821
- return;
822
- }
823
- clearBackgroundCompletionRetryTimer(agent);
824
- queueMicrotask(() => { void drainBackgroundCompletionQueue(agent); });
825
- };
826
- const dropBackgroundCompletion = (agent, completionKey) => {
827
- if (!completionKey)
828
- return;
829
- if (agent.backgroundCompletionQueue[0] === completionKey)
830
- agent.backgroundCompletionQueue.shift();
831
- else {
832
- const index = agent.backgroundCompletionQueue.indexOf(completionKey);
833
- if (index >= 0)
834
- agent.backgroundCompletionQueue.splice(index, 1);
835
- }
836
- agent.backgroundCompletionKeys.add(completionKey);
837
- if (agent.backgroundCompletionInFlight === completionKey) {
838
- agent.backgroundCompletionInFlight = null;
839
- agent.backgroundCompletionWakeInputId = null;
840
- }
841
- agent.backgroundCompletionRejectStreak = 0;
842
- clearBackgroundCompletionRetryTimer(agent);
843
- // Leave the durable wake pending so a later restart can requeue it.
844
- };
845
- const scheduleBackgroundCompletionRetry = (agent) => {
846
- const streak = agent.backgroundCompletionRejectStreak;
847
- if (streak < BACKGROUND_COMPLETION_IMMEDIATE_RETRY_LIMIT) {
848
- scheduleBackgroundCompletionDrain(agent);
849
- return;
850
- }
851
- const delayedAttempt = streak - BACKGROUND_COMPLETION_IMMEDIATE_RETRY_LIMIT;
852
- if (delayedAttempt >= retryPolicy.maxAttempts) {
853
- const exhausted = agent.backgroundCompletionQueue[0];
854
- if (exhausted) {
855
- log("background completion wake retries exhausted", exhausted);
856
- dropBackgroundCompletion(agent, exhausted);
857
- }
858
- if (agent.backgroundCompletionQueue.length > 0)
859
- scheduleBackgroundCompletionDrain(agent);
860
- return;
861
- }
862
- const delay = Math.min(retryPolicy.maxDelayMs, retryPolicy.baseDelayMs * 2 ** delayedAttempt);
863
- scheduleBackgroundCompletionDrain(agent, delay);
864
- };
865
- const isPersistentBackgroundWakeFailure = (event) => {
866
- if (!event)
867
- return false;
868
- const category = event.errorCategory;
869
- if (category === "auth" || category === "billing" || category === "quota")
870
- return true;
871
- return category === "provider" && event.retryable === false;
872
- };
873
- const failBackgroundCompletionWake = (agent, event) => {
874
- const completionKey = agent.backgroundCompletionInFlight ?? agent.backgroundCompletionQueue[0];
875
- if (!completionKey)
876
- return;
877
- rearmBackgroundCompletion(agent, completionKey);
878
- if (isPersistentBackgroundWakeFailure(event)) {
879
- log("background completion wake dropped", event?.errorCategory ?? "persistent");
880
- dropBackgroundCompletion(agent, completionKey);
881
- if (agent.backgroundCompletionQueue.length > 0)
882
- scheduleBackgroundCompletionDrain(agent);
883
- return;
884
- }
885
- agent.backgroundCompletionRejectStreak += 1;
886
- scheduleBackgroundCompletionRetry(agent);
887
- };
888
- const rearmBackgroundCompletion = (agent, completionKey = agent.backgroundCompletionInFlight) => {
889
- if (!completionKey)
890
- return;
891
- agent.backgroundCompletionKeys.delete(completionKey);
892
- if (agent.backgroundCompletionInFlight === completionKey) {
893
- agent.backgroundCompletionInFlight = null;
894
- agent.backgroundCompletionWakeInputId = null;
895
- }
896
- if (!agent.backgroundCompletionQueue.includes(completionKey)) {
897
- agent.backgroundCompletionQueue.unshift(completionKey);
898
- }
899
- };
900
- const commitBackgroundCompletion = (agent) => {
901
- const completionKey = agent.backgroundCompletionInFlight;
902
- if (!completionKey)
903
- return;
904
- if (agent.backgroundCompletionQueue[0] === completionKey)
905
- agent.backgroundCompletionQueue.shift();
906
- agent.backgroundCompletionInFlight = null;
907
- agent.backgroundCompletionWakeInputId = null;
908
- agent.backgroundCompletionRejectStreak = 0;
909
- clearBackgroundCompletionRetryTimer(agent);
910
- acknowledgeSubagentWake(agent, completionKey);
911
- };
912
- const wakeOnBackgroundCompletion = async (agent) => {
913
- // Caller already reserved submitting/busy so a concurrent idle drain cannot
914
- // dequeue another head while ensureSession/prompt yields.
915
- try {
916
- const session = await ensureSession(agent);
917
- if (agent.session !== session || agent.stopped)
918
- return { status: "dropped" };
919
- const headKey = agent.backgroundCompletionQueue[0];
920
- const tasks = taskIdsFromCompletionKey(headKey ?? "")
921
- .map((taskId) => lookupDispatchedSubagent(agent.subagentLedger, taskId))
922
- .filter((task) => Boolean(task));
923
- const details = tasks.map((task) => `task ${task.taskId} status=${task.status}`).join("; ");
924
- const wakeReason = details
925
- ? `background subagent completed; ${details}`
926
- : "background subagent completed";
927
- const input = options.promptBuilder.buildRuntimeInput("wake", crypto.randomUUID(), { wakeReason });
928
- const result = await session.prompt(input);
929
- if (agent.session !== session || agent.stopped)
930
- return { status: "dropped" };
931
- if (result.status === "accepted") {
932
- markSessionStable(agent, session);
933
- return { status: "accepted", inputId: input.inputId };
934
- }
935
- agent.busy = false;
936
- return { status: "retry" };
937
- }
938
- catch (error) {
939
- agent.busy = false;
940
- log("background completion wake failed", error instanceof Error ? error.message : String(error));
941
- return { status: "retry" };
942
- }
943
- };
944
- const drainBackgroundCompletionQueue = async (agent) => {
945
- if (agent.stopped || agent.busy || agent.turnInProgress || agent.submitting)
946
- return;
947
- if (agent.backgroundCompletionInFlight || !agent.backgroundCompletionQueue.length)
948
- return;
949
- clearBackgroundCompletionRetryTimer(agent);
950
- const proactive = agent.piProactiveCompaction
951
- && agent.piProactiveCompactionGeneration === agent.generation
952
- && agent.piProactiveCompactionSession === agent.session
953
- ? agent.piProactiveCompaction : null;
954
- if (proactive)
955
- await proactive;
956
- if (agent.adapter.id === "pi" && !agent.busy && !agent.turnInProgress && agent.session) {
957
- const idleGate = proactivelyCompactPiAtIdle(agent, agent.session);
958
- if (idleGate)
959
- await idleGate;
960
- }
961
- if (agent.stopped || agent.busy || agent.turnInProgress || agent.submitting)
962
- return;
963
- if (agent.backgroundCompletionInFlight || !agent.backgroundCompletionQueue.length)
964
- return;
965
- agent.submitting = true;
966
- agent.busy = true;
967
- const completionKey = agent.backgroundCompletionQueue[0];
968
- try {
969
- const outcome = await wakeOnBackgroundCompletion(agent);
970
- if (outcome.status === "accepted") {
971
- agent.backgroundCompletionKeys.add(completionKey);
972
- agent.backgroundCompletionInFlight = completionKey;
973
- agent.backgroundCompletionWakeInputId = outcome.inputId;
974
- // Keep the reject streak until the wake turn actually succeeds. An accepted
975
- // prompt that later terminal-fails must not reset the bounded retry budget.
976
- clearBackgroundCompletionRetryTimer(agent);
977
- }
978
- else if (outcome.status === "dropped") {
979
- if (agent.stopped) {
980
- if (agent.backgroundCompletionQueue[0] === completionKey)
981
- agent.backgroundCompletionQueue.shift();
982
- agent.backgroundCompletionKeys.delete(completionKey);
983
- clearBackgroundCompletionRetryTimer(agent);
984
- }
985
- else {
986
- rearmBackgroundCompletion(agent, completionKey);
987
- agent.busy = false;
988
- }
989
- }
990
- else {
991
- agent.busy = false;
992
- agent.backgroundCompletionRejectStreak += 1;
993
- // Leave the item queued+deduped. Immediate microtask retries cover a
994
- // brief reject; after that a bounded timer still drains because Pi will
995
- // not re-emit the same completion.
996
- scheduleBackgroundCompletionRetry(agent);
997
- }
998
- }
999
- finally {
1000
- agent.submitting = false;
1001
- }
1002
- };
1003
- const persistSubagentLedger = (agent) => {
1004
- writeDispatchedSubagentLedger(ledgerFilePath(effectivePiStateDir(agent.config)), agent.subagentLedger);
1005
- };
1006
- const dropQueuedBackgroundCompletionFallback = (agent, completionKey) => {
1007
- const keys = new Set([completionKey, ...taskIdsFromCompletionKey(completionKey)]);
1008
- for (let index = agent.backgroundCompletionQueue.length - 1; index >= 0; index -= 1) {
1009
- const queued = agent.backgroundCompletionQueue[index];
1010
- if (queued && keys.has(queued))
1011
- agent.backgroundCompletionQueue.splice(index, 1);
1012
- }
1013
- for (const key of keys)
1014
- agent.backgroundCompletionKeys.add(key);
1015
- };
1016
- const acknowledgeSubagentWake = (agent, completionKey) => {
1017
- const next = noteDispatchedSubagentWakeAcknowledged(agent.subagentLedger, { completionKey });
1018
- if (next === agent.subagentLedger)
1019
- return;
1020
- agent.subagentLedger = next;
1021
- persistSubagentLedger(agent);
1022
- for (const taskId of taskIdsFromCompletionKey(completionKey)) {
1023
- retirePiSubagentRecord(lookupDispatchedSubagent(agent.subagentLedger, taskId)?.recordFile);
1024
- }
1025
- };
1026
- const enqueueUndeliveredTerminalWakes = (agent) => {
1027
- for (const key of undeliveredTerminalWakeKeys(agent.subagentLedger)) {
1028
- agent.backgroundCompletionKeys.delete(key);
1029
- for (const taskId of taskIdsFromCompletionKey(key))
1030
- agent.backgroundCompletionKeys.delete(taskId);
1031
- noteBackgroundCompletion(agent, key);
1032
- }
1033
- };
1034
- const reconcileAfterSessionSwap = (agent) => {
1035
- reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "pi session gone" });
1036
- enqueueUndeliveredTerminalWakes(agent);
1037
- if (agent.backgroundCompletionQueue.length > 0)
1038
- scheduleBackgroundCompletionDrain(agent);
1039
- };
1040
- const loadSubagentLedger = (config) => {
1041
- return readDispatchedSubagentLedger(ledgerFilePath(effectivePiStateDir(config)));
1042
- };
1043
- const clearSubagentReconcileTimer = (agent) => {
1044
- if (!agent.subagentReconcileTimer)
1045
- return;
1046
- clearInterval(agent.subagentReconcileTimer);
1047
- agent.subagentReconcileTimer = null;
1048
- };
1049
- const hasActiveDispatchedSubagent = (agent) => agent.subagentLedger.tasks.some((task) => task.status === "dispatched");
1050
- const noteBackgroundCompletion = (agent, completionKey) => {
1051
- const taskIds = taskIdsFromCompletionKey(completionKey);
1052
- if (agent.backgroundCompletionKeys.has(completionKey))
1053
- return;
1054
- if (taskIds.length > 0 && taskIds.every((taskId) => agent.backgroundCompletionKeys.has(taskId)))
1055
- return;
1056
- agent.backgroundCompletionKeys.add(completionKey);
1057
- for (const taskId of taskIds)
1058
- agent.backgroundCompletionKeys.add(taskId);
1059
- if (!agent.backgroundCompletionQueue.includes(completionKey)) {
1060
- agent.backgroundCompletionQueue.push(completionKey);
1061
- }
1062
- if (!agent.busy && !agent.turnInProgress && !agent.submitting && !agent.backgroundCompletionInFlight) {
1063
- scheduleBackgroundCompletionDrain(agent);
1064
- }
1065
- };
1066
- const recordDispatchedSubagent = (agent, taskId, outputFile) => {
1067
- const recordFile = writeDispatchedSubagentRecordFile(effectivePiStateDir(agent.config), taskId, agent.piSessionOwner);
1068
- agent.subagentLedger = noteDispatchedSubagent(agent.subagentLedger, { taskId, outputFile, recordFile });
1069
- persistSubagentLedger(agent);
1070
- armSubagentReconcileTimer(agent);
1071
- };
1072
- const recordTerminalSubagentNotification = (agent, completionKey, statuses) => {
1073
- let next = agent.subagentLedger;
1074
- for (const taskId of taskIdsFromCompletionKey(completionKey)) {
1075
- const status = statuses?.[taskId];
1076
- next = noteDispatchedSubagentTerminal(next, {
1077
- taskId,
1078
- status: status && status !== "dispatched" && status !== "orphaned" ? status : "completed",
1079
- wakeKey: completionKey,
1080
- });
1081
- }
1082
- agent.subagentLedger = next;
1083
- persistSubagentLedger(agent);
1084
- if (!hasActiveDispatchedSubagent(agent))
1085
- clearSubagentReconcileTimer(agent);
1086
- };
1087
- const reconcileSubagentLedger = (agent, input = {}) => {
1088
- if (agent.stopped)
1089
- return;
1090
- const result = reconcileDispatchedSubagents(agent.subagentLedger, {
1091
- probe: options.subagentRecordProbe ?? probePiSubagentRecord,
1092
- forceMissing: input.forceMissing === true,
1093
- missingReason: input.missingReason,
1094
- });
1095
- const previousLedger = agent.subagentLedger;
1096
- agent.subagentLedger = result.ledger;
1097
- if (result.orphaned.length > 0 || result.terminals.length > 0 || result.ledger !== previousLedger) {
1098
- persistSubagentLedger(agent);
1099
- }
1100
- for (const record of [...result.orphaned, ...result.terminals]) {
1101
- noteBackgroundCompletion(agent, record.wakeKey ?? record.taskId);
1102
- }
1103
- if (!hasActiveDispatchedSubagent(agent))
1104
- clearSubagentReconcileTimer(agent);
1105
- else
1106
- armSubagentReconcileTimer(agent);
1107
- };
1108
- const armSubagentReconcileTimer = (agent) => {
1109
- if (agent.stopped || agent.subagentReconcileTimer || subagentReconcileIntervalMs === 0)
1110
- return;
1111
- if (!hasActiveDispatchedSubagent(agent))
1112
- return;
1113
- agent.subagentReconcileTimer = setInterval(() => {
1114
- if (agent.stopped) {
1115
- clearSubagentReconcileTimer(agent);
1116
- return;
1117
- }
1118
- reconcileSubagentLedger(agent);
1119
- }, subagentReconcileIntervalMs);
1120
- agent.subagentReconcileTimer.unref?.();
1121
- };
1122
789
  const scheduleRecreate = (agent, reason) => {
1123
790
  if (agent.stopped || agent.retryTimer || agent.starting || agent.session)
1124
791
  return;
@@ -1140,11 +807,7 @@ export function createRuntimeHost(options) {
1140
807
  const proactive = session ? proactivelyCompactPiAtIdle(agent, session) : null;
1141
808
  if (proactive)
1142
809
  await proactive;
1143
- reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "pi session gone" });
1144
- enqueueUndeliveredTerminalWakes(agent);
1145
810
  await retryPending(agent);
1146
- if (agent.backgroundCompletionQueue.length > 0)
1147
- scheduleBackgroundCompletionDrain(agent);
1148
811
  }).catch((error) => {
1149
812
  const readiness = error instanceof RuntimePrerequisiteError ? error.readiness
1150
813
  : classifyRuntimePrerequisite(agent.adapter.id, error);
@@ -1167,7 +830,6 @@ export function createRuntimeHost(options) {
1167
830
  agent.busy = false;
1168
831
  agent.submitting = false;
1169
832
  agent.generation += 1;
1170
- rearmBackgroundCompletion(agent);
1171
833
  agent.recreateReason = reason;
1172
834
  if (agent.stabilityTimer)
1173
835
  clearTimeout(agent.stabilityTimer);
@@ -1177,14 +839,11 @@ export function createRuntimeHost(options) {
1177
839
  record.status = "pending";
1178
840
  emitConsumed(agent, persist(agent));
1179
841
  emit({ type: "agent-status", agentId: agent.config.agentId, status: "error", error: reason });
1180
- // Close first so the shutdown sweep can bridge consumed or terminal state
1181
- // before force-missing reconcile treats a plain sidecar as orphaned.
1182
842
  void session.close(`replace after ${reason}`)
1183
843
  .catch((error) => log("runtime close after replacement failed", String(error)))
1184
844
  .finally(() => {
1185
845
  if (agent.stopped)
1186
846
  return;
1187
- reconcileAfterSessionSwap(agent);
1188
847
  scheduleRecreate(agent, reason);
1189
848
  });
1190
849
  };
@@ -1196,7 +855,6 @@ export function createRuntimeHost(options) {
1196
855
  agent.busy = false;
1197
856
  agent.submitting = false;
1198
857
  agent.generation += 1;
1199
- rearmBackgroundCompletion(agent);
1200
858
  if (agent.stabilityTimer)
1201
859
  clearTimeout(agent.stabilityTimer);
1202
860
  agent.stabilityTimer = null;
@@ -1211,8 +869,6 @@ export function createRuntimeHost(options) {
1211
869
  const recovery = (async () => {
1212
870
  try {
1213
871
  await session.close("runtime configuration error");
1214
- if (!agent.stopped)
1215
- reconcileAfterSessionSwap(agent);
1216
872
  const result = agent.adapter.recoverConfigurationError
1217
873
  ? await agent.adapter.recoverConfigurationError(message)
1218
874
  : { recovered: false, reason: "runtime adapter does not support automatic configuration recovery" };
@@ -1489,16 +1145,6 @@ export function createRuntimeHost(options) {
1489
1145
  // closed instead of inheriting a previous turn's chat or reminder.
1490
1146
  agent.stateStore?.clearCurrentInboxSource?.();
1491
1147
  finalizeReminderContexts(agent, "Runtime turn ended without a matching Feishu write");
1492
- if (agent.backgroundCompletionInFlight) {
1493
- if (agent.turnHadFailure)
1494
- failBackgroundCompletionWake(agent);
1495
- else
1496
- commitBackgroundCompletion(agent);
1497
- }
1498
- if (agent.backgroundCompletionQueue.length > 0 && !agent.backgroundCompletionRetryTimer) {
1499
- scheduleBackgroundCompletionDrain(agent);
1500
- }
1501
- reconcileSubagentLedger(agent);
1502
1148
  telemetry?.runtimeEvent(agent.config.agentId, event);
1503
1149
  if (recoveredAuthentication) {
1504
1150
  clearAuthFailure(agent);
@@ -1516,34 +1162,12 @@ export function createRuntimeHost(options) {
1516
1162
  void (proactive ?? Promise.resolve("noop")).then(() => retryPending(agent));
1517
1163
  queueMicrotask(() => { void scanAndPromoteAcceptedInboxUpdates(agent); });
1518
1164
  }
1519
- else if (event.type === "runtime-observation") {
1520
- if (event.phase === "background_dispatched" && typeof event.taskId === "string" && event.taskId) {
1521
- recordDispatchedSubagent(agent, event.taskId, typeof event.outputFile === "string" ? event.outputFile : undefined);
1522
- }
1523
- else if (event.phase === "completed" && typeof event.completionKey === "string") {
1524
- recordTerminalSubagentNotification(agent, event.completionKey, event.completionStatuses);
1525
- if (event.handledInTurn === true) {
1526
- acknowledgeSubagentWake(agent, event.completionKey);
1527
- dropQueuedBackgroundCompletionFallback(agent, event.completionKey);
1528
- }
1529
- else {
1530
- noteBackgroundCompletion(agent, event.completionKey);
1531
- }
1532
- }
1533
- }
1534
1165
  else if (event.type === "activity") {
1535
1166
  if (agent.turnInProgress && event.activity !== "internal")
1536
1167
  agent.turnHadAuthenticatedOutput = true;
1537
1168
  emit({ type: "activity", agentId: agent.config.agentId, activity: event.activity, activityKind: event.activity });
1538
1169
  }
1539
1170
  else if (event.type === "input-error") {
1540
- if (agent.backgroundCompletionInFlight
1541
- && (!event.inputId || !agent.backgroundCompletionWakeInputId
1542
- || event.inputId === agent.backgroundCompletionWakeInputId)
1543
- && event.willRetry !== true) {
1544
- agent.turnHadFailure = true;
1545
- failBackgroundCompletionWake(agent, event);
1546
- }
1547
1171
  const record = event.inputId
1548
1172
  ? agent.records.get(event.inputId) ?? [...agent.records.values()].find((candidate) => candidate.input.inputId === event.inputId)
1549
1173
  : undefined;
@@ -1673,7 +1297,6 @@ export function createRuntimeHost(options) {
1673
1297
  throw new Error("stale runtime session creation");
1674
1298
  }
1675
1299
  agent.session = session;
1676
- agent.piSessionOwner = sessionEnv[PI_SUBAGENT_SESSION_OWNER_ENV];
1677
1300
  session.subscribe((event) => observe(agent, session, event));
1678
1301
  if (agent.session !== session)
1679
1302
  return session;
@@ -1819,9 +1442,7 @@ export function createRuntimeHost(options) {
1819
1442
  ...(agent.previousSession ? { previousSession: agent.previousSession } : {}) });
1820
1443
  }
1821
1444
  emit({ type: "agent-status", agentId, status: "active", readiness });
1822
- agent.piSessionOwner = probeEnv[PI_SUBAGENT_SESSION_OWNER_ENV];
1823
1445
  await oldSession.close("Pi context fallback committed").catch((error) => log("previous Pi session close after fallback failed", String(error)));
1824
- reconcileAfterSessionSwap(agent);
1825
1446
  const proactive = proactivelyCompactPiAtIdle(agent, fresh);
1826
1447
  void (proactive ?? Promise.resolve("noop")).then(() => retryPending(agent));
1827
1448
  return { generationChanged: true, sessionChanged: oldSessionId !== fresh.sessionId, turns: 0,
@@ -1891,10 +1512,6 @@ export function createRuntimeHost(options) {
1891
1512
  clearTimeout(previous.retryTimer);
1892
1513
  if (previous.stabilityTimer)
1893
1514
  clearTimeout(previous.stabilityTimer);
1894
- if (previous.backgroundCompletionRetryTimer)
1895
- clearTimeout(previous.backgroundCompletionRetryTimer);
1896
- if (previous.subagentReconcileTimer)
1897
- clearInterval(previous.subagentReconcileTimer);
1898
1515
  const previousFailure = currentAuthFailure(previous);
1899
1516
  const scoped = Boolean(previousFailure
1900
1517
  && authFailureAppliesTo({
@@ -1906,7 +1523,6 @@ export function createRuntimeHost(options) {
1906
1523
  ...previous, config, adapter, session, launchId: crypto.randomUUID(), busy: false, submitting: false,
1907
1524
  starting: null, retryAfterSubmit: false, generation: 0, poller: null, retryTimer: null,
1908
1525
  recreateAttempts: 0, stabilityTimer: null, recreateReason: null, stopped: false,
1909
- backgroundCompletionRetryTimer: null, subagentReconcileTimer: null,
1910
1526
  disabledReason: null, configurationRecovery: null,
1911
1527
  authFailureActive: scoped && previous.authFailureActive,
1912
1528
  authFailureKind: scoped ? previous.authFailureKind : null,
@@ -1928,14 +1544,8 @@ export function createRuntimeHost(options) {
1928
1544
  if (!candidate.authFailureActive) {
1929
1545
  emit({ type: "agent-status", agentId: config.agentId, status: "active", readiness: candidate.readiness ?? readiness });
1930
1546
  }
1931
- candidate.piSessionOwner = stageEnv[PI_SUBAGENT_SESSION_OWNER_ENV];
1932
- // Close first so the old session's shutdown sweep can bridge consumed
1933
- // terminals before force-missing reconcile. Commit also clears any
1934
- // inherited backoff timer; reschedule so a queued completion still
1935
- // drains on the new session.
1936
1547
  await previous.session?.close("runtime candidate committed")
1937
1548
  .catch((error) => log("previous runtime close after candidate commit failed", String(error)));
1938
- reconcileAfterSessionSwap(candidate);
1939
1549
  },
1940
1550
  rollback,
1941
1551
  };
@@ -2038,10 +1648,8 @@ export function createRuntimeHost(options) {
2038
1648
  ...(agent.authFailureActive ? { error: `${agent.readiness?.reason} ${agent.readiness?.nextAction}` } : {}),
2039
1649
  readiness: agent.authFailureActive ? agent.readiness ?? readiness : readiness,
2040
1650
  });
2041
- agent.piSessionOwner = probeEnv[PI_SUBAGENT_SESSION_OWNER_ENV];
2042
1651
  await oldSession.close("fresh session reset committed")
2043
1652
  .catch((error) => log("previous runtime close after fresh reset failed", String(error)));
2044
- reconcileAfterSessionSwap(agent);
2045
1653
  return { generationChanged: true, sessionChanged: oldSessionId !== fresh.sessionId, turns: 0,
2046
1654
  runtimeReady: true, pendingCount: 0, sessionId: fresh.sessionId };
2047
1655
  },
@@ -2202,10 +1810,8 @@ export function createRuntimeHost(options) {
2202
1810
  readiness: agent.authFailureActive ? agent.readiness ?? readiness : readiness,
2203
1811
  });
2204
1812
  durableRollback = null;
2205
- agent.piSessionOwner = probeEnv[PI_SUBAGENT_SESSION_OWNER_ENV];
2206
1813
  await oldSession.close("context-window recovery committed")
2207
1814
  .catch((error) => log("previous runtime close after context-window recovery failed", String(error)));
2208
- reconcileAfterSessionSwap(agent);
2209
1815
  const proactive = proactivelyCompactPiAtIdle(agent, fresh);
2210
1816
  void (proactive ?? Promise.resolve("noop")).then(() => retryPending(agent));
2211
1817
  return { generationChanged: true, sessionChanged: oldSessionId !== fresh.sessionId, turns: 0,
@@ -2291,11 +1897,7 @@ export function createRuntimeHost(options) {
2291
1897
  previousSession: loadPersistedPreviousSession(config, stateStore), readiness: null,
2292
1898
  turnInProgress: false, turnHadFailure: false, turnHadAuthenticatedOutput: false, authFailureActive: false,
2293
1899
  authFailureKind: null, authFailureProvider: null,
2294
- promotedInboxUpdateIds: new Set(),
2295
- backgroundCompletionQueue: [], backgroundCompletionKeys: new Set(),
2296
- backgroundCompletionInFlight: null, backgroundCompletionWakeInputId: null,
2297
- backgroundCompletionRejectStreak: 0, backgroundCompletionRetryTimer: null,
2298
- subagentLedger: loadSubagentLedger(config), subagentReconcileTimer: null };
1900
+ promotedInboxUpdateIds: new Set() };
2299
1901
  restoreAuthFailure(agent);
2300
1902
  const startupConsumed = [];
2301
1903
  const startupQuarantined = [];
@@ -2329,8 +1931,6 @@ export function createRuntimeHost(options) {
2329
1931
  await ensureSession(agent);
2330
1932
  if (agent.stopped)
2331
1933
  return;
2332
- reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "runtime restarted" });
2333
- enqueueUndeliveredTerminalWakes(agent);
2334
1934
  await recoverStalePiCompaction(agent);
2335
1935
  const startupSession = agent.session;
2336
1936
  const proactive = startupSession ? proactivelyCompactPiAtIdle(agent, startupSession) : null;
@@ -2347,8 +1947,6 @@ export function createRuntimeHost(options) {
2347
1947
  const reason = error instanceof Error ? error.message : String(error);
2348
1948
  const transient = error instanceof RuntimePrerequisiteError && error.readiness.state === "unavailable";
2349
1949
  agent.disabledReason = transient ? null : reason;
2350
- reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "runtime restarted" });
2351
- enqueueUndeliveredTerminalWakes(agent);
2352
1950
  if (transient) {
2353
1951
  recoveringCount += 1;
2354
1952
  scheduleRecreate(agent, reason);
@@ -2460,10 +2058,6 @@ export function createRuntimeHost(options) {
2460
2058
  clearTimeout(agent.retryTimer);
2461
2059
  if (agent.stabilityTimer)
2462
2060
  clearTimeout(agent.stabilityTimer);
2463
- if (agent.backgroundCompletionRetryTimer)
2464
- clearTimeout(agent.backgroundCompletionRetryTimer);
2465
- if (agent.subagentReconcileTimer)
2466
- clearInterval(agent.subagentReconcileTimer);
2467
2061
  if (agent.busy)
2468
2062
  await agent.session?.cancel(reason);
2469
2063
  await agent.session?.close(reason);
@@ -2476,9 +2070,5 @@ export function createRuntimeHost(options) {
2476
2070
  },
2477
2071
  async shutdown(reason) { await Promise.allSettled([...managed.keys()].map((id) => this.stop(id, reason))); },
2478
2072
  subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); },
2479
- getDispatchedSubagent(agentId, taskId) {
2480
- const agent = managed.get(agentId);
2481
- return agent ? lookupDispatchedSubagent(agent.subagentLedger, taskId) : null;
2482
- },
2483
2073
  };
2484
2074
  }
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "larkin",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "private": false,
5
5
  "larkinPackageRole": "npm-published",
6
6
  "files": [
7
7
  "dist/",
8
- "patches/",
9
8
  "assets/",
10
9
  "scripts/npm/",
11
10
  "README.md",
@@ -70,8 +69,7 @@
70
69
  "test:live:runtime-agent-interface-v2:read-only": "LARKIN_RUN_RUNTIME_AGENT_INTERFACE_V2_LIVE=1 bun test --max-concurrency 1 test/live/runtime-agent-interface-v2-live.test.mjs",
71
70
  "test:live:runtime-agent-interface-v2:hold-host": "bun run build && bun run test/live/runtime-agent-interface-v2-hold-host.mjs app/runtime-process.mjs",
72
71
  "test:eval:long-running-im-progress": "bun run build && bun test --max-concurrency 1 test/live/long-running-im-progress-live.test.mjs",
73
- "test:eval:pi-subagents-background": "bun run build && bun test --max-concurrency 1 test/live/pi-subagents-background-live.test.mjs",
74
- "test:eval:pi-bash-timeout": "bun run build && bun test --max-concurrency 1 test/live/pi-bash-timeout-live.test.mjs",
72
+ "test:eval:pi-tmux-bash": "bun run build && LARKIN_RUN_PI_TMUX_BASH_EVAL=1 bun test --max-concurrency 1 test/live/pi-tmux-bash-live.test.mjs",
75
73
  "test:eval:runtime-agent-interface-v2": "bun test --max-concurrency 1 test/unit/evals/runtime-agent-interface-v2.test.mjs",
76
74
  "test:eval:authoritative-freshness-gate": "bun test --max-concurrency 1 test/unit/evals/authoritative-freshness-gate.test.mjs",
77
75
  "test:eval:protected-message-recall": "bun run build && bun test --max-concurrency 1 test/unit/evals/protected-message-recall.test.mjs test/unit/app/lark-cli-launcher.test.mjs",
@@ -95,7 +93,6 @@
95
93
  "@opentelemetry/sdk-trace-base": "^2.10.0",
96
94
  "@opentelemetry/sdk-trace-node": "^2.10.0",
97
95
  "@radix-ui/react-dialog": "^1.1.21",
98
- "@tintinweb/pi-subagents": "0.14.3",
99
96
  "lucide-react": "^1.26.0",
100
97
  "proper-lockfile": "4.1.2",
101
98
  "qrcode-terminal": "^0.12.0",
@@ -119,8 +116,5 @@
119
116
  "typescript": "^7.0.2",
120
117
  "vite": "^8.1.5",
121
118
  "vitest": "^4.1.10"
122
- },
123
- "patchedDependencies": {
124
- "@tintinweb/pi-subagents@0.14.3": "patches/@tintinweb%2Fpi-subagents@0.14.3.patch"
125
119
  }
126
120
  }