larkin 0.5.0 → 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.
- package/CHANGELOG.md +4 -0
- package/dist/runtime/pi-compaction-recovery.cjs +43 -11
- package/dist/runtime/pi-compaction-recovery.mjs +41 -10
- package/dist/runtime/runtime-adapters.mjs +2 -2
- package/dist/runtime/runtime-readiness.cjs +22 -1
- package/dist/runtime/runtime-readiness.mjs +22 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
External pi is accepted at 0.84.2 or newer instead of exactly 0.84.2; 0.5.0 refused newer pi and left every migrated Agent not ready.
|
|
6
|
+
|
|
3
7
|
## 0.5.0
|
|
4
8
|
|
|
5
9
|
BREAKING — builtin Pi is removed. Larkin supports only externally installed `pi`, `codex`, and `claude`. If the selected runtime is not installed, setup, runtime switch, and readiness fail with an explicit missing-install message.
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.PiCompactionRecoveryMachine = exports.PiCompactionBreaker = exports.PI_COMPACTION_RECOVERY_FILE = exports.PI_COMPACTION_RECOVERY_VERSION = exports.PI_COMPACTION_THRESHOLD = exports.COMPACTION_KEEP_RECENT_TOKENS = exports.COMPACTION_RESERVE_TOKENS = exports.PI_CONTEXT_WINDOW = exports.
|
|
6
|
+
exports.PiCompactionRecoveryMachine = exports.PiCompactionBreaker = exports.PI_COMPACTION_RECOVERY_FILE = exports.PI_COMPACTION_RECOVERY_VERSION = exports.PI_COMPACTION_THRESHOLD = exports.COMPACTION_KEEP_RECENT_TOKENS = exports.COMPACTION_RESERVE_TOKENS = exports.PI_CONTEXT_WINDOW = exports.MINIMUM_PI_VERSION = void 0;
|
|
7
7
|
exports.calculatePiCompactionSettings = calculatePiCompactionSettings;
|
|
8
8
|
exports.isPiNativeCompactionRequired = isPiNativeCompactionRequired;
|
|
9
9
|
exports.assertEffectivePiCompactionSettings = assertEffectivePiCompactionSettings;
|
|
@@ -14,6 +14,7 @@ exports.prepareOwnedPiDirectory = prepareOwnedPiDirectory;
|
|
|
14
14
|
exports.mergeOwnedPiSettings = mergeOwnedPiSettings;
|
|
15
15
|
exports.writeOwnedPiSettings = writeOwnedPiSettings;
|
|
16
16
|
exports.readOwnedPiSettings = readOwnedPiSettings;
|
|
17
|
+
exports.olderThanMinimumPiVersionMessage = olderThanMinimumPiVersionMessage;
|
|
17
18
|
exports.parsePiExecutableVersion = parsePiExecutableVersion;
|
|
18
19
|
exports.verifyPiCapabilities = verifyPiCapabilities;
|
|
19
20
|
exports.prepareOwnedPiEnvironment = prepareOwnedPiEnvironment;
|
|
@@ -24,7 +25,8 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
|
24
25
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
25
26
|
const node_os_1 = __importDefault(require("node:os"));
|
|
26
27
|
const node_path_1 = __importDefault(require("node:path"));
|
|
27
|
-
|
|
28
|
+
/** External `pi` must be this version or newer; handshake still guards protocol compatibility. */
|
|
29
|
+
exports.MINIMUM_PI_VERSION = "0.84.2";
|
|
28
30
|
/** Fallback values used while importing a Pi profile, before Pi reports its model. */
|
|
29
31
|
exports.PI_CONTEXT_WINDOW = 272_000;
|
|
30
32
|
exports.COMPACTION_RESERVE_TOKENS = 40_800;
|
|
@@ -254,21 +256,51 @@ function readOwnedPiSettings(directory) {
|
|
|
254
256
|
}
|
|
255
257
|
return { compaction: { enabled, reserveTokens: values.reserveTokens, keepRecentTokens: values.keepRecentTokens } };
|
|
256
258
|
}
|
|
259
|
+
const PI_VERSION_LINE = /^(?:pi(?:-coding-agent)?(?:\s+version)?\s+v?)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/i;
|
|
260
|
+
const PI_VERSION_CORE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?$/;
|
|
261
|
+
function comparePiNumericVersions(left, right) {
|
|
262
|
+
const leftParts = left.split(".").map(Number);
|
|
263
|
+
const rightParts = right.split(".").map(Number);
|
|
264
|
+
for (let index = 0; index < 3; index += 1) {
|
|
265
|
+
if (leftParts[index] !== rightParts[index])
|
|
266
|
+
return Math.sign(leftParts[index] - rightParts[index]);
|
|
267
|
+
}
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
function olderThanMinimumPiVersionMessage(version) {
|
|
271
|
+
return `Pi executable version ${version} is older than the minimum ${exports.MINIMUM_PI_VERSION}`;
|
|
272
|
+
}
|
|
273
|
+
function assertSupportedPiVersion(version) {
|
|
274
|
+
if (typeof version !== "string")
|
|
275
|
+
throw new Error(`Pi executable version ${exports.MINIMUM_PI_VERSION} or newer is required`);
|
|
276
|
+
const raw = version.trim();
|
|
277
|
+
const match = PI_VERSION_CORE.exec(raw);
|
|
278
|
+
if (!match)
|
|
279
|
+
throw new Error(`Pi executable version ${raw || "missing"} is not a supported version`);
|
|
280
|
+
const numeric = `${match[1]}.${match[2]}.${match[3]}`;
|
|
281
|
+
if (match[4]) {
|
|
282
|
+
throw new Error(`Pi executable version ${raw} is unsupported: SemVer ${raw} < ${numeric}; Larkin supports stable external pi only`);
|
|
283
|
+
}
|
|
284
|
+
if (comparePiNumericVersions(numeric, exports.MINIMUM_PI_VERSION) < 0) {
|
|
285
|
+
throw new Error(olderThanMinimumPiVersionMessage(numeric));
|
|
286
|
+
}
|
|
287
|
+
return numeric;
|
|
288
|
+
}
|
|
257
289
|
function parsePiExecutableVersion(output) {
|
|
258
290
|
const lines = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
259
291
|
if (lines.length !== 1)
|
|
260
292
|
throw new Error("Pi executable version output must contain exactly one version line");
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
293
|
+
const match = PI_VERSION_LINE.exec(lines[0]);
|
|
294
|
+
if (!match)
|
|
295
|
+
throw new Error("Pi executable version output is not a supported version line");
|
|
296
|
+
const version = `${match[1]}.${match[2]}.${match[3]}`;
|
|
297
|
+
if (comparePiNumericVersions(version, exports.MINIMUM_PI_VERSION) < 0) {
|
|
298
|
+
throw new Error(olderThanMinimumPiVersionMessage(version));
|
|
299
|
+
}
|
|
300
|
+
return version;
|
|
268
301
|
}
|
|
269
302
|
function verifyPiCapabilities(capabilities) {
|
|
270
|
-
|
|
271
|
-
throw new Error(`trusted Pi version ${exports.PINNED_PI_VERSION} is required`);
|
|
303
|
+
assertSupportedPiVersion(capabilities.version);
|
|
272
304
|
if (capabilities.trustedProtocol === true) {
|
|
273
305
|
throw new Error("external Pi cannot use a trusted protocol bypass");
|
|
274
306
|
}
|
|
@@ -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
|
-
|
|
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,21 +233,51 @@ 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
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
249
|
-
throw new Error(`trusted Pi version ${PINNED_PI_VERSION} is required`);
|
|
280
|
+
assertSupportedPiVersion(capabilities.version);
|
|
250
281
|
if (capabilities.trustedProtocol === true) {
|
|
251
282
|
throw new Error("external Pi cannot use a trusted protocol bypass");
|
|
252
283
|
}
|
|
@@ -14,7 +14,7 @@ import { effectivePiStateDir, extractBackgroundPiSubagentDispatch } from "./pi-s
|
|
|
14
14
|
import { resolvePiBashTimeoutExtensionArg } from "./pi-bash-timeout-injection.mjs";
|
|
15
15
|
import { classifyPiMissingCredentialRejection, classifyRuntimePrerequisite, missingProviderCredentialReadiness, probeNativeRuntimeReadiness, providerAuthenticationFailureReadiness, RuntimePrerequisiteError, } from "./runtime-readiness.mjs";
|
|
16
16
|
import { buildStrictProviderErrorInput, classifyStrictProviderError, INTERNAL_CONTEXT_WINDOW_PROJECTION_REASON, } from "./provider-error-classifier.mjs";
|
|
17
|
-
import { assertEffectivePiCompactionSettings, assertNoProjectPiCompactionOverride,
|
|
17
|
+
import { assertEffectivePiCompactionSettings, assertNoProjectPiCompactionOverride, MINIMUM_PI_VERSION, calculatePiCompactionSettings, parsePiExecutableVersion, prepareOwnedPiDirectory, readOwnedPiSettings, verifyPiCapabilities, writeOwnedPiSettings, } from "./pi-compaction-recovery.mjs";
|
|
18
18
|
function piAgentDirectory(configDir, agentId) {
|
|
19
19
|
return path.join(path.resolve(configDir), "providers", "pi", agentId);
|
|
20
20
|
}
|
|
@@ -1048,7 +1048,7 @@ async function createPiRpcBackend(input, dependencies, spawn, productionSpawn =
|
|
|
1048
1048
|
? parsePiExecutableVersion(String(spawnSync(command, [...commandPrefix, "--version"], {
|
|
1049
1049
|
cwd: input.workspaceDir, env: childEnv, encoding: "utf8", timeout: 5_000,
|
|
1050
1050
|
}).stdout || ""))
|
|
1051
|
-
:
|
|
1051
|
+
: MINIMUM_PI_VERSION;
|
|
1052
1052
|
const extensionInput = { piCommand: command, env: childEnv, platform: process.platform };
|
|
1053
1053
|
// 假 spawn 不需要真实 extension 路径;默认 resolver 会 spawnSync(`pi --version`)。
|
|
1054
1054
|
const extensionArgs = dependencies.resolvePiProcessExtensionArgs
|
|
@@ -53,6 +53,7 @@ exports.probeNativeRuntimeReadiness = probeNativeRuntimeReadiness;
|
|
|
53
53
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
54
54
|
const node_path_1 = __importDefault(require("node:path"));
|
|
55
55
|
const node_child_process_1 = require("node:child_process");
|
|
56
|
+
const pi_compaction_recovery_js_1 = require("./pi-compaction-recovery.cjs");
|
|
56
57
|
function safeProviderId(value) {
|
|
57
58
|
const provider = typeof value === "string" ? value.trim() : "";
|
|
58
59
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(provider) ? provider : null;
|
|
@@ -191,6 +192,14 @@ function classifyRuntimePrerequisite(runtime, error, executable) {
|
|
|
191
192
|
runtime, state: "unauthenticated", ...(executable ? { executable } : {}), reason,
|
|
192
193
|
nextAction: runtimeLoginNextAction(runtime),
|
|
193
194
|
};
|
|
195
|
+
if (/older than the minimum|not a supported version(?: line)?|version output must contain exactly one/i.test(reason)) {
|
|
196
|
+
return {
|
|
197
|
+
runtime, state: "incompatible", ...(executable ? { executable } : {}), reason,
|
|
198
|
+
nextAction: runtime === "pi"
|
|
199
|
+
? `Upgrade pi to ${pi_compaction_recovery_js_1.MINIMUM_PI_VERSION} or newer`
|
|
200
|
+
: `Upgrade ${runtime}, then retry.`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
194
203
|
if (/protocol (?:version )?(?:mismatch|unsupported|incompatible)|unsupported (?:rpc|protocol|schema)|schema (?:mismatch|incompatible)|requires (?:a )?newer version/i.test(reason)) {
|
|
195
204
|
return {
|
|
196
205
|
runtime, state: "incompatible", ...(executable ? { executable } : {}), reason,
|
|
@@ -238,7 +247,19 @@ async function probeNativeRuntimeReadiness(options) {
|
|
|
238
247
|
nextAction: runtimeInstallNextAction(options.runtime),
|
|
239
248
|
};
|
|
240
249
|
}
|
|
241
|
-
|
|
250
|
+
let version = executableVersion(executable, env, options.commandArgs);
|
|
251
|
+
if (options.runtime === "pi") {
|
|
252
|
+
try {
|
|
253
|
+
const probe = (0, node_child_process_1.spawnSync)(executable, [...options.commandArgs ?? [], "--version"], {
|
|
254
|
+
env, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024,
|
|
255
|
+
});
|
|
256
|
+
version = (0, pi_compaction_recovery_js_1.parsePiExecutableVersion)(String(probe.stdout || ""));
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
const classified = classifyRuntimePrerequisite("pi", error, executable);
|
|
260
|
+
return { ...classified, executable, ...(version ? { version } : {}) };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
242
263
|
try {
|
|
243
264
|
if (options.runtime === "pi") {
|
|
244
265
|
const { discoverPiModelCatalog } = await Promise.resolve().then(() => __importStar(require("./pi-model-catalog.cjs")));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { MINIMUM_PI_VERSION, parsePiExecutableVersion } from "./pi-compaction-recovery.mjs";
|
|
4
5
|
export function safeProviderId(value) {
|
|
5
6
|
const provider = typeof value === "string" ? value.trim() : "";
|
|
6
7
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(provider) ? provider : null;
|
|
@@ -138,6 +139,14 @@ export function classifyRuntimePrerequisite(runtime, error, executable) {
|
|
|
138
139
|
runtime, state: "unauthenticated", ...(executable ? { executable } : {}), reason,
|
|
139
140
|
nextAction: runtimeLoginNextAction(runtime),
|
|
140
141
|
};
|
|
142
|
+
if (/older than the minimum|not a supported version(?: line)?|version output must contain exactly one/i.test(reason)) {
|
|
143
|
+
return {
|
|
144
|
+
runtime, state: "incompatible", ...(executable ? { executable } : {}), reason,
|
|
145
|
+
nextAction: runtime === "pi"
|
|
146
|
+
? `Upgrade pi to ${MINIMUM_PI_VERSION} or newer`
|
|
147
|
+
: `Upgrade ${runtime}, then retry.`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
141
150
|
if (/protocol (?:version )?(?:mismatch|unsupported|incompatible)|unsupported (?:rpc|protocol|schema)|schema (?:mismatch|incompatible)|requires (?:a )?newer version/i.test(reason)) {
|
|
142
151
|
return {
|
|
143
152
|
runtime, state: "incompatible", ...(executable ? { executable } : {}), reason,
|
|
@@ -185,7 +194,19 @@ export async function probeNativeRuntimeReadiness(options) {
|
|
|
185
194
|
nextAction: runtimeInstallNextAction(options.runtime),
|
|
186
195
|
};
|
|
187
196
|
}
|
|
188
|
-
|
|
197
|
+
let version = executableVersion(executable, env, options.commandArgs);
|
|
198
|
+
if (options.runtime === "pi") {
|
|
199
|
+
try {
|
|
200
|
+
const probe = spawnSync(executable, [...options.commandArgs ?? [], "--version"], {
|
|
201
|
+
env, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024,
|
|
202
|
+
});
|
|
203
|
+
version = parsePiExecutableVersion(String(probe.stdout || ""));
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
const classified = classifyRuntimePrerequisite("pi", error, executable);
|
|
207
|
+
return { ...classified, executable, ...(version ? { version } : {}) };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
189
210
|
try {
|
|
190
211
|
if (options.runtime === "pi") {
|
|
191
212
|
const { discoverPiModelCatalog } = await import("./pi-model-catalog.mjs");
|