larkin 0.4.2 → 0.4.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.
- package/README.md +5 -1
- package/artifacts/release/THIRD_PARTY_NOTICES.txt +218 -228
- package/assets/readme/larkin-github-cover.png +0 -0
- package/dist/agent/agent-state-store.cjs +1 -2
- package/dist/agent/agent-state-store.mjs +1 -2
- package/dist/app/agent-config.mjs +73 -6
- package/dist/app/agent-readiness.cjs +22 -2
- package/dist/app/agent-readiness.mjs +20 -2
- package/dist/app/binary-entry.mjs +6 -0
- package/dist/app/cli.mjs +4 -0
- package/dist/app/run.mjs +8 -0
- package/dist/app/runtime-process.mjs +13 -1
- package/dist/dashboard/dashboard-view-model.mjs +12 -3
- package/dist/feishu/host-shell.cjs +33 -10
- package/dist/feishu/host-shell.mjs +34 -11
- package/dist/platform/config.cjs +406 -33
- package/dist/platform/config.mjs +405 -33
- package/dist/platform/process-boundary-trace.cjs +108 -0
- package/dist/platform/process-boundary-trace.mjs +102 -0
- package/dist/runtime/pi-artifact-provenance.cjs +147 -0
- package/dist/runtime/pi-artifact-provenance.mjs +139 -0
- package/dist/runtime/pi-compaction-recovery.cjs +120 -40
- package/dist/runtime/pi-compaction-recovery.mjs +117 -40
- package/dist/runtime/pi-model-catalog.cjs +2 -2
- package/dist/runtime/pi-model-catalog.mjs +2 -2
- package/dist/runtime/pi-profile-migration.cjs +556 -0
- package/dist/runtime/pi-profile-migration.mjs +515 -0
- package/dist/runtime/pi-provider-config.cjs +1 -1
- package/dist/runtime/pi-provider-config.mjs +1 -1
- package/dist/runtime/runtime-adapters.mjs +97 -11
- package/dist/runtime/runtime-host.cjs +162 -39
- package/dist/runtime/runtime-host.mjs +163 -40
- package/dist/runtime/runtime-readiness.cjs +6 -4
- package/dist/runtime/runtime-readiness.mjs +6 -4
- package/package.json +3 -3
|
@@ -8,7 +8,7 @@ import { buildStrictProviderErrorInput, classifyStrictProviderError } from "./pr
|
|
|
8
8
|
import { classifyRuntimePrerequisite, providerAuthenticationFailureReadiness, RuntimePrerequisiteError, } from "./runtime-readiness.mjs";
|
|
9
9
|
import { resolveOfficialLarkCli } from "../app/official-lark-cli.mjs";
|
|
10
10
|
import { assertAgentWorkspaceBound, managedLarkCliEnv } from "../app/agent-lark-cli-workspace.mjs";
|
|
11
|
-
import { PiCompactionBreaker, PiCompactionRecoveryMachine } from "./pi-compaction-recovery.mjs";
|
|
11
|
+
import { isPiNativeCompactionRequired, PiCompactionBreaker, PiCompactionRecoveryMachine, } from "./pi-compaction-recovery.mjs";
|
|
12
12
|
export class RuntimeSessionResetError extends Error {
|
|
13
13
|
code;
|
|
14
14
|
pendingCount;
|
|
@@ -388,18 +388,22 @@ export function createRuntimeHost(options) {
|
|
|
388
388
|
};
|
|
389
389
|
const resultFor = (agent, record, result) => {
|
|
390
390
|
if (result.status === "accepted") {
|
|
391
|
-
|
|
391
|
+
if (record.errorCategory !== "context_window") {
|
|
392
|
+
delete record.reason;
|
|
393
|
+
delete record.errorCategory;
|
|
394
|
+
}
|
|
392
395
|
delete record.retryable;
|
|
393
|
-
delete record.errorCategory;
|
|
394
396
|
const finalRecord = setRecord(agent, record, "accepted");
|
|
395
397
|
if (finalRecord.status !== "consumed")
|
|
396
398
|
emit({ type: "delivery", agentId: agent.config.agentId,
|
|
397
399
|
deliveryId: record.deliveryId, messageId: record.messageId, status: "accepted" });
|
|
398
400
|
return { status: "accepted", deliveryId: record.deliveryId };
|
|
399
401
|
}
|
|
400
|
-
|
|
402
|
+
if (record.errorCategory !== "context_window")
|
|
403
|
+
delete record.errorCategory;
|
|
401
404
|
const retryable = result.status === "deferred" || result.retryable;
|
|
402
|
-
record.
|
|
405
|
+
if (record.errorCategory !== "context_window")
|
|
406
|
+
record.reason = result.reason;
|
|
403
407
|
record.retryable = retryable;
|
|
404
408
|
const finalRecord = setRecord(agent, record, retryable ? "pending" : "error");
|
|
405
409
|
if (finalRecord.status === "consumed")
|
|
@@ -411,6 +415,22 @@ export function createRuntimeHost(options) {
|
|
|
411
415
|
: { status: "error", deliveryId: record.deliveryId, reason: result.reason, retryable: false };
|
|
412
416
|
};
|
|
413
417
|
const submit = async (agent, record, busy) => {
|
|
418
|
+
const proactive = agent.piProactiveCompaction
|
|
419
|
+
&& agent.piProactiveCompactionGeneration === agent.generation
|
|
420
|
+
&& agent.piProactiveCompactionSession === agent.session
|
|
421
|
+
? agent.piProactiveCompaction : null;
|
|
422
|
+
if (proactive && await proactive === "failed") {
|
|
423
|
+
return { status: "deferred", deliveryId: record.deliveryId, reason: "Pi proactive compaction failed for the current session generation" };
|
|
424
|
+
}
|
|
425
|
+
if (agent.adapter.id === "pi" && agent.piProactiveCompactionFailedGeneration === agent.generation) {
|
|
426
|
+
return { status: "deferred", deliveryId: record.deliveryId, reason: "Pi proactive compaction failed for the current session generation" };
|
|
427
|
+
}
|
|
428
|
+
if (agent.adapter.id === "pi" && !busy && !agent.busy && !agent.turnInProgress && agent.session) {
|
|
429
|
+
const idleGate = proactivelyCompactPiAtIdle(agent, agent.session);
|
|
430
|
+
if (idleGate && await idleGate === "failed") {
|
|
431
|
+
return { status: "deferred", deliveryId: record.deliveryId, reason: "Pi proactive compaction failed for the current session generation" };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
414
434
|
agent.submitting = true;
|
|
415
435
|
if (!busy)
|
|
416
436
|
agent.busy = true; // Reserve the turn before prompt() can yield.
|
|
@@ -450,9 +470,11 @@ export function createRuntimeHost(options) {
|
|
|
450
470
|
if (!busy)
|
|
451
471
|
agent.busy = false;
|
|
452
472
|
const reason = error instanceof Error ? error.message : String(error);
|
|
453
|
-
record.
|
|
473
|
+
if (record.errorCategory !== "context_window") {
|
|
474
|
+
record.reason = reason;
|
|
475
|
+
delete record.errorCategory;
|
|
476
|
+
}
|
|
454
477
|
record.retryable = true;
|
|
455
|
-
delete record.errorCategory;
|
|
456
478
|
setRecord(agent, record, "pending");
|
|
457
479
|
emit({ type: "delivery", agentId: agent.config.agentId, deliveryId: record.deliveryId, messageId: record.messageId, status: "deferred", reason });
|
|
458
480
|
return { status: "deferred", deliveryId: record.deliveryId, reason };
|
|
@@ -466,16 +488,41 @@ export function createRuntimeHost(options) {
|
|
|
466
488
|
}
|
|
467
489
|
};
|
|
468
490
|
const retryPending = async (agent) => {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
491
|
+
if (agent.retryPendingInFlight)
|
|
492
|
+
return agent.retryPendingInFlight;
|
|
493
|
+
const run = (async () => {
|
|
494
|
+
reconcileExternalConsumption(agent);
|
|
495
|
+
const proactive = agent.piProactiveCompaction
|
|
496
|
+
&& agent.piProactiveCompactionGeneration === agent.generation
|
|
497
|
+
&& agent.piProactiveCompactionSession === agent.session
|
|
498
|
+
? agent.piProactiveCompaction : null;
|
|
499
|
+
if (proactive && await proactive === "failed")
|
|
500
|
+
return;
|
|
501
|
+
if (agent.submitting || agent.starting) {
|
|
502
|
+
agent.retryAfterSubmit = true;
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (agent.adapter.id === "pi" && agent.piProactiveCompactionFailedGeneration === agent.generation)
|
|
506
|
+
return;
|
|
507
|
+
if (agent.busy || agent.turnInProgress || !agent.session)
|
|
508
|
+
return;
|
|
509
|
+
if (agent.adapter.id === "pi") {
|
|
510
|
+
const idleGate = proactivelyCompactPiAtIdle(agent, agent.session);
|
|
511
|
+
if (idleGate && await idleGate === "failed")
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const record = [...agent.records.values()].find((candidate) => candidate.status === "pending" || candidate.status === "submitting");
|
|
515
|
+
if (record)
|
|
516
|
+
await submit(agent, record, false);
|
|
517
|
+
})();
|
|
518
|
+
agent.retryPendingInFlight = run;
|
|
519
|
+
try {
|
|
520
|
+
await run;
|
|
521
|
+
}
|
|
522
|
+
finally {
|
|
523
|
+
if (agent.retryPendingInFlight === run)
|
|
524
|
+
agent.retryPendingInFlight = null;
|
|
473
525
|
}
|
|
474
|
-
if (agent.busy || agent.turnInProgress || !agent.session)
|
|
475
|
-
return;
|
|
476
|
-
const record = [...agent.records.values()].find((candidate) => candidate.status === "pending" || candidate.status === "submitting");
|
|
477
|
-
if (record)
|
|
478
|
-
await submit(agent, record, false);
|
|
479
526
|
};
|
|
480
527
|
const scheduleRecreate = (agent, reason) => {
|
|
481
528
|
if (agent.stopped || agent.retryTimer || agent.starting || agent.session)
|
|
@@ -493,8 +540,12 @@ export function createRuntimeHost(options) {
|
|
|
493
540
|
agent.retryTimer = null;
|
|
494
541
|
if (agent.stopped)
|
|
495
542
|
return;
|
|
496
|
-
void ensureSession(agent).then(() => {
|
|
497
|
-
|
|
543
|
+
void ensureSession(agent).then(async () => {
|
|
544
|
+
const session = agent.session;
|
|
545
|
+
const proactive = session ? proactivelyCompactPiAtIdle(agent, session) : null;
|
|
546
|
+
if (proactive && await proactive === "failed")
|
|
547
|
+
return;
|
|
548
|
+
await retryPending(agent);
|
|
498
549
|
}).catch((error) => {
|
|
499
550
|
const readiness = error instanceof RuntimePrerequisiteError ? error.readiness
|
|
500
551
|
: classifyRuntimePrerequisite(agent.adapter.id, error);
|
|
@@ -605,7 +656,6 @@ export function createRuntimeHost(options) {
|
|
|
605
656
|
machine.retrySubmitted();
|
|
606
657
|
record.status = "pending";
|
|
607
658
|
record.retryable = true;
|
|
608
|
-
record.reason = "Pi manual compaction succeeded; retrying the same delivery input";
|
|
609
659
|
record.updatedAt = now();
|
|
610
660
|
persist(agent);
|
|
611
661
|
queueMicrotask(() => { if (!agent.stopped)
|
|
@@ -614,14 +664,19 @@ export function createRuntimeHost(options) {
|
|
|
614
664
|
else if (action === "fresh_session_fallback") {
|
|
615
665
|
agent.busy = false;
|
|
616
666
|
agent.turnInProgress = false;
|
|
617
|
-
|
|
618
|
-
|
|
667
|
+
const recoveryReason = machine.recordSnapshot.fallbackReason || "Pi compaction recovery fallback";
|
|
668
|
+
// Delivery proof must remain the exact provider-classified context error.
|
|
669
|
+
// Generic Pi recovery text belongs only in the breaker and logs.
|
|
670
|
+
if (record.errorCategory === "context_window" && typeof record.reason === "string") {
|
|
619
671
|
record.retryable = false;
|
|
620
|
-
record.errorCategory = "context_window";
|
|
621
672
|
setRecord(agent, record, "error");
|
|
673
|
+
if (!agent.compactionRecoveryInFlight.has(key)) {
|
|
674
|
+
agent.compactionRecoveryInFlight.add(key);
|
|
675
|
+
void recoverContextOverflowInternal(agent.config.agentId, key, recoveryReason, key)
|
|
676
|
+
.catch((error) => log("Pi context fallback failed", String(error)))
|
|
677
|
+
.finally(() => agent.compactionRecoveryInFlight.delete(key));
|
|
678
|
+
}
|
|
622
679
|
}
|
|
623
|
-
void recoverContextOverflowInternal(agent.config.agentId, key, machine.recordSnapshot.fallbackReason || "Pi compaction recovery fallback", key)
|
|
624
|
-
.catch((error) => log("Pi context fallback failed", String(error)));
|
|
625
680
|
}
|
|
626
681
|
},
|
|
627
682
|
});
|
|
@@ -656,6 +711,8 @@ export function createRuntimeHost(options) {
|
|
|
656
711
|
});
|
|
657
712
|
};
|
|
658
713
|
const recoverStalePiCompaction = async (agent) => {
|
|
714
|
+
if (!agent.stateStore)
|
|
715
|
+
return;
|
|
659
716
|
const breaker = agent.compactionBreaker;
|
|
660
717
|
if (!breaker)
|
|
661
718
|
return;
|
|
@@ -670,14 +727,59 @@ export function createRuntimeHost(options) {
|
|
|
670
727
|
|| candidate.input.inputId === stale.inputId);
|
|
671
728
|
if (!record)
|
|
672
729
|
return;
|
|
673
|
-
|
|
730
|
+
const recoveryReason = stale.fallbackReason || "Pi compaction recovery resumed after restart";
|
|
731
|
+
if (record.errorCategory !== "context_window" || typeof record.reason !== "string")
|
|
732
|
+
return;
|
|
674
733
|
record.retryable = false;
|
|
675
|
-
record.errorCategory = "context_window";
|
|
676
734
|
setRecord(agent, record, "error");
|
|
677
|
-
breaker.forceFallback(stale.key,
|
|
735
|
+
breaker.forceFallback(stale.key, recoveryReason);
|
|
678
736
|
agent.busy = false;
|
|
679
737
|
agent.turnInProgress = false;
|
|
680
|
-
await recoverContextOverflowInternal(agent.config.agentId, stale.key,
|
|
738
|
+
await recoverContextOverflowInternal(agent.config.agentId, stale.key, recoveryReason, stale.key);
|
|
739
|
+
};
|
|
740
|
+
const proactivelyCompactPiAtIdle = (agent, session) => {
|
|
741
|
+
if (agent.adapter.id !== "pi" || session.piPolicyManaged === false
|
|
742
|
+
|| !session.getContextUsage || !session.compact)
|
|
743
|
+
return null;
|
|
744
|
+
if (agent.session !== session || agent.stopped || agent.busy || agent.turnInProgress || agent.submitting || agent.starting
|
|
745
|
+
|| agent.compactionRecoveryInFlight.size > 0
|
|
746
|
+
|| agent.compactionMachines.size > 0 || agent.piOverflowCompactionFailed.size > 0)
|
|
747
|
+
return null;
|
|
748
|
+
if (agent.piProactiveCompactionFailedGeneration === agent.generation)
|
|
749
|
+
return null;
|
|
750
|
+
const generation = agent.generation;
|
|
751
|
+
if (agent.piProactiveCompaction && agent.piProactiveCompactionGeneration === generation
|
|
752
|
+
&& agent.piProactiveCompactionSession === session)
|
|
753
|
+
return agent.piProactiveCompaction;
|
|
754
|
+
const task = (async () => {
|
|
755
|
+
const before = await session.getContextUsage();
|
|
756
|
+
if (!before || agent.session !== session || agent.stopped || agent.busy || agent.turnInProgress || agent.submitting
|
|
757
|
+
|| agent.compactionRecoveryInFlight.size > 0 || agent.compactionMachines.size > 0
|
|
758
|
+
|| agent.piOverflowCompactionFailed.size > 0)
|
|
759
|
+
return "noop";
|
|
760
|
+
if (!isPiNativeCompactionRequired(before.tokens, before.contextWindow))
|
|
761
|
+
return "noop";
|
|
762
|
+
await session.compact();
|
|
763
|
+
const after = await session.getContextUsage();
|
|
764
|
+
if (!after || after.contextWindow !== before.contextWindow || after.tokens >= before.tokens) {
|
|
765
|
+
throw new Error("Pi proactive compaction did not reduce the verified context usage");
|
|
766
|
+
}
|
|
767
|
+
return "succeeded";
|
|
768
|
+
})();
|
|
769
|
+
const tracked = task.catch((error) => {
|
|
770
|
+
agent.piProactiveCompactionFailedGeneration = generation;
|
|
771
|
+
emit({ type: "agent-status", agentId: agent.config.agentId, status: "error",
|
|
772
|
+
error: "Pi proactive compaction failed; the current session generation is degraded" });
|
|
773
|
+
log("Pi proactive compaction failed", String(error));
|
|
774
|
+
return "failed";
|
|
775
|
+
}).finally(() => {
|
|
776
|
+
if (agent.piProactiveCompaction === tracked)
|
|
777
|
+
agent.piProactiveCompaction = null;
|
|
778
|
+
});
|
|
779
|
+
agent.piProactiveCompactionGeneration = generation;
|
|
780
|
+
agent.piProactiveCompactionSession = session;
|
|
781
|
+
agent.piProactiveCompaction = tracked;
|
|
782
|
+
return tracked;
|
|
681
783
|
};
|
|
682
784
|
const observe = (agent, session, event) => {
|
|
683
785
|
if (agent.session !== session)
|
|
@@ -719,7 +821,6 @@ export function createRuntimeHost(options) {
|
|
|
719
821
|
agent.config.sessionId = event.sessionId;
|
|
720
822
|
emit({ type: "session", agentId: agent.config.agentId, runtime: agent.adapter.id, sessionId: event.sessionId, launchId: agent.launchId,
|
|
721
823
|
...(event.model ? { model: event.model } : {}), ...(event.reasoningEffort ? { reasoningEffort: event.reasoningEffort } : {}) });
|
|
722
|
-
void retryPending(agent);
|
|
723
824
|
}
|
|
724
825
|
else if (event.type === "turn-start") {
|
|
725
826
|
agent.busy = true;
|
|
@@ -755,7 +856,11 @@ export function createRuntimeHost(options) {
|
|
|
755
856
|
};
|
|
756
857
|
emit({ type: "agent-status", agentId: agent.config.agentId, status: "active", readiness: agent.readiness });
|
|
757
858
|
}
|
|
758
|
-
|
|
859
|
+
const proactive = proactivelyCompactPiAtIdle(agent, session);
|
|
860
|
+
void (proactive ?? Promise.resolve("noop")).then((outcome) => {
|
|
861
|
+
if (outcome !== "failed")
|
|
862
|
+
return retryPending(agent);
|
|
863
|
+
});
|
|
759
864
|
}
|
|
760
865
|
else if (event.type === "activity") {
|
|
761
866
|
if (agent.turnInProgress && event.activity !== "internal")
|
|
@@ -781,8 +886,13 @@ export function createRuntimeHost(options) {
|
|
|
781
886
|
if (agent.piOverflowCompactionFailed.has(record.input.inputId)) {
|
|
782
887
|
agent.busy = false;
|
|
783
888
|
agent.turnInProgress = false;
|
|
784
|
-
|
|
785
|
-
|
|
889
|
+
const recoveryKey = `${record.deliveryId}:${record.input.inputId}`;
|
|
890
|
+
if (!agent.compactionRecoveryInFlight.has(recoveryKey)) {
|
|
891
|
+
agent.compactionRecoveryInFlight.add(recoveryKey);
|
|
892
|
+
void recoverContextOverflowInternal(agent.config.agentId, recoveryKey, "native Pi overflow compaction failed", recoveryKey)
|
|
893
|
+
.catch((error) => log("Pi native compaction fallback failed", String(error)))
|
|
894
|
+
.finally(() => agent.compactionRecoveryInFlight.delete(recoveryKey));
|
|
895
|
+
}
|
|
786
896
|
return;
|
|
787
897
|
}
|
|
788
898
|
// Pi emits this terminal error only after agent_settled; release the
|
|
@@ -967,7 +1077,6 @@ export function createRuntimeHost(options) {
|
|
|
967
1077
|
continue;
|
|
968
1078
|
candidate.status = "pending";
|
|
969
1079
|
candidate.retryable = true;
|
|
970
|
-
candidate.reason = "context-window recovery rearmed the retained Inbox delivery";
|
|
971
1080
|
candidate.updatedAt = now();
|
|
972
1081
|
}
|
|
973
1082
|
};
|
|
@@ -1013,7 +1122,11 @@ export function createRuntimeHost(options) {
|
|
|
1013
1122
|
}
|
|
1014
1123
|
emit({ type: "agent-status", agentId, status: "active", readiness });
|
|
1015
1124
|
await oldSession.close("Pi context fallback committed").catch((error) => log("previous Pi session close after fallback failed", String(error)));
|
|
1016
|
-
|
|
1125
|
+
const proactive = proactivelyCompactPiAtIdle(agent, fresh);
|
|
1126
|
+
void (proactive ?? Promise.resolve("noop")).then((outcome) => {
|
|
1127
|
+
if (outcome !== "failed")
|
|
1128
|
+
return retryPending(agent);
|
|
1129
|
+
});
|
|
1017
1130
|
return { generationChanged: true, sessionChanged: oldSessionId !== fresh.sessionId, turns: 0,
|
|
1018
1131
|
runtimeReady: true, pendingCount: remainingPendingCount, rearmedCount,
|
|
1019
1132
|
replayStatus: remainingPendingCount > 0 ? "pending" : "consumed", sessionId: fresh.sessionId };
|
|
@@ -1325,7 +1438,6 @@ export function createRuntimeHost(options) {
|
|
|
1325
1438
|
continue;
|
|
1326
1439
|
record.status = "pending";
|
|
1327
1440
|
record.retryable = true;
|
|
1328
|
-
record.reason = "context-window recovery rearmed the retained Inbox delivery";
|
|
1329
1441
|
record.updatedAt = now();
|
|
1330
1442
|
}
|
|
1331
1443
|
});
|
|
@@ -1340,7 +1452,11 @@ export function createRuntimeHost(options) {
|
|
|
1340
1452
|
durableRollback = null;
|
|
1341
1453
|
await oldSession.close("context-window recovery committed")
|
|
1342
1454
|
.catch((error) => log("previous runtime close after context-window recovery failed", String(error)));
|
|
1343
|
-
|
|
1455
|
+
const proactive = proactivelyCompactPiAtIdle(agent, fresh);
|
|
1456
|
+
void (proactive ?? Promise.resolve("noop")).then((outcome) => {
|
|
1457
|
+
if (outcome !== "failed")
|
|
1458
|
+
return retryPending(agent);
|
|
1459
|
+
});
|
|
1344
1460
|
return { generationChanged: true, sessionChanged: oldSessionId !== fresh.sessionId, turns: 0,
|
|
1345
1461
|
runtimeReady: true, pendingCount: rearmed.remainingPendingCount, rearmedCount: rearmed.rearmedCount,
|
|
1346
1462
|
replayStatus: rearmed.remainingPendingCount > 0 ? "pending" : "consumed", sessionId: fresh.sessionId };
|
|
@@ -1408,16 +1524,19 @@ export function createRuntimeHost(options) {
|
|
|
1408
1524
|
: readDeliveryFile(config.stateDir ? path.join(config.stateDir, "runtime-deliveries.json") : null);
|
|
1409
1525
|
const agent = { config, adapter: options.adapterFor(config.runtime), session: null,
|
|
1410
1526
|
launchId: crypto.randomUUID(), busy: false, submitting: false, starting: null, retryAfterSubmit: false,
|
|
1527
|
+
retryPendingInFlight: null,
|
|
1411
1528
|
records: new Map(persisted.records.map((record) => [record.deliveryId, record])),
|
|
1412
1529
|
byMessage: new Map(persisted.records.map((record) => [record.messageId, record.deliveryId])),
|
|
1413
1530
|
generation: 0, poller: null, retryTimer: null, recreateAttempts: 0,
|
|
1414
1531
|
stabilityTimer: null, recreateReason: null, stopped: false, disabledReason: null,
|
|
1415
1532
|
configurationRecovery: null, stateStore,
|
|
1416
|
-
compactionBreaker: config.runtime === "pi"
|
|
1533
|
+
compactionBreaker: config.runtime === "pi" && stateStore
|
|
1417
1534
|
? new PiCompactionBreaker(config.stateDir ?? path.join(config.workspaceDir, ".larkin", "agents", config.agentId), {
|
|
1418
|
-
withLock:
|
|
1535
|
+
withLock: (operation) => stateStore.withInboxTransaction(operation),
|
|
1419
1536
|
}) : undefined,
|
|
1420
|
-
compactionMachines: new Map(),
|
|
1537
|
+
compactionMachines: new Map(), compactionRecoveryInFlight: new Set(), piOverflowCompactionFailed: new Set(),
|
|
1538
|
+
piProactiveCompaction: null, piProactiveCompactionGeneration: null, piProactiveCompactionSession: null,
|
|
1539
|
+
piProactiveCompactionFailedGeneration: null, readiness: null,
|
|
1421
1540
|
turnInProgress: false, turnHadFailure: false, turnHadAuthenticatedOutput: false, authFailureActive: false };
|
|
1422
1541
|
const startupConsumed = [];
|
|
1423
1542
|
const startupQuarantined = [];
|
|
@@ -1450,8 +1569,12 @@ export function createRuntimeHost(options) {
|
|
|
1450
1569
|
try {
|
|
1451
1570
|
await ensureSession(agent);
|
|
1452
1571
|
await recoverStalePiCompaction(agent);
|
|
1572
|
+
const startupSession = agent.session;
|
|
1573
|
+
const proactive = startupSession ? proactivelyCompactPiAtIdle(agent, startupSession) : null;
|
|
1574
|
+
const proactiveOutcome = proactive ? await proactive : "noop";
|
|
1453
1575
|
activeCount += 1;
|
|
1454
|
-
|
|
1576
|
+
if (proactiveOutcome !== "failed")
|
|
1577
|
+
await retryPending(agent);
|
|
1455
1578
|
}
|
|
1456
1579
|
catch (error) {
|
|
1457
1580
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -45,6 +45,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
45
45
|
const node_path_1 = __importDefault(require("node:path"));
|
|
46
46
|
const node_child_process_1 = require("node:child_process");
|
|
47
47
|
const pi_provider_config_js_1 = require("./pi-provider-config.cjs");
|
|
48
|
+
const process_boundary_trace_js_1 = require("../platform/process-boundary-trace.cjs");
|
|
48
49
|
function safeProviderLabel(value) {
|
|
49
50
|
const provider = typeof value === "string" ? value.trim() : "";
|
|
50
51
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(provider) ? provider : null;
|
|
@@ -122,8 +123,8 @@ function selectedCommand(options) {
|
|
|
122
123
|
return options.env?.LARKIN_CODEX_COMMAND || process.env.LARKIN_CODEX_COMMAND || "codex";
|
|
123
124
|
return options.env?.LARKIN_CLAUDE_COMMAND || process.env.LARKIN_CLAUDE_COMMAND || "claude";
|
|
124
125
|
}
|
|
125
|
-
function executableVersion(executable, env) {
|
|
126
|
-
const result = (0, node_child_process_1.spawnSync)(executable, ["--version"], { env, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024 });
|
|
126
|
+
function executableVersion(executable, env, commandArgs = []) {
|
|
127
|
+
const result = (0, node_child_process_1.spawnSync)(executable, [...commandArgs, "--version"], { env, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024 });
|
|
127
128
|
if (result.status !== 0)
|
|
128
129
|
return undefined;
|
|
129
130
|
return String(result.stdout || result.stderr || "").replace(/[\r\n]+/g, " ").trim().slice(0, 120) || undefined;
|
|
@@ -139,6 +140,7 @@ async function probeNativeRuntimeReadiness(options) {
|
|
|
139
140
|
return { runtime: "pi", state: "ready", executable: process.execPath, version: `official-pi ${pi_provider_config_js_1.BUNDLED_PI_VERSION} (bundled)` };
|
|
140
141
|
}
|
|
141
142
|
catch (error) {
|
|
143
|
+
(0, process_boundary_trace_js_1.traceProcessBoundary)(env, "readiness:builtin-pi-failure", { configDir: env.LARKIN_CONFIG_DIR, agentId: options.agentId, targetDir: options.agentId && env.LARKIN_CONFIG_DIR ? (0, pi_provider_config_js_1.piAgentDirectory)(env.LARKIN_CONFIG_DIR, options.agentId) : undefined, error });
|
|
142
144
|
return { runtime: "pi", state: "unauthenticated", reason: error instanceof Error ? error.message : String(error),
|
|
143
145
|
nextAction: "重新运行 larkin setup,选择内置 Pi 并配置有效 API Key。" };
|
|
144
146
|
}
|
|
@@ -153,11 +155,11 @@ async function probeNativeRuntimeReadiness(options) {
|
|
|
153
155
|
? "Install Pi and ensure `pi` is on PATH, or set LARKIN_PI_COMMAND."
|
|
154
156
|
: `Install ${options.runtime} and ensure it is on PATH.`,
|
|
155
157
|
};
|
|
156
|
-
const version = executableVersion(executable, env);
|
|
158
|
+
const version = executableVersion(executable, env, options.commandArgs);
|
|
157
159
|
try {
|
|
158
160
|
if (options.runtime === "pi") {
|
|
159
161
|
const { discoverPiModelCatalog } = await Promise.resolve().then(() => __importStar(require("./pi-model-catalog.cjs")));
|
|
160
|
-
await discoverPiModelCatalog({ cwd: options.cwd, command: executable, env });
|
|
162
|
+
await discoverPiModelCatalog({ cwd: options.cwd, command: executable, commandArgs: options.commandArgs, env });
|
|
161
163
|
}
|
|
162
164
|
else if (options.runtime === "codex") {
|
|
163
165
|
const { discoverCodexModelCatalog } = await Promise.resolve().then(() => __importStar(require("./codex-model-catalog.cjs")));
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { assertBuiltinPiAgentDirectory, BUNDLED_PI_VERSION, piAgentDirectory } from "./pi-provider-config.mjs";
|
|
5
|
+
import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
|
|
5
6
|
function safeProviderLabel(value) {
|
|
6
7
|
const provider = typeof value === "string" ? value.trim() : "";
|
|
7
8
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(provider) ? provider : null;
|
|
@@ -78,8 +79,8 @@ function selectedCommand(options) {
|
|
|
78
79
|
return options.env?.LARKIN_CODEX_COMMAND || process.env.LARKIN_CODEX_COMMAND || "codex";
|
|
79
80
|
return options.env?.LARKIN_CLAUDE_COMMAND || process.env.LARKIN_CLAUDE_COMMAND || "claude";
|
|
80
81
|
}
|
|
81
|
-
function executableVersion(executable, env) {
|
|
82
|
-
const result = spawnSync(executable, ["--version"], { env, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024 });
|
|
82
|
+
function executableVersion(executable, env, commandArgs = []) {
|
|
83
|
+
const result = spawnSync(executable, [...commandArgs, "--version"], { env, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024 });
|
|
83
84
|
if (result.status !== 0)
|
|
84
85
|
return undefined;
|
|
85
86
|
return String(result.stdout || result.stderr || "").replace(/[\r\n]+/g, " ").trim().slice(0, 120) || undefined;
|
|
@@ -95,6 +96,7 @@ export async function probeNativeRuntimeReadiness(options) {
|
|
|
95
96
|
return { runtime: "pi", state: "ready", executable: process.execPath, version: `official-pi ${BUNDLED_PI_VERSION} (bundled)` };
|
|
96
97
|
}
|
|
97
98
|
catch (error) {
|
|
99
|
+
traceProcessBoundary(env, "readiness:builtin-pi-failure", { configDir: env.LARKIN_CONFIG_DIR, agentId: options.agentId, targetDir: options.agentId && env.LARKIN_CONFIG_DIR ? piAgentDirectory(env.LARKIN_CONFIG_DIR, options.agentId) : undefined, error });
|
|
98
100
|
return { runtime: "pi", state: "unauthenticated", reason: error instanceof Error ? error.message : String(error),
|
|
99
101
|
nextAction: "重新运行 larkin setup,选择内置 Pi 并配置有效 API Key。" };
|
|
100
102
|
}
|
|
@@ -109,11 +111,11 @@ export async function probeNativeRuntimeReadiness(options) {
|
|
|
109
111
|
? "Install Pi and ensure `pi` is on PATH, or set LARKIN_PI_COMMAND."
|
|
110
112
|
: `Install ${options.runtime} and ensure it is on PATH.`,
|
|
111
113
|
};
|
|
112
|
-
const version = executableVersion(executable, env);
|
|
114
|
+
const version = executableVersion(executable, env, options.commandArgs);
|
|
113
115
|
try {
|
|
114
116
|
if (options.runtime === "pi") {
|
|
115
117
|
const { discoverPiModelCatalog } = await import("./pi-model-catalog.mjs");
|
|
116
|
-
await discoverPiModelCatalog({ cwd: options.cwd, command: executable, env });
|
|
118
|
+
await discoverPiModelCatalog({ cwd: options.cwd, command: executable, commandArgs: options.commandArgs, env });
|
|
117
119
|
}
|
|
118
120
|
else if (options.runtime === "codex") {
|
|
119
121
|
const { discoverCodexModelCatalog } = await import("./codex-model-catalog.mjs");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "larkin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"larkinPackageRole": "npm-published",
|
|
6
6
|
"files": [
|
|
@@ -84,14 +84,14 @@
|
|
|
84
84
|
"test:live:agent-cli-routing-codex": "bun run build && LARKIN_RUN_AGENT_CLI_ROUTING_CODEX_LIVE=1 bun test --max-concurrency 1 test/live/agent-cli-routing-codex-live.test.mjs"
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
88
|
-
"@tintinweb/pi-subagents": "0.14.3",
|
|
87
|
+
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
89
88
|
"@larksuite/channel": "^0.4.0",
|
|
90
89
|
"@opentelemetry/api": "^1.9.1",
|
|
91
90
|
"@opentelemetry/resources": "^2.10.0",
|
|
92
91
|
"@opentelemetry/sdk-trace-base": "^2.10.0",
|
|
93
92
|
"@opentelemetry/sdk-trace-node": "^2.10.0",
|
|
94
93
|
"@radix-ui/react-dialog": "^1.1.21",
|
|
94
|
+
"@tintinweb/pi-subagents": "0.14.3",
|
|
95
95
|
"lucide-react": "^1.26.0",
|
|
96
96
|
"proper-lockfile": "4.1.2",
|
|
97
97
|
"qrcode-terminal": "^0.12.0",
|