larkin 0.4.29 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +5 -9
  3. package/artifacts/release/THIRD_PARTY_NOTICES.txt +751 -4425
  4. package/dist/app/agent-cli.mjs +14 -2
  5. package/dist/app/agent-config.mjs +26 -86
  6. package/dist/app/binary-entry.mjs +0 -20
  7. package/dist/app/cli.mjs +6 -22
  8. package/dist/app/internal-command.cjs +0 -2
  9. package/dist/app/internal-command.mjs +0 -2
  10. package/dist/app/runtime-model-directory.mjs +2 -16
  11. package/dist/app/runtime-process.mjs +0 -1
  12. package/dist/app/setup.mjs +52 -57
  13. package/dist/dashboard/dashboard-config-controller.mjs +43 -109
  14. package/dist/dashboard/dashboard-view-model.mjs +4 -7
  15. package/dist/dashboard/web/assets/dashboard.css +1 -1
  16. package/dist/dashboard/web/assets/dashboard.js +15 -15
  17. package/dist/feishu/host-shell.cjs +1 -2
  18. package/dist/feishu/host-shell.mjs +1 -2
  19. package/dist/platform/config.cjs +168 -133
  20. package/dist/platform/config.mjs +168 -133
  21. package/dist/runtime/pi-artifact-provenance.mjs +3 -3
  22. package/dist/runtime/pi-bash-timeout-extension.mjs +4 -5
  23. package/dist/runtime/pi-compaction-recovery.cjs +45 -15
  24. package/dist/runtime/pi-compaction-recovery.mjs +43 -14
  25. package/dist/runtime/pi-extension-api.mjs +1 -0
  26. package/dist/runtime/pi-model-catalog.cjs +7 -8
  27. package/dist/runtime/pi-model-catalog.mjs +7 -8
  28. package/dist/runtime/pi-subagent-injection.mjs +17 -4
  29. package/dist/runtime/runtime-adapters.mjs +26 -43
  30. package/dist/runtime/runtime-host.cjs +21 -43
  31. package/dist/runtime/runtime-host.mjs +22 -44
  32. package/dist/runtime/runtime-model-catalog.cjs +1 -1
  33. package/dist/runtime/runtime-model-catalog.mjs +1 -1
  34. package/dist/runtime/runtime-readiness.cjs +62 -98
  35. package/dist/runtime/runtime-readiness.mjs +60 -97
  36. package/dist/runtime/user-runtime.cjs +7 -24
  37. package/dist/runtime/user-runtime.mjs +7 -23
  38. package/dist/setup/bot-register.mjs +15 -123
  39. package/dist/setup/setup-agent-choice.mjs +48 -74
  40. package/dist/setup/setup-bind.mjs +51 -151
  41. package/dist/setup/setup-binding.mjs +6 -9
  42. package/package.json +3 -2
  43. package/dist/app/pi-auth-cli.mjs +0 -277
  44. package/dist/platform/process-boundary-trace.cjs +0 -108
  45. package/dist/runtime/builtin-pi-assets.cjs +0 -117
  46. package/dist/runtime/builtin-pi-assets.mjs +0 -105
  47. package/dist/runtime/pi-artifact-provenance.cjs +0 -147
  48. package/dist/runtime/pi-distribution-label.cjs +0 -6
  49. package/dist/runtime/pi-distribution-label.mjs +0 -3
  50. package/dist/runtime/pi-inline-extensions.mjs +0 -78
  51. package/dist/runtime/pi-official-auth.cjs +0 -594
  52. package/dist/runtime/pi-official-auth.mjs +0 -544
  53. package/dist/runtime/pi-profile-migration.cjs +0 -764
  54. package/dist/runtime/pi-profile-migration.mjs +0 -719
  55. package/dist/runtime/pi-provider-config.cjs +0 -354
  56. package/dist/runtime/pi-provider-config.mjs +0 -334
  57. package/dist/runtime/pi-provider-login.mjs +0 -240
@@ -2,7 +2,8 @@ import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
- import { BUNDLED_PI_VERSION } from "./pi-provider-config.mjs";
5
+ /** External `pi` must be this version or newer; handshake still guards protocol compatibility. */
6
+ export const MINIMUM_PI_VERSION = "0.84.2";
6
7
  /** Fallback values used while importing a Pi profile, before Pi reports its model. */
7
8
  export const PI_CONTEXT_WINDOW = 272_000;
8
9
  export const COMPACTION_RESERVE_TOKENS = 40_800;
@@ -232,23 +233,53 @@ export function readOwnedPiSettings(directory) {
232
233
  }
233
234
  return { compaction: { enabled, reserveTokens: values.reserveTokens, keepRecentTokens: values.keepRecentTokens } };
234
235
  }
