larkin 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/feishu/host-shell.cjs +47 -14
- package/dist/feishu/host-shell.mjs +47 -14
- package/dist/runtime/provider-error-classifier.cjs +14 -0
- package/dist/runtime/provider-error-classifier.mjs +13 -0
- package/dist/runtime/runtime-adapters.mjs +40 -13
- package/dist/runtime/runtime-host.cjs +46 -26
- package/dist/runtime/runtime-host.mjs +46 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.3
|
|
4
|
+
|
|
5
|
+
External Pi initialization now runs with bounded concurrency and recovers from isolated startup-probe timeouts through the existing retry policy. Shutdown waits for pending initialization and closes late sessions. Generic Runtime and delivery failures show unavailable instead of incompatible, while explicit prerequisite failures and useful provider diagnostics are preserved. Recovery workflow tests wait for delivery consumption and Inbox watermarks to converge and isolate temporary state.
|
|
6
|
+
|
|
3
7
|
## 0.5.2
|
|
4
8
|
|
|
5
9
|
External pi now runs with the user's own Pi home; compaction settings move to the Agent workspace's `.pi/settings.json`; 0.5.0/0.5.1 started external pi with an empty agent dir and saw no logins. Stock pi 0.84.x does not emit `get_state.compactionCapabilities` (that handshake only existed for the bundled build); Larkin accepts the absence and keeps native compaction via the workspace settings file, while a present handshake must still match exactly.
|
|
@@ -57,6 +57,7 @@ const host_channel_business_js_1 = require("./host-channel-business.cjs");
|
|
|
57
57
|
const interaction_orchestrator_js_1 = require("./interaction-orchestrator.cjs");
|
|
58
58
|
const message_policy_js_1 = require("./message-policy.cjs");
|
|
59
59
|
const runtime_readiness_js_1 = require("../runtime/runtime-readiness.cjs");
|
|
60
|
+
const provider_error_classifier_js_1 = require("../runtime/provider-error-classifier.cjs");
|
|
60
61
|
const callback_capability_js_1 = require("../platform/callback-capability.cjs");
|
|
61
62
|
const config_js_1 = require("../platform/config.cjs");
|
|
62
63
|
const internal_command_js_1 = require("../app/internal-command.cjs");
|
|
@@ -119,6 +120,9 @@ function memberNamesFromPayloads(payloads) {
|
|
|
119
120
|
return names;
|
|
120
121
|
}
|
|
121
122
|
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
123
|
+
function safeRuntimeStatusDiagnostic(error) {
|
|
124
|
+
return (0, provider_error_classifier_js_1.safeProviderDiagnostic)(error, "Runtime entered an error state", 500);
|
|
125
|
+
}
|
|
122
126
|
function agentConfigSignature(agent) {
|
|
123
127
|
return JSON.stringify({
|
|
124
128
|
agentId: agent.agentId, runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
|
|
@@ -233,17 +237,40 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
233
237
|
}
|
|
234
238
|
hostState.updateStatus(agent, { runtimeReadiness: { runtime: agent.runtime, state: "ready", observedAt } });
|
|
235
239
|
};
|
|
240
|
+
const projectFallbackRuntimeReadiness = (agent, observedAt, fallback, preserveCurrentUnavailableDiagnostic = false) => {
|
|
241
|
+
const failure = unresolvedCurrentAuth(agent);
|
|
242
|
+
if (failure) {
|
|
243
|
+
hostState.updateStatus(agent, {
|
|
244
|
+
runtimeReadiness: { ...(0, runtime_readiness_js_1.readinessForPersistedAuthFailure)(failure), observedAt },
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
249
|
+
const observedMs = Date.parse(String(currentReadiness?.observedAt || ""));
|
|
250
|
+
const daemonStartedMs = Date.parse(daemonStartedAt);
|
|
251
|
+
const currentEpoch = currentReadiness?.runtime === agent.runtime
|
|
252
|
+
&& Number.isFinite(observedMs) && Number.isFinite(daemonStartedMs) && observedMs >= daemonStartedMs;
|
|
253
|
+
if (currentEpoch && (currentReadiness?.state === "missing" || currentReadiness?.state === "unauthenticated" || currentReadiness?.state === "incompatible"))
|
|
254
|
+
return;
|
|
255
|
+
if (currentEpoch && preserveCurrentUnavailableDiagnostic && currentReadiness?.state === "unavailable"
|
|
256
|
+
&& typeof currentReadiness.reason === "string" && currentReadiness.reason.trim()) {
|
|
257
|
+
hostState.updateStatus(agent, {
|
|
258
|
+
runtimeReadiness: { ...currentReadiness, runtime: agent.runtime, state: "unavailable", observedAt },
|
|
259
|
+
});
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
hostState.updateStatus(agent, {
|
|
263
|
+
runtimeReadiness: { runtime: agent.runtime, ...fallback, observedAt },
|
|
264
|
+
});
|
|
265
|
+
};
|
|
236
266
|
const recordInboundDeliveryFailure = (agent, code) => {
|
|
237
267
|
const at = new Date().toISOString();
|
|
238
268
|
const reason = "Inbound Runtime delivery failed non-retryably; durable Inbox/ledger state is retained for recovery.";
|
|
239
269
|
const nextAction = "Inspect the delivery/status error, correct the Runtime or canonical Inbox state, then restart to replay safely.";
|
|
240
|
-
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
241
270
|
hostState.updateStatus(agent, {
|
|
242
271
|
inboundDeliveryHealth: { state: "error", code, at, reason, nextAction },
|
|
243
|
-
...(currentReadiness?.state === "unauthenticated" ? {} : {
|
|
244
|
-
runtimeReadiness: { runtime: agent.runtime, state: "incompatible", reason, nextAction },
|
|
245
|
-
}),
|
|
246
272
|
});
|
|
273
|
+
projectFallbackRuntimeReadiness(agent, at, { state: "unavailable", reason, nextAction }, true);
|
|
247
274
|
hostState.recordStatusError(agent, `${reason} ${nextAction} code=${code}`);
|
|
248
275
|
};
|
|
249
276
|
const agentStates = new Map();
|
|
@@ -1312,15 +1339,21 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
1312
1339
|
if (message.type === "agent-status") {
|
|
1313
1340
|
log("agent:status", message.agentId, message.status);
|
|
1314
1341
|
const observedAt = new Date().toISOString();
|
|
1315
|
-
if (message.status === "error" || message.status === "inactive")
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1342
|
+
if (message.status === "error" || message.status === "inactive") {
|
|
1343
|
+
if (message.readiness?.state && message.readiness.state !== "ready") {
|
|
1344
|
+
hostState.updateStatus(agent, { runtimeReadiness: { ...message.readiness, observedAt } });
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
projectFallbackRuntimeReadiness(agent, observedAt, message.status === "error" ? {
|
|
1348
|
+
state: "unavailable",
|
|
1349
|
+
reason: safeRuntimeStatusDiagnostic(message.error),
|
|
1350
|
+
nextAction: "Inspect the Runtime status error, correct the Runtime availability issue, then retry.",
|
|
1351
|
+
} : {
|
|
1352
|
+
state: "missing",
|
|
1353
|
+
reason: "Runtime is inactive",
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1324
1357
|
else if (message.readiness) {
|
|
1325
1358
|
if (message.readiness.state === "ready" && unresolvedCurrentAuth(agent)) {
|
|
1326
1359
|
projectReadyUnlessUnresolvedAuth(agent, observedAt);
|
|
@@ -1338,7 +1371,7 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
1338
1371
|
redeliveryTimer.unref?.();
|
|
1339
1372
|
}
|
|
1340
1373
|
if (message.status === "error" && message.error)
|
|
1341
|
-
hostState.recordStatusError(agent, message.error);
|
|
1374
|
+
hostState.recordStatusError(agent, safeRuntimeStatusDiagnostic(message.error));
|
|
1342
1375
|
if (message.status === "error" || message.status === "inactive") {
|
|
1343
1376
|
processingEyes.clear(agent, `agent-status:${message.status}`);
|
|
1344
1377
|
}
|
|
@@ -17,6 +17,7 @@ import { HostChannelBusiness } from "./host-channel-business.mjs";
|
|
|
17
17
|
import { HostInteractionOrchestrator } from "./interaction-orchestrator.mjs";
|
|
18
18
|
import { targetFor } from "./message-policy.mjs";
|
|
19
19
|
import { authFailureAppliesTo, parsePersistedAuthFailure, providerAuthenticationFailureReadiness, readinessForPersistedAuthFailure, RuntimePrerequisiteError, } from "../runtime/runtime-readiness.mjs";
|
|
20
|
+
import { safeProviderDiagnostic } from "../runtime/provider-error-classifier.mjs";
|
|
20
21
|
import { readDocumentCommentSubscription, verifyCallbackProbe } from "../platform/callback-capability.mjs";
|
|
21
22
|
import { loadConfig, resolveMentionPolicy } from "../platform/config.mjs";
|
|
22
23
|
import { processCommandToken } from "../app/internal-command.mjs";
|
|
@@ -79,6 +80,9 @@ export function memberNamesFromPayloads(payloads) {
|
|
|
79
80
|
return names;
|
|
80
81
|
}
|
|
81
82
|
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
83
|
+
function safeRuntimeStatusDiagnostic(error) {
|
|
84
|
+
return safeProviderDiagnostic(error, "Runtime entered an error state", 500);
|
|
85
|
+
}
|
|
82
86
|
function agentConfigSignature(agent) {
|
|
83
87
|
return JSON.stringify({
|
|
84
88
|
agentId: agent.agentId, runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
|
|
@@ -193,17 +197,40 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
193
197
|
}
|
|
194
198
|
hostState.updateStatus(agent, { runtimeReadiness: { runtime: agent.runtime, state: "ready", observedAt } });
|
|
195
199
|
};
|
|
200
|
+
const projectFallbackRuntimeReadiness = (agent, observedAt, fallback, preserveCurrentUnavailableDiagnostic = false) => {
|
|
201
|
+
const failure = unresolvedCurrentAuth(agent);
|
|
202
|
+
if (failure) {
|
|
203
|
+
hostState.updateStatus(agent, {
|
|
204
|
+
runtimeReadiness: { ...readinessForPersistedAuthFailure(failure), observedAt },
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
209
|
+
const observedMs = Date.parse(String(currentReadiness?.observedAt || ""));
|
|
210
|
+
const daemonStartedMs = Date.parse(daemonStartedAt);
|
|
211
|
+
const currentEpoch = currentReadiness?.runtime === agent.runtime
|
|
212
|
+
&& Number.isFinite(observedMs) && Number.isFinite(daemonStartedMs) && observedMs >= daemonStartedMs;
|
|
213
|
+
if (currentEpoch && (currentReadiness?.state === "missing" || currentReadiness?.state === "unauthenticated" || currentReadiness?.state === "incompatible"))
|
|
214
|
+
return;
|
|
215
|
+
if (currentEpoch && preserveCurrentUnavailableDiagnostic && currentReadiness?.state === "unavailable"
|
|
216
|
+
&& typeof currentReadiness.reason === "string" && currentReadiness.reason.trim()) {
|
|
217
|
+
hostState.updateStatus(agent, {
|
|
218
|
+
runtimeReadiness: { ...currentReadiness, runtime: agent.runtime, state: "unavailable", observedAt },
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
hostState.updateStatus(agent, {
|
|
223
|
+
runtimeReadiness: { runtime: agent.runtime, ...fallback, observedAt },
|
|
224
|
+
});
|
|
225
|
+
};
|
|
196
226
|
const recordInboundDeliveryFailure = (agent, code) => {
|
|
197
227
|
const at = new Date().toISOString();
|
|
198
228
|
const reason = "Inbound Runtime delivery failed non-retryably; durable Inbox/ledger state is retained for recovery.";
|
|
199
229
|
const nextAction = "Inspect the delivery/status error, correct the Runtime or canonical Inbox state, then restart to replay safely.";
|
|
200
|
-
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
201
230
|
hostState.updateStatus(agent, {
|
|
202
231
|
inboundDeliveryHealth: { state: "error", code, at, reason, nextAction },
|
|
203
|
-
...(currentReadiness?.state === "unauthenticated" ? {} : {
|
|
204
|
-
runtimeReadiness: { runtime: agent.runtime, state: "incompatible", reason, nextAction },
|
|
205
|
-
}),
|
|
206
232
|
});
|
|
233
|
+
projectFallbackRuntimeReadiness(agent, at, { state: "unavailable", reason, nextAction }, true);
|
|
207
234
|
hostState.recordStatusError(agent, `${reason} ${nextAction} code=${code}`);
|
|
208
235
|
};
|
|
209
236
|
const agentStates = new Map();
|
|
@@ -1272,15 +1299,21 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
1272
1299
|
if (message.type === "agent-status") {
|
|
1273
1300
|
log("agent:status", message.agentId, message.status);
|
|
1274
1301
|
const observedAt = new Date().toISOString();
|
|
1275
|
-
if (message.status === "error" || message.status === "inactive")
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1302
|
+
if (message.status === "error" || message.status === "inactive") {
|
|
1303
|
+
if (message.readiness?.state && message.readiness.state !== "ready") {
|
|
1304
|
+
hostState.updateStatus(agent, { runtimeReadiness: { ...message.readiness, observedAt } });
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
projectFallbackRuntimeReadiness(agent, observedAt, message.status === "error" ? {
|
|
1308
|
+
state: "unavailable",
|
|
1309
|
+
reason: safeRuntimeStatusDiagnostic(message.error),
|
|
1310
|
+
nextAction: "Inspect the Runtime status error, correct the Runtime availability issue, then retry.",
|
|
1311
|
+
} : {
|
|
1312
|
+
state: "missing",
|
|
1313
|
+
reason: "Runtime is inactive",
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1284
1317
|
else if (message.readiness) {
|
|
1285
1318
|
if (message.readiness.state === "ready" && unresolvedCurrentAuth(agent)) {
|
|
1286
1319
|
projectReadyUnlessUnresolvedAuth(agent, observedAt);
|
|
@@ -1298,7 +1331,7 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
1298
1331
|
redeliveryTimer.unref?.();
|
|
1299
1332
|
}
|
|
1300
1333
|
if (message.status === "error" && message.error)
|
|
1301
|
-
hostState.recordStatusError(agent, message.error);
|
|
1334
|
+
hostState.recordStatusError(agent, safeRuntimeStatusDiagnostic(message.error));
|
|
1302
1335
|
if (message.status === "error" || message.status === "inactive") {
|
|
1303
1336
|
processingEyes.clear(agent, `agent-status:${message.status}`);
|
|
1304
1337
|
}
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.INTERNAL_CONTEXT_WINDOW_PROJECTION_REASON = exports.CANONICAL_CONTEXT_WINDOW_MESSAGE = exports.CODEX_ERROR_PREFIX = void 0;
|
|
4
|
+
exports.safeProviderDiagnostic = safeProviderDiagnostic;
|
|
4
5
|
exports.buildStrictProviderErrorInput = buildStrictProviderErrorInput;
|
|
5
6
|
exports.classifyStrictProviderError = classifyStrictProviderError;
|
|
6
7
|
exports.CODEX_ERROR_PREFIX = "Codex error: ";
|
|
7
8
|
exports.CANONICAL_CONTEXT_WINDOW_MESSAGE = "Your input exceeds the context window of this model. Please adjust your input and try again.";
|
|
8
9
|
exports.INTERNAL_CONTEXT_WINDOW_PROJECTION_REASON = "provider rejected the input because the context window was exceeded";
|
|
10
|
+
/** Redact credentials and local filesystem locations from provider diagnostics kept in status. */
|
|
11
|
+
function safeProviderDiagnostic(value, fallback, max = 2_000) {
|
|
12
|
+
const source = typeof value === "string" ? value : fallback;
|
|
13
|
+
return source
|
|
14
|
+
.replace(/\b(?:authorization|proxy-authorization)\s*[:=]\s*(?:Bearer\s+)?[^\s,;]+/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
15
|
+
.replace(/\b(?:cookie|set-cookie)\s*[:=]\s*(?:[^;\s,]+(?:\s*;\s*[^;\s,]+)*)/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
16
|
+
.replace(/\b(?:api[_ -]?key|access[_ -]?token|refresh[_ -]?token|token|secret|password)\s*[:=]\s*[^\s,;]+/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
17
|
+
.replace(/(["'](?:authorization|cookie|api[_-]?key|access[_-]?token|refresh[_-]?token|token|secret|password)["']\s*:\s*)["'][^"']*["']/gi, "$1\"[redacted]\"")
|
|
18
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+\/-]+/gi, "Bearer [redacted]")
|
|
19
|
+
.replace(/\b(?:sk|ghp|github_pat)-?[A-Za-z0-9_-]{8,}\b/g, "[redacted]")
|
|
20
|
+
.replace(/(^|[\s"'=])(?:\/(?:Users|home|private|var|tmp|etc)\/[^\s"']+|[A-Za-z]:[\\/][^\s"']+)/g, "$1[redacted-path]")
|
|
21
|
+
.replace(/[\r\n\t]+/g, " ").replace(/\s+/g, " ").trim().slice(0, max) || fallback;
|
|
22
|
+
}
|
|
9
23
|
/** Build the one structured provider-error shape shared by ingestion and recovery. */
|
|
10
24
|
function buildStrictProviderErrorInput(source) {
|
|
11
25
|
const upstream = source.upstream && typeof source.upstream === "object" && !Array.isArray(source.upstream)
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
export const CODEX_ERROR_PREFIX = "Codex error: ";
|
|
2
2
|
export const CANONICAL_CONTEXT_WINDOW_MESSAGE = "Your input exceeds the context window of this model. Please adjust your input and try again.";
|
|
3
3
|
export const INTERNAL_CONTEXT_WINDOW_PROJECTION_REASON = "provider rejected the input because the context window was exceeded";
|
|
4
|
+
/** Redact credentials and local filesystem locations from provider diagnostics kept in status. */
|
|
5
|
+
export function safeProviderDiagnostic(value, fallback, max = 2_000) {
|
|
6
|
+
const source = typeof value === "string" ? value : fallback;
|
|
7
|
+
return source
|
|
8
|
+
.replace(/\b(?:authorization|proxy-authorization)\s*[:=]\s*(?:Bearer\s+)?[^\s,;]+/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
9
|
+
.replace(/\b(?:cookie|set-cookie)\s*[:=]\s*(?:[^;\s,]+(?:\s*;\s*[^;\s,]+)*)/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
10
|
+
.replace(/\b(?:api[_ -]?key|access[_ -]?token|refresh[_ -]?token|token|secret|password)\s*[:=]\s*[^\s,;]+/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
11
|
+
.replace(/(["'](?:authorization|cookie|api[_-]?key|access[_-]?token|refresh[_-]?token|token|secret|password)["']\s*:\s*)["'][^"']*["']/gi, "$1\"[redacted]\"")
|
|
12
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+\/-]+/gi, "Bearer [redacted]")
|
|
13
|
+
.replace(/\b(?:sk|ghp|github_pat)-?[A-Za-z0-9_-]{8,}\b/g, "[redacted]")
|
|
14
|
+
.replace(/(^|[\s"'=])(?:\/(?:Users|home|private|var|tmp|etc)\/[^\s"']+|[A-Za-z]:[\\/][^\s"']+)/g, "$1[redacted-path]")
|
|
15
|
+
.replace(/[\r\n\t]+/g, " ").replace(/\s+/g, " ").trim().slice(0, max) || fallback;
|
|
16
|
+
}
|
|
4
17
|
/** Build the one structured provider-error shape shared by ingestion and recovery. */
|
|
5
18
|
export function buildStrictProviderErrorInput(source) {
|
|
6
19
|
const upstream = source.upstream && typeof source.upstream === "object" && !Array.isArray(source.upstream)
|
|
@@ -12,7 +12,7 @@ import { resolvePiSubagentRecordWatchdogExtensionArg } from "./pi-subagent-recor
|
|
|
12
12
|
import { effectivePiStateDir, extractBackgroundPiSubagentDispatch } from "./pi-subagent-ledger.mjs";
|
|
13
13
|
import { resolvePiBashTimeoutExtensionArg } from "./pi-bash-timeout-injection.mjs";
|
|
14
14
|
import { classifyPiMissingCredentialRejection, classifyRuntimePrerequisite, missingProviderCredentialReadiness, probeNativeRuntimeReadiness, providerAuthenticationFailureReadiness, RuntimePrerequisiteError, } from "./runtime-readiness.mjs";
|
|
15
|
-
import { buildStrictProviderErrorInput, classifyStrictProviderError, INTERNAL_CONTEXT_WINDOW_PROJECTION_REASON, } from "./provider-error-classifier.mjs";
|
|
15
|
+
import { buildStrictProviderErrorInput, classifyStrictProviderError, INTERNAL_CONTEXT_WINDOW_PROJECTION_REASON, safeProviderDiagnostic, } from "./provider-error-classifier.mjs";
|
|
16
16
|
import { assertEffectivePiCompactionSettings, MINIMUM_PI_VERSION, calculatePiCompactionSettings, parsePiExecutableVersion, projectPiSettingsFile, readOwnedPiSettings, verifyPiCapabilities, writeOwnedPiSettings, } from "./pi-compaction-recovery.mjs";
|
|
17
17
|
/** External pi must see the user's own agent dir; Larkin never injects or honors PI_CODING_AGENT_DIR. */
|
|
18
18
|
function stripPiCodingAgentDir(env) {
|
|
@@ -20,6 +20,31 @@ function stripPiCodingAgentDir(env) {
|
|
|
20
20
|
delete next.PI_CODING_AGENT_DIR;
|
|
21
21
|
return next;
|
|
22
22
|
}
|
|
23
|
+
const PI_INITIALIZATION_CONCURRENCY = 2;
|
|
24
|
+
/** Limits short-lived Pi startup handshakes without serializing live sessions. */
|
|
25
|
+
function createInitializationLimiter(limit) {
|
|
26
|
+
let active = 0;
|
|
27
|
+
const waiters = [];
|
|
28
|
+
const release = () => {
|
|
29
|
+
const next = waiters.shift();
|
|
30
|
+
if (next)
|
|
31
|
+
next();
|
|
32
|
+
else
|
|
33
|
+
active -= 1;
|
|
34
|
+
};
|
|
35
|
+
return async (operation) => {
|
|
36
|
+
if (active >= limit)
|
|
37
|
+
await new Promise((resolve) => waiters.push(resolve));
|
|
38
|
+
else
|
|
39
|
+
active += 1;
|
|
40
|
+
try {
|
|
41
|
+
return await operation();
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
release();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
23
48
|
class EventSession {
|
|
24
49
|
listeners = new Set();
|
|
25
50
|
subscribe(listener) {
|
|
@@ -780,14 +805,7 @@ class PiSession extends EventSession {
|
|
|
780
805
|
}
|
|
781
806
|
}
|
|
782
807
|
function safeProviderText(value, fallback) {
|
|
783
|
-
|
|
784
|
-
return source
|
|
785
|
-
.replace(/\b(?:authorization|proxy-authorization)\s*[:=]\s*(?:Bearer\s+)?[^\s,;]+/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
786
|
-
.replace(/\b(?:cookie|set-cookie|api[_ -]?key|access[_ -]?token|refresh[_ -]?token|secret)\s*[:=]\s*[^\s,;]+/gi, (match) => `${match.split(/[:=]/, 1)[0]}=[redacted]`)
|
|
787
|
-
.replace(/(["'](?:authorization|cookie|api[_-]?key|access[_-]?token|refresh[_-]?token|token|secret)["']\s*:\s*)["'][^"']*["']/gi, "$1\"[redacted]\"")
|
|
788
|
-
.replace(/Bearer\s+[A-Za-z0-9._~+\/-]+/gi, "Bearer [redacted]")
|
|
789
|
-
.replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "[redacted]")
|
|
790
|
-
.replace(/[\r\n\t]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 2_000) || fallback;
|
|
808
|
+
return safeProviderDiagnostic(value, fallback);
|
|
791
809
|
}
|
|
792
810
|
function piAssistantProviderError(message) {
|
|
793
811
|
const diagnostic = [...(Array.isArray(message.diagnostics) ? message.diagnostics : [])]
|
|
@@ -1048,8 +1066,16 @@ async function createPiRpcBackend(input, dependencies, spawn, productionSpawn =
|
|
|
1048
1066
|
commandArgs.push(...extensionArgs);
|
|
1049
1067
|
let probedModel = null;
|
|
1050
1068
|
if (productionSpawn) {
|
|
1051
|
-
|
|
1052
|
-
|
|
1069
|
+
try {
|
|
1070
|
+
// Isolated probes must share the same prerequisite contract as the
|
|
1071
|
+
// session handshake, so a cold-start timeout can use bounded recovery.
|
|
1072
|
+
probedModel = await discoverEffectivePiContextWindow(input, command, commandPrefix, requestedModel, childEnv, spawn, dependencies.piRpcClientOptions);
|
|
1073
|
+
}
|
|
1074
|
+
catch (error) {
|
|
1075
|
+
if (error instanceof RuntimePrerequisiteError)
|
|
1076
|
+
throw error;
|
|
1077
|
+
throw new RuntimePrerequisiteError(classifyRuntimePrerequisite("pi", error, command));
|
|
1078
|
+
}
|
|
1053
1079
|
writeOwnedPiSettings(input.workspaceDir, calculatePiCompactionSettings(probedModel.contextWindow));
|
|
1054
1080
|
}
|
|
1055
1081
|
const child = spawn(command, commandArgs, {
|
|
@@ -1119,6 +1145,7 @@ export function createNativeRuntimeAdapter(id, dependencies = {}) {
|
|
|
1119
1145
|
? dependencies.codexCommand ?? dependencies.env?.LARKIN_CODEX_COMMAND ?? "codex"
|
|
1120
1146
|
: dependencies.claudeCommand ?? dependencies.env?.LARKIN_CLAUDE_COMMAND ?? "claude";
|
|
1121
1147
|
const codexCommand = dependencies.codexCommand ?? dependencies.env?.LARKIN_CODEX_COMMAND ?? "codex";
|
|
1148
|
+
const runPiInitialization = createInitializationLimiter(PI_INITIALIZATION_CONCURRENCY);
|
|
1122
1149
|
let codexUpdateAttempt = null;
|
|
1123
1150
|
let codexUpdateAttempted = false;
|
|
1124
1151
|
const runCodexUpdate = () => new Promise((resolve) => {
|
|
@@ -1217,9 +1244,9 @@ export function createNativeRuntimeAdapter(id, dependencies = {}) {
|
|
|
1217
1244
|
throw new RuntimePrerequisiteError(readiness);
|
|
1218
1245
|
}
|
|
1219
1246
|
if (id === "pi") {
|
|
1220
|
-
return new PiSession(await (dependencies.createPiSession
|
|
1247
|
+
return runPiInitialization(async () => new PiSession(await (dependencies.createPiSession
|
|
1221
1248
|
? dependencies.createPiSession(input)
|
|
1222
|
-
: createPiRpcBackend(input, { ...dependencies, piCommand: resolvedExecutable }, spawn, productionSpawn)));
|
|
1249
|
+
: createPiRpcBackend(input, { ...dependencies, piCommand: resolvedExecutable }, spawn, productionSpawn))));
|
|
1223
1250
|
}
|
|
1224
1251
|
if (id === "codex") {
|
|
1225
1252
|
const codexInput = dependencies.codexModelOverride?.trim()
|
|
@@ -1640,38 +1640,45 @@ function createRuntimeHost(options) {
|
|
|
1640
1640
|
const ensureSession = async (agent) => {
|
|
1641
1641
|
if (agent.disabledReason)
|
|
1642
1642
|
throw new Error(agent.disabledReason);
|
|
1643
|
+
if (agent.stopped)
|
|
1644
|
+
throw new Error("runtime Agent is stopped");
|
|
1643
1645
|
if (agent.session)
|
|
1644
1646
|
return agent.session;
|
|
1645
1647
|
if (agent.starting)
|
|
1646
1648
|
return agent.starting;
|
|
1647
|
-
const probeEnv = runtimeEnv(agent.config);
|
|
1648
|
-
await assertOfficialCliReady(agent.config, probeEnv);
|
|
1649
|
-
const readiness = agent.adapter.probe ? await agent.adapter.probe({ agentId: agent.config.agentId,
|
|
1650
|
-
workspaceDir: agent.config.workspaceDir, stateDir: agent.config.stateDir,
|
|
1651
|
-
env: { LARKIN_PI_COMMAND: process.env.LARKIN_PI_COMMAND, LARKIN_CODEX_COMMAND: process.env.LARKIN_CODEX_COMMAND,
|
|
1652
|
-
LARKIN_CLAUDE_COMMAND: process.env.LARKIN_CLAUDE_COMMAND, ...probeEnv } })
|
|
1653
|
-
: { runtime: agent.adapter.id, state: "ready" };
|
|
1654
|
-
if (!agent.authFailureActive)
|
|
1655
|
-
agent.readiness = readiness;
|
|
1656
|
-
if (readiness.state !== "ready")
|
|
1657
|
-
throw new runtime_readiness_js_1.RuntimePrerequisiteError(readiness);
|
|
1658
|
-
const standingPrompt = options.promptBuilder.build({
|
|
1659
|
-
agentId: agent.config.agentId, name: agent.config.displayName || agent.config.name,
|
|
1660
|
-
description: agent.config.description || "", runtime: agent.adapter.id,
|
|
1661
|
-
cli: (0, agent_cli_capabilities_js_1.agentCliPromptCapabilities)("larkin"), previousSession: agent.previousSession,
|
|
1662
|
-
});
|
|
1663
|
-
const generation = ++agent.generation;
|
|
1664
1649
|
let completedSession = null;
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1650
|
+
agent.starting = (async () => {
|
|
1651
|
+
const probeEnv = runtimeEnv(agent.config);
|
|
1652
|
+
await assertOfficialCliReady(agent.config, probeEnv);
|
|
1653
|
+
if (agent.stopped)
|
|
1654
|
+
throw new Error("runtime Agent is stopped");
|
|
1655
|
+
const readiness = agent.adapter.probe ? await agent.adapter.probe({ agentId: agent.config.agentId,
|
|
1656
|
+
workspaceDir: agent.config.workspaceDir, stateDir: agent.config.stateDir,
|
|
1657
|
+
env: { LARKIN_PI_COMMAND: process.env.LARKIN_PI_COMMAND, LARKIN_CODEX_COMMAND: process.env.LARKIN_CODEX_COMMAND,
|
|
1658
|
+
LARKIN_CLAUDE_COMMAND: process.env.LARKIN_CLAUDE_COMMAND, ...probeEnv } })
|
|
1659
|
+
: { runtime: agent.adapter.id, state: "ready" };
|
|
1660
|
+
if (!agent.authFailureActive)
|
|
1661
|
+
agent.readiness = readiness;
|
|
1662
|
+
if (readiness.state !== "ready")
|
|
1663
|
+
throw new runtime_readiness_js_1.RuntimePrerequisiteError(readiness);
|
|
1664
|
+
if (agent.stopped)
|
|
1665
|
+
throw new Error("runtime Agent is stopped");
|
|
1666
|
+
const standingPrompt = options.promptBuilder.build({
|
|
1667
|
+
agentId: agent.config.agentId, name: agent.config.displayName || agent.config.name,
|
|
1668
|
+
description: agent.config.description || "", runtime: agent.adapter.id,
|
|
1669
|
+
cli: (0, agent_cli_capabilities_js_1.agentCliPromptCapabilities)("larkin"), previousSession: agent.previousSession,
|
|
1670
|
+
});
|
|
1671
|
+
const generation = ++agent.generation;
|
|
1672
|
+
const sessionEnv = runtimeEnv(agent.config, `${agent.launchId}:${generation}`);
|
|
1673
|
+
const session = await agent.adapter.createSession({
|
|
1674
|
+
agentId: agent.config.agentId, model: agent.config.model, reasoningEffort: agent.config.effort || null,
|
|
1675
|
+
workspaceDir: agent.config.workspaceDir, stateDir: agent.config.stateDir,
|
|
1676
|
+
resumeSessionId: agent.config.sessionId || null, standingPrompt,
|
|
1677
|
+
env: sessionEnv,
|
|
1678
|
+
});
|
|
1672
1679
|
completedSession = session;
|
|
1673
1680
|
if (agent.stopped || generation !== agent.generation) {
|
|
1674
|
-
|
|
1681
|
+
await session.close("stale creation");
|
|
1675
1682
|
throw new Error("stale runtime session creation");
|
|
1676
1683
|
}
|
|
1677
1684
|
agent.session = session;
|
|
@@ -1691,7 +1698,7 @@ function createRuntimeHost(options) {
|
|
|
1691
1698
|
agent.stabilityTimer = setTimeout(() => markSessionStable(agent, session), retryPolicy.stableWindowMs);
|
|
1692
1699
|
agent.stabilityTimer.unref?.();
|
|
1693
1700
|
return session;
|
|
1694
|
-
}).finally(() => {
|
|
1701
|
+
})().finally(() => {
|
|
1695
1702
|
agent.starting = null;
|
|
1696
1703
|
if (completedSession && !agent.session && !agent.stopped) {
|
|
1697
1704
|
scheduleRecreate(agent, agent.recreateReason || "runtime closed during session initialization");
|
|
@@ -2329,6 +2336,8 @@ function createRuntimeHost(options) {
|
|
|
2329
2336
|
emitConsumed(agent, [...startupConsumed, ...persist(agent)]);
|
|
2330
2337
|
try {
|
|
2331
2338
|
await ensureSession(agent);
|
|
2339
|
+
if (agent.stopped)
|
|
2340
|
+
return;
|
|
2332
2341
|
reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "runtime restarted" });
|
|
2333
2342
|
enqueueUndeliveredTerminalWakes(agent);
|
|
2334
2343
|
await recoverStalePiCompaction(agent);
|
|
@@ -2336,10 +2345,14 @@ function createRuntimeHost(options) {
|
|
|
2336
2345
|
const proactive = startupSession ? proactivelyCompactPiAtIdle(agent, startupSession) : null;
|
|
2337
2346
|
if (proactive)
|
|
2338
2347
|
await proactive;
|
|
2348
|
+
if (agent.stopped)
|
|
2349
|
+
return;
|
|
2339
2350
|
activeCount += 1;
|
|
2340
2351
|
await retryPending(agent);
|
|
2341
2352
|
}
|
|
2342
2353
|
catch (error) {
|
|
2354
|
+
if (agent.stopped)
|
|
2355
|
+
return;
|
|
2343
2356
|
const reason = error instanceof Error ? error.message : String(error);
|
|
2344
2357
|
const transient = error instanceof runtime_readiness_js_1.RuntimePrerequisiteError && error.readiness.state === "unavailable";
|
|
2345
2358
|
agent.disabledReason = transient ? null : reason;
|
|
@@ -2353,6 +2366,8 @@ function createRuntimeHost(options) {
|
|
|
2353
2366
|
emit({ type: "agent-status", agentId: config.agentId, status: "error", error: reason,
|
|
2354
2367
|
...(error instanceof runtime_readiness_js_1.RuntimePrerequisiteError ? { readiness: error.readiness } : {}) });
|
|
2355
2368
|
}
|
|
2369
|
+
if (agent.stopped)
|
|
2370
|
+
return;
|
|
2356
2371
|
const visibleQuarantines = startupQuarantined.filter(({ record }) => agent.records.get(record.deliveryId)?.status === "error");
|
|
2357
2372
|
for (const { record, code } of visibleQuarantines)
|
|
2358
2373
|
emit({ type: "delivery", agentId: config.agentId,
|
|
@@ -2447,6 +2462,7 @@ function createRuntimeHost(options) {
|
|
|
2447
2462
|
if (!agent)
|
|
2448
2463
|
return;
|
|
2449
2464
|
agent.stopped = true;
|
|
2465
|
+
const starting = agent.starting;
|
|
2450
2466
|
if (agent.poller)
|
|
2451
2467
|
clearInterval(agent.poller);
|
|
2452
2468
|
if (agent.retryTimer)
|
|
@@ -2460,6 +2476,10 @@ function createRuntimeHost(options) {
|
|
|
2460
2476
|
if (agent.busy)
|
|
2461
2477
|
await agent.session?.cancel(reason);
|
|
2462
2478
|
await agent.session?.close(reason);
|
|
2479
|
+
// A queued Pi initializer cannot be cancelled through the public adapter
|
|
2480
|
+
// contract. Drain it before removing the Agent; ensureSession sees stopped
|
|
2481
|
+
// and closes any session created after this point.
|
|
2482
|
+
await starting?.catch(() => { });
|
|
2463
2483
|
managed.delete(agentId);
|
|
2464
2484
|
emit({ type: "agent-status", agentId, status: "inactive" });
|
|
2465
2485
|
},
|
|
@@ -1631,38 +1631,45 @@ export function createRuntimeHost(options) {
|
|
|
1631
1631
|
const ensureSession = async (agent) => {
|
|
1632
1632
|
if (agent.disabledReason)
|
|
1633
1633
|
throw new Error(agent.disabledReason);
|
|
1634
|
+
if (agent.stopped)
|
|
1635
|
+
throw new Error("runtime Agent is stopped");
|
|
1634
1636
|
if (agent.session)
|
|
1635
1637
|
return agent.session;
|
|
1636
1638
|
if (agent.starting)
|
|
1637
1639
|
return agent.starting;
|
|
1638
|
-
const probeEnv = runtimeEnv(agent.config);
|
|
1639
|
-
await assertOfficialCliReady(agent.config, probeEnv);
|
|
1640
|
-
const readiness = agent.adapter.probe ? await agent.adapter.probe({ agentId: agent.config.agentId,
|
|
1641
|
-
workspaceDir: agent.config.workspaceDir, stateDir: agent.config.stateDir,
|
|
1642
|
-
env: { LARKIN_PI_COMMAND: process.env.LARKIN_PI_COMMAND, LARKIN_CODEX_COMMAND: process.env.LARKIN_CODEX_COMMAND,
|
|
1643
|
-
LARKIN_CLAUDE_COMMAND: process.env.LARKIN_CLAUDE_COMMAND, ...probeEnv } })
|
|
1644
|
-
: { runtime: agent.adapter.id, state: "ready" };
|
|
1645
|
-
if (!agent.authFailureActive)
|
|
1646
|
-
agent.readiness = readiness;
|
|
1647
|
-
if (readiness.state !== "ready")
|
|
1648
|
-
throw new RuntimePrerequisiteError(readiness);
|
|
1649
|
-
const standingPrompt = options.promptBuilder.build({
|
|
1650
|
-
agentId: agent.config.agentId, name: agent.config.displayName || agent.config.name,
|
|
1651
|
-
description: agent.config.description || "", runtime: agent.adapter.id,
|
|
1652
|
-
cli: agentCliPromptCapabilities("larkin"), previousSession: agent.previousSession,
|
|
1653
|
-
});
|
|
1654
|
-
const generation = ++agent.generation;
|
|
1655
1640
|
let completedSession = null;
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1641
|
+
agent.starting = (async () => {
|
|
1642
|
+
const probeEnv = runtimeEnv(agent.config);
|
|
1643
|
+
await assertOfficialCliReady(agent.config, probeEnv);
|
|
1644
|
+
if (agent.stopped)
|
|
1645
|
+
throw new Error("runtime Agent is stopped");
|
|
1646
|
+
const readiness = agent.adapter.probe ? await agent.adapter.probe({ agentId: agent.config.agentId,
|
|
1647
|
+
workspaceDir: agent.config.workspaceDir, stateDir: agent.config.stateDir,
|
|
1648
|
+
env: { LARKIN_PI_COMMAND: process.env.LARKIN_PI_COMMAND, LARKIN_CODEX_COMMAND: process.env.LARKIN_CODEX_COMMAND,
|
|
1649
|
+
LARKIN_CLAUDE_COMMAND: process.env.LARKIN_CLAUDE_COMMAND, ...probeEnv } })
|
|
1650
|
+
: { runtime: agent.adapter.id, state: "ready" };
|
|
1651
|
+
if (!agent.authFailureActive)
|
|
1652
|
+
agent.readiness = readiness;
|
|
1653
|
+
if (readiness.state !== "ready")
|
|
1654
|
+
throw new RuntimePrerequisiteError(readiness);
|
|
1655
|
+
if (agent.stopped)
|
|
1656
|
+
throw new Error("runtime Agent is stopped");
|
|
1657
|
+
const standingPrompt = options.promptBuilder.build({
|
|
1658
|
+
agentId: agent.config.agentId, name: agent.config.displayName || agent.config.name,
|
|
1659
|
+
description: agent.config.description || "", runtime: agent.adapter.id,
|
|
1660
|
+
cli: agentCliPromptCapabilities("larkin"), previousSession: agent.previousSession,
|
|
1661
|
+
});
|
|
1662
|
+
const generation = ++agent.generation;
|
|
1663
|
+
const sessionEnv = runtimeEnv(agent.config, `${agent.launchId}:${generation}`);
|
|
1664
|
+
const session = await agent.adapter.createSession({
|
|
1665
|
+
agentId: agent.config.agentId, model: agent.config.model, reasoningEffort: agent.config.effort || null,
|
|
1666
|
+
workspaceDir: agent.config.workspaceDir, stateDir: agent.config.stateDir,
|
|
1667
|
+
resumeSessionId: agent.config.sessionId || null, standingPrompt,
|
|
1668
|
+
env: sessionEnv,
|
|
1669
|
+
});
|
|
1663
1670
|
completedSession = session;
|
|
1664
1671
|
if (agent.stopped || generation !== agent.generation) {
|
|
1665
|
-
|
|
1672
|
+
await session.close("stale creation");
|
|
1666
1673
|
throw new Error("stale runtime session creation");
|
|
1667
1674
|
}
|
|
1668
1675
|
agent.session = session;
|
|
@@ -1682,7 +1689,7 @@ export function createRuntimeHost(options) {
|
|
|
1682
1689
|
agent.stabilityTimer = setTimeout(() => markSessionStable(agent, session), retryPolicy.stableWindowMs);
|
|
1683
1690
|
agent.stabilityTimer.unref?.();
|
|
1684
1691
|
return session;
|
|
1685
|
-
}).finally(() => {
|
|
1692
|
+
})().finally(() => {
|
|
1686
1693
|
agent.starting = null;
|
|
1687
1694
|
if (completedSession && !agent.session && !agent.stopped) {
|
|
1688
1695
|
scheduleRecreate(agent, agent.recreateReason || "runtime closed during session initialization");
|
|
@@ -2320,6 +2327,8 @@ export function createRuntimeHost(options) {
|
|
|
2320
2327
|
emitConsumed(agent, [...startupConsumed, ...persist(agent)]);
|
|
2321
2328
|
try {
|
|
2322
2329
|
await ensureSession(agent);
|
|
2330
|
+
if (agent.stopped)
|
|
2331
|
+
return;
|
|
2323
2332
|
reconcileSubagentLedger(agent, { forceMissing: true, missingReason: "runtime restarted" });
|
|
2324
2333
|
enqueueUndeliveredTerminalWakes(agent);
|
|
2325
2334
|
await recoverStalePiCompaction(agent);
|
|
@@ -2327,10 +2336,14 @@ export function createRuntimeHost(options) {
|
|
|
2327
2336
|
const proactive = startupSession ? proactivelyCompactPiAtIdle(agent, startupSession) : null;
|
|
2328
2337
|
if (proactive)
|
|
2329
2338
|
await proactive;
|
|
2339
|
+
if (agent.stopped)
|
|
2340
|
+
return;
|
|
2330
2341
|
activeCount += 1;
|
|
2331
2342
|
await retryPending(agent);
|
|
2332
2343
|
}
|
|
2333
2344
|
catch (error) {
|
|
2345
|
+
if (agent.stopped)
|
|
2346
|
+
return;
|
|
2334
2347
|
const reason = error instanceof Error ? error.message : String(error);
|
|
2335
2348
|
const transient = error instanceof RuntimePrerequisiteError && error.readiness.state === "unavailable";
|
|
2336
2349
|
agent.disabledReason = transient ? null : reason;
|
|
@@ -2344,6 +2357,8 @@ export function createRuntimeHost(options) {
|
|
|
2344
2357
|
emit({ type: "agent-status", agentId: config.agentId, status: "error", error: reason,
|
|
2345
2358
|
...(error instanceof RuntimePrerequisiteError ? { readiness: error.readiness } : {}) });
|
|
2346
2359
|
}
|
|
2360
|
+
if (agent.stopped)
|
|
2361
|
+
return;
|
|
2347
2362
|
const visibleQuarantines = startupQuarantined.filter(({ record }) => agent.records.get(record.deliveryId)?.status === "error");
|
|
2348
2363
|
for (const { record, code } of visibleQuarantines)
|
|
2349
2364
|
emit({ type: "delivery", agentId: config.agentId,
|
|
@@ -2438,6 +2453,7 @@ export function createRuntimeHost(options) {
|
|
|
2438
2453
|
if (!agent)
|
|
2439
2454
|
return;
|
|
2440
2455
|
agent.stopped = true;
|
|
2456
|
+
const starting = agent.starting;
|
|
2441
2457
|
if (agent.poller)
|
|
2442
2458
|
clearInterval(agent.poller);
|
|
2443
2459
|
if (agent.retryTimer)
|
|
@@ -2451,6 +2467,10 @@ export function createRuntimeHost(options) {
|
|
|
2451
2467
|
if (agent.busy)
|
|
2452
2468
|
await agent.session?.cancel(reason);
|
|
2453
2469
|
await agent.session?.close(reason);
|
|
2470
|
+
// A queued Pi initializer cannot be cancelled through the public adapter
|
|
2471
|
+
// contract. Drain it before removing the Agent; ensureSession sees stopped
|
|
2472
|
+
// and closes any session created after this point.
|
|
2473
|
+
await starting?.catch(() => { });
|
|
2454
2474
|
managed.delete(agentId);
|
|
2455
2475
|
emit({ type: "agent-status", agentId, status: "inactive" });
|
|
2456
2476
|
},
|