larkin 0.4.28 → 0.5.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 +11 -0
- package/README.md +5 -9
- package/artifacts/release/THIRD_PARTY_NOTICES.txt +751 -4425
- package/dist/app/agent-cli.mjs +14 -2
- package/dist/app/agent-config.mjs +26 -84
- package/dist/app/binary-entry.mjs +0 -20
- package/dist/app/cli.mjs +8 -14
- package/dist/app/internal-command.cjs +0 -2
- package/dist/app/internal-command.mjs +0 -2
- package/dist/app/local-control.mjs +16 -3
- package/dist/app/runtime-model-directory.mjs +2 -16
- package/dist/app/runtime-process.mjs +42 -6
- package/dist/app/setup.mjs +52 -57
- package/dist/dashboard/dashboard-config-controller.mjs +83 -24
- package/dist/dashboard/dashboard-view-model.mjs +4 -7
- package/dist/dashboard/web/assets/dashboard.js +1 -1
- package/dist/feishu/host-shell.cjs +68 -17
- package/dist/feishu/host-shell.mjs +69 -18
- package/dist/platform/config.cjs +201 -138
- package/dist/platform/config.mjs +199 -138
- package/dist/runtime/pi-artifact-provenance.mjs +3 -3
- package/dist/runtime/pi-bash-timeout-extension.mjs +4 -5
- package/dist/runtime/pi-compaction-recovery.cjs +9 -11
- package/dist/runtime/pi-compaction-recovery.mjs +8 -10
- package/dist/runtime/pi-extension-api.mjs +1 -0
- package/dist/runtime/pi-model-catalog.cjs +7 -8
- package/dist/runtime/pi-model-catalog.mjs +7 -8
- package/dist/runtime/pi-subagent-injection.mjs +17 -4
- package/dist/runtime/runtime-adapters.mjs +41 -38
- package/dist/runtime/runtime-host.cjs +197 -20
- package/dist/runtime/runtime-host.mjs +198 -21
- package/dist/runtime/runtime-model-catalog.cjs +1 -1
- package/dist/runtime/runtime-model-catalog.mjs +1 -1
- package/dist/runtime/runtime-readiness.cjs +112 -49
- package/dist/runtime/runtime-readiness.mjs +103 -49
- package/dist/runtime/user-runtime.cjs +7 -24
- package/dist/runtime/user-runtime.mjs +7 -23
- package/dist/setup/bot-register.mjs +15 -123
- package/dist/setup/setup-agent-choice.mjs +48 -74
- package/dist/setup/setup-bind.mjs +51 -151
- package/dist/setup/setup-binding.mjs +6 -9
- package/package.json +3 -2
- package/dist/app/pi-auth-cli.mjs +0 -67
- package/dist/platform/process-boundary-trace.cjs +0 -108
- package/dist/runtime/builtin-pi-assets.cjs +0 -117
- package/dist/runtime/builtin-pi-assets.mjs +0 -105
- package/dist/runtime/pi-artifact-provenance.cjs +0 -147
- package/dist/runtime/pi-distribution-label.cjs +0 -6
- package/dist/runtime/pi-distribution-label.mjs +0 -3
- package/dist/runtime/pi-inline-extensions.mjs +0 -78
- package/dist/runtime/pi-official-auth.mjs +0 -491
- package/dist/runtime/pi-profile-migration.cjs +0 -764
- package/dist/runtime/pi-profile-migration.mjs +0 -719
- package/dist/runtime/pi-provider-config.cjs +0 -331
- package/dist/runtime/pi-provider-config.mjs +0 -314
|
@@ -6,7 +6,7 @@ import { auditReminderDelivery } from "../agent/reminder-delivery-audit.mjs";
|
|
|
6
6
|
import { isCanonicalInboxTarget, targetKeyOfInboxEnvelope } from "../agent/inbox-projection.mjs";
|
|
7
7
|
import { SpanKind } from "@opentelemetry/api";
|
|
8
8
|
import { buildStrictProviderErrorInput, classifyStrictProviderError } from "./provider-error-classifier.mjs";
|
|
9
|
-
import { classifyRuntimePrerequisite, providerAuthenticationFailureReadiness, RuntimePrerequisiteError, } from "./runtime-readiness.mjs";
|
|
9
|
+
import { authFailureAppliesTo, classifyPiMissingCredentialRejection, classifyRuntimePrerequisite, configuredProviderId, missingProviderCredentialReadiness, parsePersistedAuthFailure, providerAuthenticationFailureReadiness, readinessForPersistedAuthFailure, safeProviderId, RuntimePrerequisiteError, } from "./runtime-readiness.mjs";
|
|
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";
|
|
@@ -93,6 +93,107 @@ function persistPreviousSession(agent, previous) {
|
|
|
93
93
|
// In-memory ref still holds for this process; restart will miss it only if this write failed.
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
function authFailureScopeOf(agent) {
|
|
97
|
+
return {
|
|
98
|
+
runtime: agent.config.runtime,
|
|
99
|
+
model: agent.config.model,
|
|
100
|
+
adapterId: agent.adapter.id,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function currentAuthFailure(agent) {
|
|
104
|
+
if (!agent.authFailureActive)
|
|
105
|
+
return null;
|
|
106
|
+
const runtime = agent.adapter.id === "codex" || agent.adapter.id === "claude" || agent.adapter.id === "pi"
|
|
107
|
+
? agent.adapter.id : null;
|
|
108
|
+
if (!runtime)
|
|
109
|
+
return null;
|
|
110
|
+
return {
|
|
111
|
+
kind: agent.authFailureKind === "missing-provider" ? "missing-provider" : "generic",
|
|
112
|
+
runtime,
|
|
113
|
+
provider: agent.authFailureProvider,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function genericAuthProvider(agent, upstream) {
|
|
117
|
+
return safeProviderId(upstream?.provider) ?? configuredProviderId(agent.config.model);
|
|
118
|
+
}
|
|
119
|
+
function persistAuthFailure(agent) {
|
|
120
|
+
const store = agent.stateStore;
|
|
121
|
+
if (!store)
|
|
122
|
+
return;
|
|
123
|
+
try {
|
|
124
|
+
const write = () => {
|
|
125
|
+
const current = store.readJson("agentState", { sessions: {} });
|
|
126
|
+
if (Array.isArray(current.records) && !("sessions" in current))
|
|
127
|
+
return;
|
|
128
|
+
const { authFailureProvider: _legacy, authFailure: _scoped, ...rest } = current;
|
|
129
|
+
const failure = currentAuthFailure(agent);
|
|
130
|
+
if (!failure) {
|
|
131
|
+
if (!("authFailureProvider" in current) && !("authFailure" in current))
|
|
132
|
+
return;
|
|
133
|
+
store.writeJson("agentState", rest);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
store.writeJson("agentState", {
|
|
137
|
+
...rest,
|
|
138
|
+
authFailure: failure,
|
|
139
|
+
...(failure.kind === "missing-provider" && failure.provider
|
|
140
|
+
? { authFailureProvider: failure.provider } : {}),
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
if (store.withInboxTransaction)
|
|
144
|
+
store.withInboxTransaction(write);
|
|
145
|
+
else
|
|
146
|
+
write();
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// In-memory flags and the delivery ledger still hold for this process.
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function loadPersistedAuthFailure(store) {
|
|
153
|
+
if (!store)
|
|
154
|
+
return null;
|
|
155
|
+
try {
|
|
156
|
+
return parsePersistedAuthFailure(store.readJson("agentState", {}));
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function applyAuthFailure(agent, failure) {
|
|
163
|
+
agent.authFailureActive = true;
|
|
164
|
+
agent.authFailureKind = failure.kind;
|
|
165
|
+
agent.authFailureProvider = failure.provider ?? null;
|
|
166
|
+
agent.readiness = readinessForPersistedAuthFailure(failure);
|
|
167
|
+
}
|
|
168
|
+
function clearAuthFailure(agent, persist = true) {
|
|
169
|
+
agent.authFailureActive = false;
|
|
170
|
+
agent.authFailureKind = null;
|
|
171
|
+
agent.authFailureProvider = null;
|
|
172
|
+
if (persist)
|
|
173
|
+
persistAuthFailure(agent);
|
|
174
|
+
}
|
|
175
|
+
function restoreAuthFailure(agent) {
|
|
176
|
+
const persisted = loadPersistedAuthFailure(agent.stateStore);
|
|
177
|
+
if (!persisted)
|
|
178
|
+
return;
|
|
179
|
+
// 只恢复仍绑定当前 candidate 的当前作用域 auth;不从历史 delivery 回灌。
|
|
180
|
+
if (!authFailureAppliesTo(authFailureScopeOf(agent), persisted)) {
|
|
181
|
+
clearAuthFailure(agent);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
applyAuthFailure(agent, persisted);
|
|
185
|
+
}
|
|
186
|
+
function clearLedgerAuthProviders(agent) {
|
|
187
|
+
let changed = false;
|
|
188
|
+
for (const record of agent.records.values()) {
|
|
189
|
+
if (!record.authProvider)
|
|
190
|
+
continue;
|
|
191
|
+
delete record.authProvider;
|
|
192
|
+
changed = true;
|
|
193
|
+
}
|
|
194
|
+
if (changed)
|
|
195
|
+
persist(agent);
|
|
196
|
+
}
|
|
96
197
|
const REPLAY_FAILURE_CODES = new Set([
|
|
97
198
|
"canonical_inbox_row_missing", "canonical_inbox_malformed", "duplicate_message_id",
|
|
98
199
|
"inbox_state_conflict", "delivery_target_conflict", "structured_target_invalid",
|
|
@@ -219,7 +320,6 @@ export function createRuntimeHost(options) {
|
|
|
219
320
|
} : {}),
|
|
220
321
|
LARKIN_CONFIG_DIR: process.env.LARKIN_CONFIG_DIR,
|
|
221
322
|
LARKIN_HOME: process.env.LARKIN_HOME,
|
|
222
|
-
...(config.piDistribution ? { LARKIN_PI_DISTRIBUTION: config.piDistribution } : {}),
|
|
223
323
|
LARKIN_STATE_DIR: effectivePiStateDir(config),
|
|
224
324
|
...(process.env.HOME ? { HOME: process.env.HOME } : {}),
|
|
225
325
|
...(process.env.SHELL ? { SHELL: process.env.SHELL } : {}),
|
|
@@ -481,6 +581,25 @@ export function createRuntimeHost(options) {
|
|
|
481
581
|
messageId: record.messageId, status: "consumed" });
|
|
482
582
|
}
|
|
483
583
|
};
|
|
584
|
+
const projectAuthFailure = (agent, readiness, failure) => {
|
|
585
|
+
agent.authFailureActive = true;
|
|
586
|
+
agent.authFailureKind = failure.kind;
|
|
587
|
+
agent.authFailureProvider = failure.provider;
|
|
588
|
+
agent.readiness = {
|
|
589
|
+
...readiness,
|
|
590
|
+
...(agent.readiness?.executable ? { executable: agent.readiness.executable } : {}),
|
|
591
|
+
...(agent.readiness?.version ? { version: agent.readiness.version } : {}),
|
|
592
|
+
};
|
|
593
|
+
persistAuthFailure(agent);
|
|
594
|
+
emit({ type: "agent-status", agentId: agent.config.agentId, status: "error",
|
|
595
|
+
error: `${agent.readiness.reason} ${agent.readiness.nextAction}`, readiness: agent.readiness });
|
|
596
|
+
};
|
|
597
|
+
const namedAuthProviderPresent = (agent) => {
|
|
598
|
+
if (agent.authFailureKind !== "missing-provider" || !agent.authFailureProvider)
|
|
599
|
+
return true;
|
|
600
|
+
const failure = currentAuthFailure(agent);
|
|
601
|
+
return Boolean(failure && authFailureAppliesTo(authFailureScopeOf(agent), failure));
|
|
602
|
+
};
|
|
484
603
|
const resultFor = (agent, record, result) => {
|
|
485
604
|
if (result.status === "accepted") {
|
|
486
605
|
if (record.errorCategory !== "context_window") {
|
|
@@ -494,6 +613,28 @@ export function createRuntimeHost(options) {
|
|
|
494
613
|
deliveryId: record.deliveryId, messageId: record.messageId, status: "accepted" });
|
|
495
614
|
return { status: "accepted", deliveryId: record.deliveryId };
|
|
496
615
|
}
|
|
616
|
+
const missing = result.status === "rejected" && agent.adapter.id === "pi"
|
|
617
|
+
? classifyPiMissingCredentialRejection(result.reason)
|
|
618
|
+
: null;
|
|
619
|
+
if (missing) {
|
|
620
|
+
const readiness = missingProviderCredentialReadiness(agent.adapter.id, missing.provider);
|
|
621
|
+
const reason = readiness.reason ?? result.reason;
|
|
622
|
+
if (record.status !== "error") {
|
|
623
|
+
record.reason = reason;
|
|
624
|
+
record.retryable = false;
|
|
625
|
+
record.errorCategory = "auth";
|
|
626
|
+
record.authProvider = missing.provider;
|
|
627
|
+
const finalRecord = setRecord(agent, record, "error");
|
|
628
|
+
if (finalRecord.status !== "consumed")
|
|
629
|
+
emit({ type: "delivery", agentId: agent.config.agentId,
|
|
630
|
+
deliveryId: record.deliveryId, messageId: record.messageId, status: "error", reason });
|
|
631
|
+
projectAuthFailure(agent, readiness, { kind: "missing-provider", provider: missing.provider });
|
|
632
|
+
}
|
|
633
|
+
return { status: "error", deliveryId: record.deliveryId, reason: record.reason ?? reason, retryable: false };
|
|
634
|
+
}
|
|
635
|
+
if (record.status === "error") {
|
|
636
|
+
return { status: "error", deliveryId: record.deliveryId, reason: record.reason ?? result.reason, retryable: false };
|
|
637
|
+
}
|
|
497
638
|
if (record.errorCategory !== "context_window")
|
|
498
639
|
delete record.errorCategory;
|
|
499
640
|
const retryable = result.status === "deferred" || result.retryable;
|
|
@@ -1338,7 +1479,7 @@ export function createRuntimeHost(options) {
|
|
|
1338
1479
|
agent.compactionMachines.delete(inputId);
|
|
1339
1480
|
}
|
|
1340
1481
|
const recoveredAuthentication = agent.authFailureActive && agent.turnInProgress
|
|
1341
|
-
&& agent.turnHadAuthenticatedOutput && !agent.turnHadFailure;
|
|
1482
|
+
&& agent.turnHadAuthenticatedOutput && !agent.turnHadFailure && namedAuthProviderPresent(agent);
|
|
1342
1483
|
agent.turnInProgress = false;
|
|
1343
1484
|
agent.busy = false;
|
|
1344
1485
|
emit({ type: "activity", agentId: agent.config.agentId, activity: "idle", activityKind: "idle", detailKind: "turn_ended" });
|
|
@@ -1360,7 +1501,8 @@ export function createRuntimeHost(options) {
|
|
|
1360
1501
|
reconcileSubagentLedger(agent);
|
|
1361
1502
|
telemetry?.runtimeEvent(agent.config.agentId, event);
|
|
1362
1503
|
if (recoveredAuthentication) {
|
|
1363
|
-
agent
|
|
1504
|
+
clearAuthFailure(agent);
|
|
1505
|
+
clearLedgerAuthProviders(agent);
|
|
1364
1506
|
const prior = agent.readiness;
|
|
1365
1507
|
agent.readiness = {
|
|
1366
1508
|
runtime: agent.adapter.id,
|
|
@@ -1453,23 +1595,30 @@ export function createRuntimeHost(options) {
|
|
|
1453
1595
|
record.errorCategory = classifiedCategory;
|
|
1454
1596
|
else
|
|
1455
1597
|
delete record.errorCategory;
|
|
1456
|
-
const
|
|
1598
|
+
const missing = agent.adapter.id === "pi"
|
|
1599
|
+
? classifyPiMissingCredentialRejection(event.upstream?.message)
|
|
1600
|
+
?? classifyPiMissingCredentialRejection(event.message)
|
|
1601
|
+
: null;
|
|
1602
|
+
if (missing) {
|
|
1603
|
+
record.retryable = false;
|
|
1604
|
+
record.errorCategory = "auth";
|
|
1605
|
+
record.authProvider = missing.provider;
|
|
1606
|
+
}
|
|
1607
|
+
const terminal = Boolean(missing) || !event.retryable;
|
|
1608
|
+
const finalRecord = setRecord(agent, record, terminal ? "error" : "pending");
|
|
1457
1609
|
if (finalRecord.status !== "consumed")
|
|
1458
1610
|
emit({ type: "delivery", agentId: agent.config.agentId,
|
|
1459
1611
|
deliveryId: record.deliveryId, messageId: record.messageId,
|
|
1460
|
-
status:
|
|
1461
|
-
if (
|
|
1612
|
+
status: terminal ? "error" : "deferred", reason: event.message });
|
|
1613
|
+
if (missing) {
|
|
1614
|
+
const readiness = missingProviderCredentialReadiness(agent.adapter.id, missing.provider);
|
|
1615
|
+
projectAuthFailure(agent, readiness, { kind: "missing-provider", provider: missing.provider });
|
|
1616
|
+
}
|
|
1617
|
+
else if (event.errorCategory === "auth" && !event.retryable) {
|
|
1462
1618
|
const readiness = providerAuthenticationFailureReadiness(agent.adapter.id, event.upstream?.provider);
|
|
1463
|
-
agent.
|
|
1464
|
-
agent.readiness = {
|
|
1465
|
-
...readiness,
|
|
1466
|
-
...(agent.readiness?.executable ? { executable: agent.readiness.executable } : {}),
|
|
1467
|
-
...(agent.readiness?.version ? { version: agent.readiness.version } : {}),
|
|
1468
|
-
};
|
|
1469
|
-
emit({ type: "agent-status", agentId: agent.config.agentId, status: "error",
|
|
1470
|
-
error: `${agent.readiness.reason} ${agent.readiness.nextAction}`, readiness: agent.readiness });
|
|
1619
|
+
projectAuthFailure(agent, readiness, { kind: "generic", provider: genericAuthProvider(agent, event.upstream) });
|
|
1471
1620
|
}
|
|
1472
|
-
if (event.retryable && !agent.busy && !agent.turnInProgress)
|
|
1621
|
+
if (!terminal && event.retryable && !agent.busy && !agent.turnInProgress)
|
|
1473
1622
|
void retryPending(agent);
|
|
1474
1623
|
}
|
|
1475
1624
|
else if (event.type === "configuration-error") {
|
|
@@ -1739,14 +1888,26 @@ export function createRuntimeHost(options) {
|
|
|
1739
1888
|
clearTimeout(previous.backgroundCompletionRetryTimer);
|
|
1740
1889
|
if (previous.subagentReconcileTimer)
|
|
1741
1890
|
clearInterval(previous.subagentReconcileTimer);
|
|
1891
|
+
const previousFailure = currentAuthFailure(previous);
|
|
1892
|
+
const scoped = Boolean(previousFailure
|
|
1893
|
+
&& authFailureAppliesTo({
|
|
1894
|
+
runtime: config.runtime,
|
|
1895
|
+
model: config.model,
|
|
1896
|
+
adapterId: adapter.id,
|
|
1897
|
+
}, previousFailure));
|
|
1742
1898
|
const candidate = {
|
|
1743
1899
|
...previous, config, adapter, session, launchId: crypto.randomUUID(), busy: false, submitting: false,
|
|
1744
1900
|
starting: null, retryAfterSubmit: false, generation: 0, poller: null, retryTimer: null,
|
|
1745
1901
|
recreateAttempts: 0, stabilityTimer: null, recreateReason: null, stopped: false,
|
|
1746
1902
|
backgroundCompletionRetryTimer: null, subagentReconcileTimer: null,
|
|
1747
1903
|
disabledReason: null, configurationRecovery: null,
|
|
1748
|
-
|
|
1904
|
+
authFailureActive: scoped && previous.authFailureActive,
|
|
1905
|
+
authFailureKind: scoped ? previous.authFailureKind : null,
|
|
1906
|
+
authFailureProvider: scoped ? previous.authFailureProvider : null,
|
|
1907
|
+
readiness: scoped && previous.authFailureActive ? previous.readiness : readiness,
|
|
1749
1908
|
};
|
|
1909
|
+
if (previous.authFailureActive && !candidate.authFailureActive)
|
|
1910
|
+
clearAuthFailure(candidate);
|
|
1750
1911
|
managed.set(config.agentId, candidate);
|
|
1751
1912
|
session.subscribe((event) => observe(candidate, session, event));
|
|
1752
1913
|
if (session.sessionId) {
|
|
@@ -1841,7 +2002,8 @@ export function createRuntimeHost(options) {
|
|
|
1841
2002
|
agent.config.sessionId = null;
|
|
1842
2003
|
agent.session = fresh;
|
|
1843
2004
|
persistPreviousSession(agent, previousRef);
|
|
1844
|
-
agent.
|
|
2005
|
+
if (!agent.authFailureActive)
|
|
2006
|
+
agent.readiness = readiness;
|
|
1845
2007
|
agent.disabledReason = null;
|
|
1846
2008
|
};
|
|
1847
2009
|
try {
|
|
@@ -1862,7 +2024,13 @@ export function createRuntimeHost(options) {
|
|
|
1862
2024
|
...(fresh.effectiveReasoningEffort ? { reasoningEffort: fresh.effectiveReasoningEffort } : {}),
|
|
1863
2025
|
...(agent.previousSession ? { previousSession: agent.previousSession } : {}) });
|
|
1864
2026
|
}
|
|
1865
|
-
emit({
|
|
2027
|
+
emit({
|
|
2028
|
+
type: "agent-status",
|
|
2029
|
+
agentId,
|
|
2030
|
+
status: agent.authFailureActive ? "error" : "active",
|
|
2031
|
+
...(agent.authFailureActive ? { error: `${agent.readiness?.reason} ${agent.readiness?.nextAction}` } : {}),
|
|
2032
|
+
readiness: agent.authFailureActive ? agent.readiness ?? readiness : readiness,
|
|
2033
|
+
});
|
|
1866
2034
|
agent.piSessionOwner = probeEnv[PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
1867
2035
|
await oldSession.close("fresh session reset committed")
|
|
1868
2036
|
.catch((error) => log("previous runtime close after fresh reset failed", String(error)));
|
|
@@ -2000,7 +2168,8 @@ export function createRuntimeHost(options) {
|
|
|
2000
2168
|
agent.config.sessionId = null;
|
|
2001
2169
|
agent.session = fresh;
|
|
2002
2170
|
persistPreviousSession(agent, previousRef);
|
|
2003
|
-
agent.
|
|
2171
|
+
if (!agent.authFailureActive)
|
|
2172
|
+
agent.readiness = readiness;
|
|
2004
2173
|
agent.disabledReason = null;
|
|
2005
2174
|
for (const record of agent.records.values()) {
|
|
2006
2175
|
if (!messageIds.includes(record.messageId))
|
|
@@ -2018,7 +2187,13 @@ export function createRuntimeHost(options) {
|
|
|
2018
2187
|
...(fresh.effectiveReasoningEffort ? { reasoningEffort: fresh.effectiveReasoningEffort } : {}),
|
|
2019
2188
|
...(agent.previousSession ? { previousSession: agent.previousSession } : {}) });
|
|
2020
2189
|
}
|
|
2021
|
-
emit({
|
|
2190
|
+
emit({
|
|
2191
|
+
type: "agent-status",
|
|
2192
|
+
agentId,
|
|
2193
|
+
status: agent.authFailureActive ? "error" : "active",
|
|
2194
|
+
...(agent.authFailureActive ? { error: `${agent.readiness?.reason} ${agent.readiness?.nextAction}` } : {}),
|
|
2195
|
+
readiness: agent.authFailureActive ? agent.readiness ?? readiness : readiness,
|
|
2196
|
+
});
|
|
2022
2197
|
durableRollback = null;
|
|
2023
2198
|
agent.piSessionOwner = probeEnv[PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
2024
2199
|
await oldSession.close("context-window recovery committed")
|
|
@@ -2108,11 +2283,13 @@ export function createRuntimeHost(options) {
|
|
|
2108
2283
|
piProactiveCompactionFailedGeneration: null,
|
|
2109
2284
|
previousSession: loadPersistedPreviousSession(config, stateStore), readiness: null,
|
|
2110
2285
|
turnInProgress: false, turnHadFailure: false, turnHadAuthenticatedOutput: false, authFailureActive: false,
|
|
2286
|
+
authFailureKind: null, authFailureProvider: null,
|
|
2111
2287
|
promotedInboxUpdateIds: new Set(),
|
|
2112
2288
|
backgroundCompletionQueue: [], backgroundCompletionKeys: new Set(),
|
|
2113
2289
|
backgroundCompletionInFlight: null, backgroundCompletionWakeInputId: null,
|
|
2114
2290
|
backgroundCompletionRejectStreak: 0, backgroundCompletionRetryTimer: null,
|
|
2115
2291
|
subagentLedger: loadSubagentLedger(config), subagentReconcileTimer: null };
|
|
2292
|
+
restoreAuthFailure(agent);
|
|
2116
2293
|
const startupConsumed = [];
|
|
2117
2294
|
const startupQuarantined = [];
|
|
2118
2295
|
for (const record of agent.records.values()) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CURRENT_RUNTIME_MODELS = void 0;
|
|
4
|
-
// Authored catalog keys are adapter ids only.
|
|
4
|
+
// Authored catalog keys are adapter ids only.
|
|
5
5
|
// Pi, Codex and Claude replace these placeholders/fallbacks at runtime with their machine-readable catalogs.
|
|
6
6
|
// Claude's supported stdin control-channel list is the live authority; its entries below
|
|
7
7
|
// remain explicit compatibility candidates for config loading and discovery failures.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Authored catalog keys are adapter ids only.
|
|
1
|
+
// Authored catalog keys are adapter ids only.
|
|
2
2
|
// Pi, Codex and Claude replace these placeholders/fallbacks at runtime with their machine-readable catalogs.
|
|
3
3
|
// Claude's supported stdin control-channel list is the live authority; its entries below
|
|
4
4
|
// remain explicit compatibility candidates for config loading and discovery failures.
|
|
@@ -37,22 +37,97 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.RuntimePrerequisiteError = void 0;
|
|
40
|
+
exports.safeProviderId = safeProviderId;
|
|
41
|
+
exports.configuredProviderId = configuredProviderId;
|
|
42
|
+
exports.parsePersistedAuthFailure = parsePersistedAuthFailure;
|
|
43
|
+
exports.authFailureAppliesTo = authFailureAppliesTo;
|
|
44
|
+
exports.readinessForPersistedAuthFailure = readinessForPersistedAuthFailure;
|
|
45
|
+
exports.classifyPiMissingCredentialRejection = classifyPiMissingCredentialRejection;
|
|
46
|
+
exports.runtimeInstallNextAction = runtimeInstallNextAction;
|
|
47
|
+
exports.runtimeLoginNextAction = runtimeLoginNextAction;
|
|
40
48
|
exports.providerAuthenticationFailureReadiness = providerAuthenticationFailureReadiness;
|
|
49
|
+
exports.missingProviderCredentialReadiness = missingProviderCredentialReadiness;
|
|
41
50
|
exports.resolveRuntimeExecutable = resolveRuntimeExecutable;
|
|
42
51
|
exports.classifyRuntimePrerequisite = classifyRuntimePrerequisite;
|
|
43
52
|
exports.probeNativeRuntimeReadiness = probeNativeRuntimeReadiness;
|
|
44
53
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
45
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
46
54
|
const node_path_1 = __importDefault(require("node:path"));
|
|
47
55
|
const node_child_process_1 = require("node:child_process");
|
|
48
|
-
|
|
49
|
-
const builtin_pi_assets_js_1 = require("./builtin-pi-assets.cjs");
|
|
50
|
-
const pi_provider_config_js_1 = require("./pi-provider-config.cjs");
|
|
51
|
-
const process_boundary_trace_js_1 = require("../platform/process-boundary-trace.cjs");
|
|
52
|
-
function safeProviderLabel(value) {
|
|
56
|
+
function safeProviderId(value) {
|
|
53
57
|
const provider = typeof value === "string" ? value.trim() : "";
|
|
54
58
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(provider) ? provider : null;
|
|
55
59
|
}
|
|
60
|
+
function safeProviderLabel(value) {
|
|
61
|
+
return safeProviderId(value);
|
|
62
|
+
}
|
|
63
|
+
/** Model ids are `provider/model`; only the provider prefix is used for scope. */
|
|
64
|
+
function configuredProviderId(model) {
|
|
65
|
+
const value = typeof model === "string" ? model.trim() : "";
|
|
66
|
+
const slash = value.indexOf("/");
|
|
67
|
+
return slash > 0 ? safeProviderId(value.slice(0, slash)) : null;
|
|
68
|
+
}
|
|
69
|
+
function persistedRuntime(value) {
|
|
70
|
+
return value === "pi" || value === "codex" || value === "claude" ? value : null;
|
|
71
|
+
}
|
|
72
|
+
function parsePersistedAuthFailure(state) {
|
|
73
|
+
if (!state || typeof state !== "object" || Array.isArray(state))
|
|
74
|
+
return null;
|
|
75
|
+
const record = state;
|
|
76
|
+
if (record.authFailure && typeof record.authFailure === "object" && !Array.isArray(record.authFailure)) {
|
|
77
|
+
const failure = record.authFailure;
|
|
78
|
+
const kind = failure.kind === "missing-provider" || failure.kind === "generic" ? failure.kind : null;
|
|
79
|
+
const runtime = persistedRuntime(failure.runtime);
|
|
80
|
+
if (!kind || !runtime)
|
|
81
|
+
return null;
|
|
82
|
+
const provider = safeProviderId(failure.provider);
|
|
83
|
+
if (kind === "missing-provider" && !provider)
|
|
84
|
+
return null;
|
|
85
|
+
return { kind, runtime, provider };
|
|
86
|
+
}
|
|
87
|
+
const legacy = safeProviderId(record.authFailureProvider);
|
|
88
|
+
return legacy ? { kind: "missing-provider", runtime: "pi", provider: legacy } : null;
|
|
89
|
+
}
|
|
90
|
+
function currentAdapterId(current) {
|
|
91
|
+
return current.adapterId ?? current.runtime;
|
|
92
|
+
}
|
|
93
|
+
function authFailureAppliesTo(current, failure) {
|
|
94
|
+
if (failure.runtime !== currentAdapterId(current))
|
|
95
|
+
return false;
|
|
96
|
+
if (!failure.provider)
|
|
97
|
+
return true;
|
|
98
|
+
const currentProvider = configuredProviderId(current.model);
|
|
99
|
+
return currentProvider ? currentProvider === failure.provider : true;
|
|
100
|
+
}
|
|
101
|
+
function readinessForPersistedAuthFailure(failure) {
|
|
102
|
+
if (failure.kind === "missing-provider") {
|
|
103
|
+
return missingProviderCredentialReadiness(failure.runtime, failure.provider);
|
|
104
|
+
}
|
|
105
|
+
return providerAuthenticationFailureReadiness(failure.runtime, failure.provider);
|
|
106
|
+
}
|
|
107
|
+
const MISSING_CREDENTIAL_REJECTION = /^(?:Pi RPC (?:prompt|steer) failed: )?(No API key found for|No login found for) ([A-Za-z0-9][A-Za-z0-9._-]{0,79})$/;
|
|
108
|
+
/** Narrow match for an explicit Pi absent-key / absent-login diagnostic. */
|
|
109
|
+
function classifyPiMissingCredentialRejection(message) {
|
|
110
|
+
const text = typeof message === "string"
|
|
111
|
+
? message.replace(/[\r\n\t]+/g, " ").replace(/\s+/g, " ").trim()
|
|
112
|
+
: "";
|
|
113
|
+
const match = MISSING_CREDENTIAL_REJECTION.exec(text);
|
|
114
|
+
const provider = match ? safeProviderLabel(match[2]) : null;
|
|
115
|
+
return provider && match ? { provider, diagnostic: `${match[1]} ${provider}` } : null;
|
|
116
|
+
}
|
|
117
|
+
function runtimeInstallNextAction(runtime) {
|
|
118
|
+
if (runtime === "pi")
|
|
119
|
+
return "Install Pi and ensure `pi` is on PATH, or set LARKIN_PI_COMMAND.";
|
|
120
|
+
if (runtime === "codex")
|
|
121
|
+
return "Install Codex and ensure `codex` is on PATH, or set LARKIN_CODEX_COMMAND.";
|
|
122
|
+
return "Install Claude Code and ensure `claude` is on PATH, or set LARKIN_CLAUDE_COMMAND.";
|
|
123
|
+
}
|
|
124
|
+
function runtimeLoginNextAction(runtime) {
|
|
125
|
+
if (runtime === "pi")
|
|
126
|
+
return "Log in with the external `pi` CLI, then retry.";
|
|
127
|
+
if (runtime === "codex")
|
|
128
|
+
return "Run `codex login`, then retry.";
|
|
129
|
+
return "Run `claude login`, then retry.";
|
|
130
|
+
}
|
|
56
131
|
function providerAuthenticationFailureReadiness(runtime, provider) {
|
|
57
132
|
const label = safeProviderLabel(provider);
|
|
58
133
|
return {
|
|
@@ -61,7 +136,20 @@ function providerAuthenticationFailureReadiness(runtime, provider) {
|
|
|
61
136
|
reason: label
|
|
62
137
|
? `Provider ${label} API-key authentication failed during a Runtime turn.`
|
|
63
138
|
: "Configured provider API-key authentication failed during a Runtime turn.",
|
|
64
|
-
nextAction:
|
|
139
|
+
nextAction: runtimeLoginNextAction(runtime),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function missingProviderCredentialReadiness(runtime, provider) {
|
|
143
|
+
const label = safeProviderLabel(provider);
|
|
144
|
+
return {
|
|
145
|
+
runtime,
|
|
146
|
+
state: "unauthenticated",
|
|
147
|
+
reason: label
|
|
148
|
+
? `Provider ${label} is not authenticated for this runtime.`
|
|
149
|
+
: "The configured provider is not authenticated for this runtime.",
|
|
150
|
+
nextAction: runtime === "pi" && label
|
|
151
|
+
? `Log in with the external \`pi\` CLI for provider ${label}, then retry.`
|
|
152
|
+
: runtimeLoginNextAction(runtime),
|
|
65
153
|
};
|
|
66
154
|
}
|
|
67
155
|
class RuntimePrerequisiteError extends Error {
|
|
@@ -94,24 +182,29 @@ function classifyRuntimePrerequisite(runtime, error, executable) {
|
|
|
94
182
|
const reason = (error instanceof Error ? error.message : String(error)).replace(/[\r\n]+/g, " ").slice(0, 500);
|
|
95
183
|
if (/ENOENT|not found|no such file|spawn .* failed/i.test(reason))
|
|
96
184
|
return {
|
|
97
|
-
runtime, state: "missing", ...(executable ? { executable } : {}),
|
|
98
|
-
|
|
185
|
+
runtime, state: "missing", ...(executable ? { executable } : {}),
|
|
186
|
+
reason: `${runtime} is not installed`,
|
|
187
|
+
nextAction: runtimeInstallNextAction(runtime),
|
|
99
188
|
};
|
|
100
189
|
if (/no authenticated available models|login|credential|unauthenticated|unauthorized|oauth/i.test(reason))
|
|
101
190
|
return {
|
|
102
191
|
runtime, state: "unauthenticated", ...(executable ? { executable } : {}), reason,
|
|
103
|
-
nextAction: runtime
|
|
192
|
+
nextAction: runtimeLoginNextAction(runtime),
|
|
104
193
|
};
|
|
105
|
-
if (/protocol (?:version )?(?:mismatch|unsupported|incompatible)|unsupported (?:rpc|protocol|schema)|schema (?:mismatch|incompatible)|requires (?:a )?newer version/i.test(reason))
|
|
194
|
+
if (/protocol (?:version )?(?:mismatch|unsupported|incompatible)|unsupported (?:rpc|protocol|schema)|schema (?:mismatch|incompatible)|requires (?:a )?newer version/i.test(reason)) {
|
|
106
195
|
return {
|
|
107
196
|
runtime, state: "incompatible", ...(executable ? { executable } : {}), reason,
|
|
108
|
-
nextAction: runtime === "pi"
|
|
197
|
+
nextAction: runtime === "pi"
|
|
198
|
+
? "Upgrade local Pi to a version that supports the documented RPC protocol."
|
|
199
|
+
: `Upgrade ${runtime}, then retry.`,
|
|
109
200
|
};
|
|
110
|
-
|
|
201
|
+
}
|
|
202
|
+
if (/timeout|timed out|unexpected EOF|\bEOF\b|TLS|ECONNRESET|socket hang up|network|temporar(?:y|ily)|unavailable/i.test(reason)) {
|
|
111
203
|
return {
|
|
112
204
|
runtime, state: "unavailable", ...(executable ? { executable } : {}), reason,
|
|
113
205
|
nextAction: `Retry ${runtime}; Larkin will use its bounded Runtime recreate/backoff policy.`,
|
|
114
206
|
};
|
|
207
|
+
}
|
|
115
208
|
return {
|
|
116
209
|
runtime, state: "unavailable", ...(executable ? { executable } : {}), reason,
|
|
117
210
|
nextAction: `Retry ${runtime}; the failure is not proven to be a protocol incompatibility.`,
|
|
@@ -134,44 +227,15 @@ function executableVersion(executable, env, commandArgs = []) {
|
|
|
134
227
|
}
|
|
135
228
|
/** Resolve and handshake through each runtime's structured native control protocol. */
|
|
136
229
|
async function probeNativeRuntimeReadiness(options) {
|
|
137
|
-
|
|
138
|
-
const piDistribution = options.runtime === "pi" ? (0, builtin_pi_assets_js_1.piChildDistributionFromOverrides)(options.env) : "external";
|
|
139
|
-
if (options.runtime === "pi" && piDistribution !== "builtin") {
|
|
140
|
-
if (env.LARKIN_PI_DISTRIBUTION === "builtin")
|
|
141
|
-
delete env.LARKIN_PI_DISTRIBUTION;
|
|
142
|
-
env = (0, builtin_pi_assets_js_1.applyPiPackageDirForChild)(env, { distribution: "external" });
|
|
143
|
-
}
|
|
144
|
-
if (options.runtime === "pi" && piDistribution === "builtin") {
|
|
145
|
-
try {
|
|
146
|
-
if (!options.agentId || !env.LARKIN_CONFIG_DIR)
|
|
147
|
-
throw new Error("内置 Pi readiness 缺少 Agent/config identity");
|
|
148
|
-
(0, pi_provider_config_js_1.assertBuiltinPiAgentDirectory)((0, pi_provider_config_js_1.piAgentDirectory)(env.LARKIN_CONFIG_DIR, options.agentId));
|
|
149
|
-
return { runtime: "pi", state: "ready", executable: process.execPath, version: `official-pi ${pi_provider_config_js_1.BUNDLED_PI_VERSION} (bundled)` };
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
(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 });
|
|
153
|
-
return { runtime: "pi", state: "unauthenticated", reason: error instanceof Error ? error.message : String(error),
|
|
154
|
-
nextAction: "重新运行 larkin setup,选择内置 Pi 并配置有效 API Key。" };
|
|
155
|
-
}
|
|
156
|
-
}
|
|
230
|
+
const env = { ...process.env, ...options.env };
|
|
157
231
|
const command = selectedCommand(options);
|
|
158
232
|
const executable = resolveRuntimeExecutable(command, env);
|
|
159
|
-
if (!executable)
|
|
160
|
-
return {
|
|
161
|
-
runtime: options.runtime, state: "missing",
|
|
162
|
-
reason: `${command} executable was not found`,
|
|
163
|
-
nextAction: options.runtime === "pi"
|
|
164
|
-
? "Install Pi and ensure `pi` is on PATH, or set LARKIN_PI_COMMAND."
|
|
165
|
-
: `Install ${options.runtime} and ensure it is on PATH.`,
|
|
166
|
-
};
|
|
167
|
-
const piAgentId = options.agentId;
|
|
168
|
-
if (options.runtime === "pi" && !piAgentId) {
|
|
233
|
+
if (!executable) {
|
|
169
234
|
return {
|
|
170
|
-
runtime:
|
|
171
|
-
state: "
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
nextAction: "Retry setup or start so Larkin can probe the Agent-owned Pi provider directory.",
|
|
235
|
+
runtime: options.runtime,
|
|
236
|
+
state: "missing",
|
|
237
|
+
reason: `${options.runtime} is not installed`,
|
|
238
|
+
nextAction: runtimeInstallNextAction(options.runtime),
|
|
175
239
|
};
|
|
176
240
|
}
|
|
177
241
|
const version = executableVersion(executable, env, options.commandArgs);
|
|
@@ -183,7 +247,6 @@ async function probeNativeRuntimeReadiness(options) {
|
|
|
183
247
|
command: executable,
|
|
184
248
|
commandArgs: options.commandArgs,
|
|
185
249
|
env,
|
|
186
|
-
agentDir: (0, pi_provider_config_js_1.ownedPiCatalogAgentDir)((0, root_layout_js_1.resolveConfigDir)(env, node_os_1.default.homedir()), piAgentId),
|
|
187
250
|
});
|
|
188
251
|
}
|
|
189
252
|
else if (options.runtime === "codex") {
|