236
+ const PI_VERSION_LINE = /^(?:pi(?:-coding-agent)?(?:\s+version)?\s+v?)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/i;
237
+ const PI_VERSION_CORE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?$/;
238
+ function comparePiNumericVersions(left, right) {
239
+ const leftParts = left.split(".").map(Number);
240
+ const rightParts = right.split(".").map(Number);
241
+ for (let index = 0; index < 3; index += 1) {
242
+ if (leftParts[index] !== rightParts[index])
243
+ return Math.sign(leftParts[index] - rightParts[index]);
244
+ }
245
+ return 0;
246
+ }
247
+ export function olderThanMinimumPiVersionMessage(version) {
248
+ return `Pi executable version ${version} is older than the minimum ${MINIMUM_PI_VERSION}`;
249
+ }
250
+ function assertSupportedPiVersion(version) {
251
+ if (typeof version !== "string")
252
+ throw new Error(`Pi executable version ${MINIMUM_PI_VERSION} or newer is required`);
253
+ const raw = version.trim();
254
+ const match = PI_VERSION_CORE.exec(raw);
255
+ if (!match)
256
+ throw new Error(`Pi executable version ${raw || "missing"} is not a supported version`);
257
+ const numeric = `${match[1]}.${match[2]}.${match[3]}`;
258
+ if (match[4]) {
259
+ throw new Error(`Pi executable version ${raw} is unsupported: SemVer ${raw} < ${numeric}; Larkin supports stable external pi only`);
260
+ }
261
+ if (comparePiNumericVersions(numeric, MINIMUM_PI_VERSION) < 0) {
262
+ throw new Error(olderThanMinimumPiVersionMessage(numeric));
263
+ }
264
+ return numeric;
265
+ }
235
266
  export function parsePiExecutableVersion(output) {
236
267
  const lines = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
237
268
  if (lines.length !== 1)
238
269
  throw new Error("Pi executable version output must contain exactly one version line");
239
- const line = lines[0];
240
- // Pi's bare version is canonical; permit only the fixed official display prefix.
241
- const escapedVersion = BUNDLED_PI_VERSION.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
242
- if (!new RegExp(`^(?:${escapedVersion}|pi(?:-coding-agent)?(?:\\s+version)?\\s+v?${escapedVersion})$`, "i").test(line)) {
243
- throw new Error(`Pi executable version must be exactly ${BUNDLED_PI_VERSION}`);
244
- }
245
- return BUNDLED_PI_VERSION;
270
+ const match = PI_VERSION_LINE.exec(lines[0]);
271
+ if (!match)
272
+ throw new Error("Pi executable version output is not a supported version line");
273
+ const version = `${match[1]}.${match[2]}.${match[3]}`;
274
+ if (comparePiNumericVersions(version, MINIMUM_PI_VERSION) < 0) {
275
+ throw new Error(olderThanMinimumPiVersionMessage(version));
276
+ }
277
+ return version;
246
278
  }
247
279
  export function verifyPiCapabilities(capabilities) {
248
- if (capabilities.version !== BUNDLED_PI_VERSION)
249
- throw new Error(`trusted Pi version ${BUNDLED_PI_VERSION} is required`);
250
- if (capabilities.distribution === "external" && capabilities.trustedProtocol === true) {
251
- throw new Error("external Pi cannot use the bundled trusted protocol bypass");
280
+ assertSupportedPiVersion(capabilities.version);
281
+ if (capabilities.trustedProtocol === true) {
282
+ throw new Error("external Pi cannot use a trusted protocol bypass");
252
283
  }
253
284
  const contextWindow = capabilities.contextWindow ?? capabilities.model?.contextWindow;
254
285
  const expected = (() => {
@@ -264,8 +295,6 @@ export function verifyPiCapabilities(capabilities) {
264
295
  }
265
296
  if (capabilities.compactRpc !== true)
266
297
  throw new Error("Pi compact RPC capability is missing");
267
- if (capabilities.distribution === "builtin" && capabilities.trustedProtocol === true)
268
- return;
269
298
  if (capabilities.reserveTokens !== expected.reserveTokens
270
299
  || capabilities.keepRecentTokens !== expected.keepRecentTokens) {
271
300
  throw new Error(`Pi external effective compaction reserve/keep settings are unproven for context window ${contextWindow}`);
@@ -0,0 +1 @@
1
+ export {};
@@ -41,7 +41,6 @@ exports.findExactPiModel = findExactPiModel;
41
41
  exports.discoverPiModelCatalog = discoverPiModelCatalog;
42
42
  const node_child_process_1 = require("node:child_process");
43
43
  const path = __importStar(require("node:path"));
44
- const builtin_pi_assets_js_1 = require("./builtin-pi-assets.cjs");
45
44
  const pi_rpc_client_js_1 = require("./pi-rpc-client.cjs");
46
45
  const runtime_readiness_js_1 = require("./runtime-readiness.cjs");
47
46
  exports.PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
@@ -73,16 +72,16 @@ function findExactPiModel(reference, models) {
73
72
  const discoveryCache = new Map();
74
73
  /** Discover only through Pi's structured RPC protocol; no table parsing or static fallback. */
75
74
  function piCatalogChildEnv(options) {
76
- const mergedEnv = {
75
+ const env = {
77
76
  ...process.env,
78
77
  ...options.env,
79
- ...(options.agentDir ? { PI_CODING_AGENT_DIR: path.resolve(options.agentDir) } : {}),
80
78
  NO_COLOR: "1",
81
79
  };
82
- return (0, builtin_pi_assets_js_1.applyPiPackageDirForChild)(mergedEnv, {
83
- distribution: (0, builtin_pi_assets_js_1.catalogPiChildDistribution)(options.commandArgs),
84
- explicitPackageDir: options.packageDir,
85
- });
80
+ if (options.agentDir)
81
+ env.PI_CODING_AGENT_DIR = path.resolve(options.agentDir);
82
+ else
83
+ delete env.PI_CODING_AGENT_DIR;
84
+ return env;
86
85
  }
87
86
  async function discoverPiModelCatalog(options) {
88
87
  if (!options.spawn) {
@@ -118,7 +117,7 @@ async function discoverPiModelCatalogUncached(options) {
118
117
  ]);
119
118
  const available = [...(availableResponse?.models ?? [])].sort((left, right) => piModelId(left).localeCompare(piModelId(right)));
120
119
  if (!available.length)
121
- throw new Error("Pi has no authenticated available models. Run the official `pi` login flow or configure provider credentials; Larkin will not create a fallback session.");
120
+ throw new Error("Pi has no authenticated available models. Run the official `pi` login flow; Larkin will not create a fallback session.");
122
121
  const effectiveModel = state.model ? piModelId(state.model) : null;
123
122
  if (!effectiveModel || !available.some((model) => piModelId(model) === effectiveModel)) {
124
123
  throw new Error(`Pi official default resolution returned an unavailable model (${effectiveModel || "none"}); refusing implicit fallback`);
@@ -1,6 +1,5 @@
1
1
  import { spawn as nodeSpawn } from "node:child_process";
2
2
  import * as path from "node:path";
3
- import { applyPiPackageDirForChild, catalogPiChildDistribution } from "./builtin-pi-assets.mjs";
4
3
  import { PiRpcClient } from "./pi-rpc-client.mjs";
5
4
  import { classifyRuntimePrerequisite, RuntimePrerequisiteError } from "./runtime-readiness.mjs";
6
5
  export const PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
@@ -32,16 +31,16 @@ export function findExactPiModel(reference, models) {
32
31
  const discoveryCache = new Map();
33
32
  /** Discover only through Pi's structured RPC protocol; no table parsing or static fallback. */
34
33
  function piCatalogChildEnv(options) {
35
- const mergedEnv = {
34
+ const env = {
36
35
  ...process.env,
37
36
  ...options.env,
38
- ...(options.agentDir ? { PI_CODING_AGENT_DIR: path.resolve(options.agentDir) } : {}),
39
37
  NO_COLOR: "1",
40
38
  };
41
- return applyPiPackageDirForChild(mergedEnv, {
42
- distribution: catalogPiChildDistribution(options.commandArgs),
43
- explicitPackageDir: options.packageDir,
44
- });
39
+ if (options.agentDir)
40
+ env.PI_CODING_AGENT_DIR = path.resolve(options.agentDir);
41
+ else
42
+ delete env.PI_CODING_AGENT_DIR;
43
+ return env;
45
44
  }
46
45
  export async function discoverPiModelCatalog(options) {
47
46
  if (!options.spawn) {
@@ -77,7 +76,7 @@ async function discoverPiModelCatalogUncached(options) {
77
76
  ]);
78
77
  const available = [...(availableResponse?.models ?? [])].sort((left, right) => piModelId(left).localeCompare(piModelId(right)));
79
78
  if (!available.length)
80
- throw new Error("Pi has no authenticated available models. Run the official `pi` login flow or configure provider credentials; Larkin will not create a fallback session.");
79
+ throw new Error("Pi has no authenticated available models. Run the official `pi` login flow; Larkin will not create a fallback session.");
81
80
  const effectiveModel = state.model ? piModelId(state.model) : null;
82
81
  if (!effectiveModel || !available.some((model) => piModelId(model) === effectiveModel)) {
83
82
  throw new Error(`Pi official default resolution returned an unavailable model (${effectiveModel || "none"}); refusing implicit fallback`);
@@ -160,12 +160,25 @@ export function piVersionSupportsSubagents(version) {
160
160
  return false;
161
161
  return version.major > 0 || (version.major === 0 && version.minor >= 80);
162
162
  }
163
- /** 解析 external `pi --version`;无法确认版本时视为不支持,不注入。 */
163
+ const externalPiVersionProbeCache = new Map();
164
+ function externalPiVersionProbeKey(piCommand, env) {
165
+ return JSON.stringify([piCommand, env.PATH ?? ""]);
166
+ }
167
+ /** 测试用:清空进程内 `pi --version` 探测缓存。 */
168
+ export function resetExternalPiVersionProbeCache() {
169
+ externalPiVersionProbeCache.clear();
170
+ }
171
+ /** 解析 external `pi --version`;无法确认版本时视为不支持,不注入。按 piCommand+PATH 缓存。 */
164
172
  export function probeExternalPiVersion(piCommand, env) {
173
+ const key = externalPiVersionProbeKey(piCommand, env);
174
+ if (externalPiVersionProbeCache.has(key))
175
+ return externalPiVersionProbeCache.get(key) ?? null;
165
176
  const result = spawnSync(piCommand, ["--version"], { env, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024 });
166
- if (result.error || result.status !== 0)
167
- return null;
168
- return parsePiVersion(String(result.stdout || result.stderr || ""));
177
+ const version = result.error || result.status !== 0
178
+ ? null
179
+ : parsePiVersion(String(result.stdout || result.stderr || ""));
180
+ externalPiVersionProbeCache.set(key, version);
181
+ return version;
169
182
  }
170
183
  /**
171
184
  * 用户 pi 是否已自行安装 pi-subagents(settings.json packages 或包目录)。
@@ -3,11 +3,8 @@ import crypto from "node:crypto";
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
5
  import { createInterface } from "node:readline";
6
- import { applyPiPackageDirForChild, piChildDistributionFromOverrides } from "./builtin-pi-assets.mjs";
7
6
  import { isPiThinkingLevel } from "./pi-model-catalog.mjs";
8
7
  import { PiRpcClient } from "./pi-rpc-client.mjs";
9
- import { internalCommandSpec } from "../app/internal-command.mjs";
10
- import { BUNDLED_PI_VERSION, piAgentDirectory } from "./pi-provider-config.mjs";
11
8
  import { recordPiRuntimeArtifactProvenance } from "./pi-artifact-provenance.mjs";
12
9
  import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
13
10
  import { resolvePiSubagentExtensionArg } from "./pi-subagent-injection.mjs";
@@ -15,9 +12,12 @@ import { extractCanonicalPiSubagentNotification, ledgerStatusFromPiNotificationS
15
12
  import { resolvePiSubagentRecordWatchdogExtensionArg } from "./pi-subagent-record-watchdog-injection.mjs";
16
13
  import { effectivePiStateDir, extractBackgroundPiSubagentDispatch } from "./pi-subagent-ledger.mjs";
17
14
  import { resolvePiBashTimeoutExtensionArg } from "./pi-bash-timeout-injection.mjs";
18
- import { classifyPiMissingCredentialRejection, classifyRuntimePrerequisite, isBuiltinPiDistribution, missingProviderCredentialReadiness, probeNativeRuntimeReadiness, providerAuthenticationFailureReadiness, RuntimePrerequisiteError, } from "./runtime-readiness.mjs";
15
+ import { classifyPiMissingCredentialRejection, classifyRuntimePrerequisite, missingProviderCredentialReadiness, probeNativeRuntimeReadiness, providerAuthenticationFailureReadiness, RuntimePrerequisiteError, } from "./runtime-readiness.mjs";
19
16
  import { buildStrictProviderErrorInput, classifyStrictProviderError, INTERNAL_CONTEXT_WINDOW_PROJECTION_REASON, } from "./provider-error-classifier.mjs";
20
- import { assertEffectivePiCompactionSettings, assertNoProjectPiCompactionOverride, calculatePiCompactionSettings, parsePiExecutableVersion, prepareOwnedPiDirectory, readOwnedPiSettings, verifyPiCapabilities, writeOwnedPiSettings, } from "./pi-compaction-recovery.mjs";
17
+ import { assertEffectivePiCompactionSettings, assertNoProjectPiCompactionOverride, MINIMUM_PI_VERSION, calculatePiCompactionSettings, parsePiExecutableVersion, prepareOwnedPiDirectory, readOwnedPiSettings, verifyPiCapabilities, writeOwnedPiSettings, } from "./pi-compaction-recovery.mjs";
18
+ function piAgentDirectory(configDir, agentId) {
19
+ return path.join(path.resolve(configDir), "providers", "pi", agentId);
20
+ }
21
21
  class EventSession {
22
22
  listeners = new Set();
23
23
  subscribe(listener) {
@@ -462,7 +462,7 @@ class PiSession extends EventSession {
462
462
  firstOutputObserved = false;
463
463
  toolCallOpen = false;
464
464
  piPolicyManaged;
465
- constructor(sdk, distribution) {
465
+ constructor(sdk, distribution = "external") {
466
466
  super();
467
467
  this.sdk = sdk;
468
468
  this.distribution = distribution;
@@ -538,17 +538,16 @@ class PiSession extends EventSession {
538
538
  this.ownedInputIds.delete(input.inputId);
539
539
  this.inputEpochs.delete(input.inputId);
540
540
  const rawReason = error instanceof Error ? error.message : String(error);
541
- const missing = isBuiltinPiDistribution(this.distribution)
542
- ? classifyPiMissingCredentialRejection(rawReason)
543
- : null;
541
+ const missing = classifyPiMissingCredentialRejection(rawReason);
544
542
  if (missing) {
545
- const classified = classifyPiProviderError({ provider: missing.provider, message: missing.diagnostic }, { distribution: this.distribution });
543
+ const classified = classifyPiProviderError({ provider: missing.provider, message: missing.diagnostic });
546
544
  this.emit({
547
545
  type: "input-error", inputId: input.inputId, retryable: false, willRetry: false,
548
546
  message: classified.reason, errorCategory: classified.category, nextAction: classified.nextAction,
549
547
  upstream: { provider: missing.provider, message: missing.diagnostic },
550
548
  });
551
- return { status: "rejected", inputId: input.inputId, retryable: false, reason: classified.reason };
549
+ // 拒绝原因保留 Pi 原文,方便 RuntimeHost 独立分类,不依赖 input-error 事件顺序。
550
+ return { status: "rejected", inputId: input.inputId, retryable: false, reason: rawReason };
552
551
  }
553
552
  return { status: "rejected", inputId: input.inputId, retryable: true, reason: rawReason };
554
553
  }
@@ -831,9 +830,7 @@ export function classifyPiProviderError(error, scope) {
831
830
  return {
832
831
  category: "rate_limit", reason, nextAction: "Wait for the provider rate-limit window, then retry.",
833
832
  };
834
- const missing = isBuiltinPiDistribution(scope?.distribution)
835
- ? classifyPiMissingCredentialRejection(reason)
836
- : null;
833
+ const missing = classifyPiMissingCredentialRejection(reason);
837
834
  if (missing) {
838
835
  const readiness = missingProviderCredentialReadiness("pi", missing.provider);
839
836
  return { category: "auth", reason: readiness.reason, nextAction: readiness.nextAction };
@@ -1003,10 +1000,6 @@ class PiRpcBackend {
1003
1000
  }
1004
1001
  }
1005
1002
  export function resolvePiProcessExtensionArgs(input, resolvers = {}) {
1006
- // Builtin factories are passed directly to Pi main on every platform. The platform
1007
- // field makes that invariant explicit and testable without changing process.platform.
1008
- if (input.distribution === "builtin")
1009
- return [];
1010
1003
  const resolverInput = { distribution: "external", piCommand: input.piCommand, env: input.env };
1011
1004
  const args = [];
1012
1005
  // Watchdog must load before the subagent extension so session_shutdown still
@@ -1044,31 +1037,23 @@ async function createPiRpcBackend(input, dependencies, spawn, productionSpawn =
1044
1037
  ...(session.sessionFile ? ["--session", session.sessionFile] : []),
1045
1038
  ...(requestedModel ? ["--model", requestedModel] : []),
1046
1039
  ...(requestedEffort ? ["--thinking", requestedEffort] : [])];
1047
- const builtin = piChildDistributionFromOverrides(dependencies.env, input.env) === "builtin";
1048
- if (!builtin && mergedEnv.LARKIN_PI_DISTRIBUTION === "builtin")
1040
+ if (mergedEnv.LARKIN_PI_DISTRIBUTION === "builtin")
1049
1041
  delete mergedEnv.LARKIN_PI_DISTRIBUTION;
1050
- const builtinSpec = builtin ? internalCommandSpec("pi-rpc", [], mergedEnv) : null;
1051
- const command = builtinSpec?.command ?? dependencies.piCommand ?? dependencies.env?.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi";
1052
- const commandPrefix = builtinSpec?.args ?? dependencies.piCommandArgs ?? [];
1042
+ const command = dependencies.piCommand ?? dependencies.env?.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi";
1043
+ const commandPrefix = dependencies.piCommandArgs ?? [];
1053
1044
  const commandArgs = [...commandPrefix, ...args];
1054
1045
  mergedEnv.PI_CODING_AGENT_DIR = ownedPiDirectory;
1055
- if (builtin)
1056
- mergedEnv.PI_TELEMETRY = "0";
1057
- const childEnv = applyPiPackageDirForChild(mergedEnv, {
1058
- distribution: builtin ? "builtin" : "external",
1059
- explicitPackageDir: input.env?.PI_PACKAGE_DIR ?? dependencies.env?.PI_PACKAGE_DIR,
1060
- });
1061
- const reportedVersion = productionSpawn && !builtin
1046
+ const childEnv = { ...mergedEnv };
1047
+ const reportedVersion = productionSpawn
1062
1048
  ? parsePiExecutableVersion(String(spawnSync(command, [...commandPrefix, "--version"], {
1063
1049
  cwd: input.workspaceDir, env: childEnv, encoding: "utf8", timeout: 5_000,
1064
1050
  }).stdout || ""))
1065
- : BUNDLED_PI_VERSION;
1066
- const extensionArgs = (dependencies.resolvePiProcessExtensionArgs ?? resolvePiProcessExtensionArgs)({
1067
- distribution: builtin ? "builtin" : "external",
1068
- piCommand: command,
1069
- env: childEnv,
1070
- platform: process.platform,
1071
- });
1051
+ : MINIMUM_PI_VERSION;
1052
+ const extensionInput = { piCommand: command, env: childEnv, platform: process.platform };
1053
+ // 假 spawn 不需要真实 extension 路径;默认 resolver 会 spawnSync(`pi --version`)。
1054
+ const extensionArgs = dependencies.resolvePiProcessExtensionArgs
1055
+ ? dependencies.resolvePiProcessExtensionArgs(extensionInput)
1056
+ : productionSpawn ? resolvePiProcessExtensionArgs(extensionInput) : [];
1072
1057
  commandArgs.push(...extensionArgs);
1073
1058
  let probedModel = null;
1074
1059
  if (productionSpawn) {
@@ -1094,7 +1079,7 @@ async function createPiRpcBackend(input, dependencies, spawn, productionSpawn =
1094
1079
  client.request("get_available_models"),
1095
1080
  ]);
1096
1081
  if (!available?.models?.length)
1097
- throw new Error("Pi has no authenticated available models. Run the official `pi` login flow or configure provider credentials; Larkin will not create a fallback session.");
1082
+ throw new Error("Pi has no authenticated available models. Run the official `pi` login flow; Larkin will not create a fallback session.");
1098
1083
  const effectiveModel = state.model?.provider && state.model.id ? `${state.model.provider}/${state.model.id}` : null;
1099
1084
  if (productionSpawn) {
1100
1085
  if (!effectiveModel || !probedModel || effectiveModel !== probedModel.model
@@ -1108,11 +1093,10 @@ async function createPiRpcBackend(input, dependencies, spawn, productionSpawn =
1108
1093
  keepRecentTokens: ownedSettings.compaction?.keepRecentTokens,
1109
1094
  } });
1110
1095
  const handshake = state.compactionCapabilities;
1111
- verifyPiCapabilities({ distribution: builtin ? "builtin" : "external", version: reportedVersion,
1096
+ verifyPiCapabilities({ distribution: "external", version: reportedVersion,
1112
1097
  contextWindow: state.model?.contextWindow, autoCompactionEnabled: state.autoCompactionEnabled,
1113
1098
  reserveTokens: handshake?.reserveTokens, keepRecentTokens: handshake?.keepRecentTokens,
1114
- compactRpc: true, events: handshake?.events,
1115
- trustedProtocol: builtin });
1099
+ compactRpc: true, events: handshake?.events });
1116
1100
  }
1117
1101
  // 严格对账(Owner 决策):模型必须来自 pi 的权威可用列表,不做后缀猜测——
1118
1102
  // 配置的模型与 pi 可用列表不符即报错,并在错误里列出可用模型。
@@ -1247,10 +1231,9 @@ export function createNativeRuntimeAdapter(id, dependencies = {}) {
1247
1231
  throw new RuntimePrerequisiteError(readiness);
1248
1232
  }
1249
1233
  if (id === "pi") {
1250
- const distribution = piChildDistributionFromOverrides(dependencies.env, input.env);
1251
1234
  return new PiSession(await (dependencies.createPiSession
1252
1235
  ? dependencies.createPiSession(input)
1253
- : createPiRpcBackend(input, { ...dependencies, piCommand: resolvedExecutable }, spawn, productionSpawn)), distribution);
1236
+ : createPiRpcBackend(input, { ...dependencies, piCommand: resolvedExecutable }, spawn, productionSpawn)));
1254
1237
  }
1255
1238
  if (id === "codex") {
1256
1239
  const codexInput = dependencies.codexModelOverride?.trim()
@@ -14,7 +14,6 @@ const inbox_projection_js_1 = require("../agent/inbox-projection.cjs");
14
14
  const api_1 = require("@opentelemetry/api");
15
15
  const provider_error_classifier_js_1 = require("./provider-error-classifier.cjs");
16
16
  const runtime_readiness_js_1 = require("./runtime-readiness.cjs");
17
- const pi_official_auth_js_1 = require("./pi-official-auth.cjs");
18
17
  const official_lark_cli_js_1 = require("../app/official-lark-cli.cjs");
19
18
  const agent_lark_cli_workspace_js_1 = require("../app/agent-lark-cli-workspace.cjs");
20
19
  const pi_compaction_recovery_js_1 = require("./pi-compaction-recovery.cjs");
@@ -103,37 +102,23 @@ function persistPreviousSession(agent, previous) {
103
102
  // In-memory ref still holds for this process; restart will miss it only if this write failed.
104
103
  }
105
104
  }
106
- function isBuiltinPiAgent(agent) {
107
- return agent.adapter.id === "pi"
108
- && ((0, runtime_readiness_js_1.isBuiltinPiDistribution)(agent.config.piDistribution) || agent.config.runtime === "builtin-pi");
109
- }
110
105
  function authFailureScopeOf(agent) {
111
106
  return {
112
107
  runtime: agent.config.runtime,
113
108
  model: agent.config.model,
114
- piDistribution: agent.config.piDistribution,
115
109
  adapterId: agent.adapter.id,
116
110
  };
117
111
  }
118
112
  function currentAuthFailure(agent) {
119
113
  if (!agent.authFailureActive)
120
114
  return null;
121
- const kind = agent.authFailureKind
122
- ?? (agent.authFailureProvider ? "missing-provider" : "generic");
123
- if (kind === "missing-provider") {
124
- if (!agent.authFailureProvider)
125
- return null;
126
- return { kind: "missing-provider", runtime: "pi", piDistribution: "builtin", provider: agent.authFailureProvider };
127
- }
128
115
  const runtime = agent.adapter.id === "codex" || agent.adapter.id === "claude" || agent.adapter.id === "pi"
129
116
  ? agent.adapter.id : null;
130
117
  if (!runtime)
131
118
  return null;
132
119
  return {
133
- kind: "generic",
120
+ kind: agent.authFailureKind === "missing-provider" ? "missing-provider" : "generic",
134
121
  runtime,
135
- ...(runtime === "pi" && (agent.config.piDistribution === "builtin" || agent.config.piDistribution === "external")
136
- ? { piDistribution: agent.config.piDistribution } : {}),
137
122
  provider: agent.authFailureProvider,
138
123
  };
139
124
  }
@@ -344,7 +329,6 @@ function createRuntimeHost(options) {
344
329
  } : {}),
345
330
  LARKIN_CONFIG_DIR: process.env.LARKIN_CONFIG_DIR,
346
331
  LARKIN_HOME: process.env.LARKIN_HOME,
347
- ...(config.piDistribution ? { LARKIN_PI_DISTRIBUTION: config.piDistribution } : {}),
348
332
  LARKIN_STATE_DIR: (0, pi_subagent_ledger_js_1.effectivePiStateDir)(config),
349
333
  ...(process.env.HOME ? { HOME: process.env.HOME } : {}),
350
334
  ...(process.env.SHELL ? { SHELL: process.env.SHELL } : {}),
@@ -606,10 +590,6 @@ function createRuntimeHost(options) {
606
590
  messageId: record.messageId, status: "consumed" });
607
591
  }
608
592
  };
609
- const ownedConfigDir = () => {
610
- const value = process.env.LARKIN_CONFIG_DIR?.trim();
611
- return value || null;
612
- };
613
593
  const projectAuthFailure = (agent, readiness, failure) => {
614
594
  agent.authFailureActive = true;
615
595
  agent.authFailureKind = failure.kind;
@@ -627,12 +607,7 @@ function createRuntimeHost(options) {
627
607
  if (agent.authFailureKind !== "missing-provider" || !agent.authFailureProvider)
628
608
  return true;
629
609
  const failure = currentAuthFailure(agent);
630
- if (!failure || !(0, runtime_readiness_js_1.authFailureAppliesTo)(authFailureScopeOf(agent), failure))
631
- return false;
632
- const configDir = ownedConfigDir();
633
- if (!configDir)
634
- return false;
635
- return (0, pi_official_auth_js_1.officialPiHasStoredProvider)(configDir, agent.config.agentId, agent.authFailureProvider);
610
+ return Boolean(failure && (0, runtime_readiness_js_1.authFailureAppliesTo)(authFailureScopeOf(agent), failure));
636
611
  };
637
612
  const resultFor = (agent, record, result) => {
638
613
  if (result.status === "accepted") {
@@ -647,12 +622,12 @@ function createRuntimeHost(options) {
647
622
  deliveryId: record.deliveryId, messageId: record.messageId, status: "accepted" });
648
623
  return { status: "accepted", deliveryId: record.deliveryId };
649
624
  }
650
- const missing = result.status === "rejected" && isBuiltinPiAgent(agent)
625
+ const missing = result.status === "rejected" && agent.adapter.id === "pi"
651
626
  ? (0, runtime_readiness_js_1.classifyPiMissingCredentialRejection)(result.reason)
652
627
  : null;
653
628
  if (missing) {
654
629
  const readiness = (0, runtime_readiness_js_1.missingProviderCredentialReadiness)(agent.adapter.id, missing.provider);
655
- const reason = readiness.reason ?? `Provider ${missing.provider} is missing from this Agent's official credential store.`;
630
+ const reason = readiness.reason ?? result.reason;
656
631
  if (record.status !== "error") {
657
632
  record.reason = reason;
658
633
  record.retryable = false;
@@ -1629,26 +1604,30 @@ function createRuntimeHost(options) {
1629
1604
  record.errorCategory = classifiedCategory;
1630
1605
  else
1631
1606
  delete record.errorCategory;
1632
- const missing = event.errorCategory === "auth" && !event.retryable && isBuiltinPiAgent(agent)
1607
+ const missing = agent.adapter.id === "pi"
1633
1608
  ? (0, runtime_readiness_js_1.classifyPiMissingCredentialRejection)(event.upstream?.message)
1634
1609
  ?? (0, runtime_readiness_js_1.classifyPiMissingCredentialRejection)(event.message)
1635
1610
  : null;
1636
- if (missing)
1611
+ if (missing) {
1612
+ record.retryable = false;
1613
+ record.errorCategory = "auth";
1637
1614
  record.authProvider = missing.provider;
1638
- const finalRecord = setRecord(agent, record, event.retryable ? "pending" : "error");
1615
+ }
1616
+ const terminal = Boolean(missing) || !event.retryable;
1617
+ const finalRecord = setRecord(agent, record, terminal ? "error" : "pending");
1639
1618
  if (finalRecord.status !== "consumed")
1640
1619
  emit({ type: "delivery", agentId: agent.config.agentId,
1641
1620
  deliveryId: record.deliveryId, messageId: record.messageId,
1642
- status: event.retryable ? "deferred" : "error", reason: event.message });
1643
- if (event.errorCategory === "auth" && !event.retryable) {
1644
- const readiness = missing
1645
- ? (0, runtime_readiness_js_1.missingProviderCredentialReadiness)(agent.adapter.id, missing.provider)
1646
- : (0, runtime_readiness_js_1.providerAuthenticationFailureReadiness)(agent.adapter.id, event.upstream?.provider);
1647
- projectAuthFailure(agent, readiness, missing
1648
- ? { kind: "missing-provider", provider: missing.provider }
1649
- : { kind: "generic", provider: genericAuthProvider(agent, event.upstream) });
1650
- }
1651
- if (event.retryable && !agent.busy && !agent.turnInProgress)
1621
+ status: terminal ? "error" : "deferred", reason: event.message });
1622
+ if (missing) {
1623
+ const readiness = (0, runtime_readiness_js_1.missingProviderCredentialReadiness)(agent.adapter.id, missing.provider);
1624
+ projectAuthFailure(agent, readiness, { kind: "missing-provider", provider: missing.provider });
1625
+ }
1626
+ else if (event.errorCategory === "auth" && !event.retryable) {
1627
+ const readiness = (0, runtime_readiness_js_1.providerAuthenticationFailureReadiness)(agent.adapter.id, event.upstream?.provider);
1628
+ projectAuthFailure(agent, readiness, { kind: "generic", provider: genericAuthProvider(agent, event.upstream) });
1629
+ }
1630
+ if (!terminal && event.retryable && !agent.busy && !agent.turnInProgress)
1652
1631
  void retryPending(agent);
1653
1632
  }
1654
1633
  else if (event.type === "configuration-error") {
@@ -1923,7 +1902,6 @@ function createRuntimeHost(options) {
1923
1902
  && (0, runtime_readiness_js_1.authFailureAppliesTo)({
1924
1903
  runtime: config.runtime,
1925
1904
  model: config.model,
1926
- piDistribution: config.piDistribution,
1927
1905
  adapterId: adapter.id,
1928
1906
  }, previousFailure));
1929
1907
  const candidate = {