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
@@ -121,7 +121,7 @@ function memberNamesFromPayloads(payloads) {
121
121
  function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
122
122
  function agentConfigSignature(agent) {
123
123
  return JSON.stringify({
124
- agentId: agent.agentId, runtime: agent.runtime, model: agent.model, piDistribution: agent.piDistribution ?? null, effort: agent.effort ?? null,
124
+ agentId: agent.agentId, runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
125
125
  feishuAppId: agent.feishuAppId, feishuProfile: agent.feishuProfile,
126
126
  larkConfigDir: agent.larkConfigDir, feishuDomain: agent.feishuDomain,
127
127
  feishuAppSecret: agent.feishuAppSecret, workspaceDir: agent.workspaceDir, stateDir: agent.stateDir,
@@ -217,7 +217,6 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
217
217
  return (0, runtime_readiness_js_1.authFailureAppliesTo)({
218
218
  runtime: agent.runtime,
219
219
  model: agent.model,
220
- piDistribution: agent.piDistribution,
221
220
  }, persisted) ? persisted : null;
222
221
  }
223
222
  catch {
@@ -81,7 +81,7 @@ export function memberNamesFromPayloads(payloads) {
81
81
  function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
82
82
  function agentConfigSignature(agent) {
83
83
  return JSON.stringify({
84
- agentId: agent.agentId, runtime: agent.runtime, model: agent.model, piDistribution: agent.piDistribution ?? null, effort: agent.effort ?? null,
84
+ agentId: agent.agentId, runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
85
85
  feishuAppId: agent.feishuAppId, feishuProfile: agent.feishuProfile,
86
86
  larkConfigDir: agent.larkConfigDir, feishuDomain: agent.feishuDomain,
87
87
  feishuAppSecret: agent.feishuAppSecret, workspaceDir: agent.workspaceDir, stateDir: agent.stateDir,
@@ -177,7 +177,6 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
177
177
  return authFailureAppliesTo({
178
178
  runtime: agent.runtime,
179
179
  model: agent.model,
180
- piDistribution: agent.piDistribution,
181
180
  }, persisted) ? persisted : null;
182
181
  }
183
182
  catch {
@@ -66,10 +66,8 @@ const path = __importStar(require("node:path"));
66
66
  const root_layout_js_1 = require("./root-layout.cjs");
67
67
  const secure_metadata_js_1 = require("./secure-metadata.cjs");
68
68
  const runtime_model_catalog_js_1 = require("../runtime/runtime-model-catalog.cjs");
69
- const pi_provider_config_js_1 = require("../runtime/pi-provider-config.cjs");
70
69
  const user_runtime_js_1 = require("../runtime/user-runtime.cjs");
71
70
  const process_inspect_cjs_1 = __importDefault(require("./process-inspect.cjs"));
72
- const pi_profile_migration_js_1 = require("../runtime/pi-profile-migration.cjs");
73
71
  var user_runtime_js_2 = require("../runtime/user-runtime.cjs");
74
72
  Object.defineProperty(exports, "RUNTIME_OPTIONS", { enumerable: true, get: function () { return user_runtime_js_2.RUNTIME_OPTIONS; } });
75
73
  Object.defineProperty(exports, "fromUserRuntime", { enumerable: true, get: function () { return user_runtime_js_2.fromUserRuntime; } });
@@ -81,7 +79,8 @@ Object.defineProperty(exports, "toUserRuntime", { enumerable: true, get: functio
81
79
  const TOP_FIELDS_V3 = new Set(["version", "serverId", "activeAgent", "agents"]);
82
80
  const TOP_FIELDS_V4 = new Set(["version", "serverId", "mentionPolicy", "activeAgent", "agents"]);
83
81
  const AGENT_FIELDS_V3 = new Set(["runtime", "model", "effort", "noMentionChats", "createdAt"]);
84
- const AGENT_FIELDS_V4 = new Set(["runtime", "model", "piDistribution", "effort", "mentionPolicy", "chatMentionPolicies", "createdAt"]);
82
+ const AGENT_FIELDS_V4 = new Set(["runtime", "model", "effort", "mentionPolicy", "chatMentionPolicies", "createdAt"]);
83
+ const LEGACY_AGENT_FIELDS = new Set(["piDistribution", "runtimeOption"]);
85
84
  const APP_ID = /^cli_[A-Za-z0-9]+$/;
86
85
  const CHAT_ID = /^oc_[A-Za-z0-9_-]+$/;
87
86
  const PI_EFFORTS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
@@ -159,31 +158,51 @@ function runtimeSupportsEffort(runtime, effort) {
159
158
  : runtime === "codex" ? CODEX_EFFORTS.has(effort)
160
159
  : runtime === "claude" ? CLAUDE_EFFORTS.has(effort) : false;
161
160
  }
161
+ function takeLegacyAgentFields(agent) {
162
+ const cleaned = {};
163
+ for (const [field, value] of Object.entries(agent)) {
164
+ if (!LEGACY_AGENT_FIELDS.has(field))
165
+ cleaned[field] = value;
166
+ }
167
+ return cleaned;
168
+ }
169
+ function isLegacyBuiltinAgentRecord(agent) {
170
+ if (!isPlainObject(agent))
171
+ return false;
172
+ return agent.piDistribution === "builtin" || agent.runtime === "builtin-pi" || agent.runtimeOption === "builtin-pi";
173
+ }
174
+ function canonicalizeLegacyStoredAgent(key, agent) {
175
+ const cleaned = takeLegacyAgentFields(agent);
176
+ if (cleaned.runtime === "builtin-pi" || isLegacyBuiltinAgentRecord(agent))
177
+ cleaned.runtime = "pi";
178
+ if (typeof cleaned.runtime !== "string" || !cleaned.runtime)
179
+ throw new Error(`Agent ${key}.runtime 必须是非空字符串`);
180
+ return cleaned;
181
+ }
162
182
  function validateStoredAgent(key, agent, version) {
163
183
  if (!APP_ID.test(key))
164
184
  throw new Error(`Agent key 必须是安全的飞书 App ID(cli_ + ASCII 字母数字):${key}`);
165
185
  if (!isPlainObject(agent))
166
186
  throw new Error(`Agent ${key} 必须是普通 object`);
167
- assertAllowedFields(agent, version === 3 ? AGENT_FIELDS_V3 : AGENT_FIELDS_V4, `Agent ${key}`);
168
- if (typeof agent.runtime !== "string" || !agent.runtime)
187
+ const canonical = canonicalizeLegacyStoredAgent(key, agent);
188
+ assertAllowedFields(canonical, version === 3 ? AGENT_FIELDS_V3 : AGENT_FIELDS_V4, `Agent ${key}`);
189
+ if (typeof canonical.runtime !== "string" || !canonical.runtime)
169
190
  throw new Error(`Agent ${key}.runtime 必须是非空字符串`);
170
- if (!(0, user_runtime_js_1.isAdapterRuntime)(agent.runtime))
171
- throw new Error(`Agent ${key}.runtime 未知:${agent.runtime}`);
172
- if (typeof agent.model !== "string" || !agent.model)
191
+ if (!(0, user_runtime_js_1.isAdapterRuntime)(canonical.runtime))
192
+ throw new Error(`Agent ${key}.runtime 未知:${canonical.runtime}`);
193
+ if (typeof canonical.model !== "string" || !canonical.model)
173
194
  throw new Error(`Agent ${key}.model 必须是非空字符串`);
174
- assertModel(agent.runtime, agent.model);
175
- if (Object.hasOwn(agent, "piDistribution")) {
176
- if (agent.runtime !== "pi" || (agent.piDistribution !== "external" && agent.piDistribution !== "builtin")) {
177
- throw new Error(`Agent ${key}.piDistribution 只允许 Pi runtime 使用 external/builtin`);
178
- }
179
- }
195
+ assertModel(canonical.runtime, canonical.model);
196
+ Object.assign(agent, canonical);
197
+ for (const field of LEGACY_AGENT_FIELDS)
198
+ delete agent[field];
180
199
  if (Object.hasOwn(agent, "effort") && (typeof agent.effort !== "string" || !agent.effort))
181
200
  throw new Error(`Agent ${key}.effort 必须是非空字符串`);
182
201
  if (agent.model === "default" && Object.hasOwn(agent, "effort"))
183
202
  throw new Error(`Agent ${key}.model=default 时不能保存 effort`);
184
203
  if (typeof agent.effort === "string") {
185
- if (!runtimeSupportsEffort(agent.runtime, agent.effort))
186
- throw new Error(`Agent ${key}.effort=${agent.effort} 不在 ${agent.runtime} 安全档位中`);
204
+ if (!runtimeSupportsEffort(canonical.runtime, agent.effort))
205
+ throw new Error(`Agent ${key}.effort=${agent.effort} 不在 ${canonical.runtime} 安全档位中`);
187
206
  }
188
207
  if (version === 3 && Object.hasOwn(agent, "noMentionChats")) {
189
208
  if (!Array.isArray(agent.noMentionChats) || agent.noMentionChats.some((chat) => typeof chat !== "string" || !CHAT_ID.test(chat))) {
@@ -218,7 +237,6 @@ function hydratedStoredAgent(key, agent, version) {
218
237
  return {
219
238
  runtime: agent.runtime,
220
239
  model: agent.model,
221
- ...(agent.piDistribution === "external" || agent.piDistribution === "builtin" ? { piDistribution: agent.piDistribution } : {}),
222
240
  ...(typeof agent.effort === "string" ? { effort: agent.effort } : {}),
223
241
  ...(version === 4 && (agent.mentionPolicy === "require" || agent.mentionPolicy === "free") ? { mentionPolicy: agent.mentionPolicy } : {}),
224
242
  ...(Object.keys(chatMentionPolicies).length ? { chatMentionPolicies, noMentionChats: Object.entries(chatMentionPolicies).filter(([, policy]) => policy === "free").map(([chatId]) => chatId) } : {}),
@@ -230,7 +248,6 @@ function hydrateAgent(key, agent, configDir) {
230
248
  return {
231
249
  name: key, agentId: key, feishuAppId: key, feishuProfile: key,
232
250
  runtime: agent.runtime, model: agent.model,
233
- ...(agent.piDistribution ? { piDistribution: agent.piDistribution } : {}),
234
251
  workspaceDir: layout.workspaceDir(key), stateDir: layout.agentStateDir(key),
235
252
  larkConfigDir: path.join(layout.agentStateDir(key), "lark-cli-config"),
236
253
  ...(agent.effort ? { effort: agent.effort } : {}),
@@ -351,9 +368,137 @@ function readConfigFile(file, root = path.dirname(file)) {
351
368
  throw error;
352
369
  }
353
370
  }
371
+ function collectLegacyBuiltinAgentIds(raw) {
372
+ if (!isPlainObject(raw) || !isPlainObject(raw.agents))
373
+ return [];
374
+ return Object.entries(raw.agents).filter(([, agent]) => isLegacyBuiltinAgentRecord(agent)).map(([agentId]) => agentId);
375
+ }
376
+ function ownedPiProviderDirectory(configDir, agentId) {
377
+ if (!APP_ID.test(agentId))
378
+ throw new Error(`Pi Agent ID 格式无效:${agentId}`);
379
+ return path.join(path.resolve(configDir), "providers", "pi", agentId);
380
+ }
381
+ function userPiHome(env) {
382
+ const home = typeof env.HOME === "string" && env.HOME.trim() ? env.HOME : os.homedir();
383
+ return path.resolve(home, ".pi");
384
+ }
385
+ function isInsideOrEqual(candidate, root) {
386
+ const relative = path.relative(root, candidate);
387
+ return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
388
+ }
389
+ function pathExists(target) {
390
+ try {
391
+ fs.lstatSync(target);
392
+ return true;
393
+ }
394
+ catch (error) {
395
+ if (error.code === "ENOENT")
396
+ return false;
397
+ throw error;
398
+ }
399
+ }
400
+ function shouldSkipOwnedPiDeletion(configDir, expected, env) {
401
+ let userPi;
402
+ try {
403
+ userPi = fs.realpathSync(userPiHome(env));
404
+ }
405
+ catch {
406
+ userPi = path.resolve(userPiHome(env));
407
+ }
408
+ let configRoot;
409
+ try {
410
+ configRoot = fs.realpathSync(path.resolve(configDir));
411
+ }
412
+ catch {
413
+ configRoot = path.resolve(configDir);
414
+ }
415
+ let resolvedExpected;
416
+ try {
417
+ resolvedExpected = fs.realpathSync(path.resolve(expected));
418
+ }
419
+ catch {
420
+ resolvedExpected = path.resolve(expected);
421
+ }
422
+ if (isInsideOrEqual(configRoot, userPi))
423
+ return "config root is inside the user ~/.pi directory";
424
+ if (isInsideOrEqual(resolvedExpected, userPi))
425
+ return "owned Pi directory is inside the user ~/.pi directory";
426
+ return null;
427
+ }
428
+ function deleteOwnedPiProviderDirectory(configDir, agentId, env = process.env) {
429
+ const expected = ownedPiProviderDirectory(configDir, agentId);
430
+ const skip = shouldSkipOwnedPiDeletion(configDir, expected, env);
431
+ if (skip) {
432
+ process.stderr.write(`[larkin] skipped leftover builtin Pi directory for ${agentId}: ${skip}\n`);
433
+ return;
434
+ }
435
+ const parent = path.dirname(expected);
436
+ if (!pathExists(parent) || !pathExists(expected))
437
+ return;
438
+ assertNoSymlinkAncestors(expected);
439
+ const rootReal = fs.realpathSync(path.resolve(configDir));
440
+ const parentReal = fs.realpathSync(parent);
441
+ const relative = path.relative(rootReal, parentReal);
442
+ if (relative.startsWith(`..${path.sep}`) || relative === ".." || path.isAbsolute(relative)) {
443
+ throw new Error("owned Pi provider directory escaped the config root");
444
+ }
445
+ if (path.basename(parent) !== "pi" || path.basename(path.dirname(parent)) !== "providers") {
446
+ throw new Error("owned Pi provider directory is not providers/pi/<agentId>");
447
+ }
448
+ const stat = fs.lstatSync(expected);
449
+ if (stat.isSymbolicLink())
450
+ throw new Error("owned Pi provider directory is a symlink");
451
+ if (!stat.isDirectory())
452
+ throw new Error("owned Pi provider directory must be a directory");
453
+ fs.rmSync(expected, { recursive: true, force: false });
454
+ }
455
+ function rewriteLegacyBuiltinAgents(raw) {
456
+ if (!isPlainObject(raw) || !isPlainObject(raw.agents))
457
+ return raw;
458
+ const legacyIds = new Set(collectLegacyBuiltinAgentIds(raw));
459
+ if (legacyIds.size === 0)
460
+ return raw;
461
+ const agents = {};
462
+ for (const [agentId, agent] of Object.entries(raw.agents)) {
463
+ if (!legacyIds.has(agentId) || !isPlainObject(agent)) {
464
+ agents[agentId] = agent;
465
+ continue;
466
+ }
467
+ const next = takeLegacyAgentFields(agent);
468
+ if (next.runtime === "builtin-pi" || isLegacyBuiltinAgentRecord(agent))
469
+ next.runtime = "pi";
470
+ agents[agentId] = next;
471
+ }
472
+ return { ...raw, agents };
473
+ }
474
+ function persistLegacyBuiltinMigration(layout, opts, env) {
475
+ const current = readConfigFile(layout.configFile, layout.root);
476
+ const migratedIds = collectLegacyBuiltinAgentIds(current.raw);
477
+ if (migratedIds.length === 0)
478
+ return current;
479
+ const rewritten = rewriteLegacyBuiltinAgents(current.raw);
480
+ const config = normalizeConfig(structuredClone(rewritten), layout.root, opts);
481
+ const bytes = atomicWriteConfig(layout.configFile, rewritten);
482
+ for (const agentId of migratedIds) {
483
+ const agent = config.agents[agentId];
484
+ const model = typeof agent?.model === "string" ? agent.model : "unknown";
485
+ process.stderr.write(`[larkin] migrated Agent ${agentId} from builtin-pi to pi (model=${model})\n`);
486
+ try {
487
+ deleteOwnedPiProviderDirectory(layout.root, agentId, env);
488
+ }
489
+ catch (error) {
490
+ const reason = error instanceof Error ? error.message : String(error);
491
+ process.stderr.write(`[larkin] failed to delete leftover builtin Pi directory for ${agentId}: ${reason}\n`);
492
+ }
493
+ }
494
+ return { raw: rewritten, bytes };
495
+ }
354
496
  function loadConfig(env = process.env, opts = {}) {
355
497
  const layout = root_layout_js_1.TargetRootLayout.fromConfigDir(resolveConfigDir(env));
356
- const { raw, bytes } = readConfigFile(layout.configFile, layout.root);
498
+ const initial = readConfigFile(layout.configFile, layout.root);
499
+ const { raw, bytes } = collectLegacyBuiltinAgentIds(initial.raw).length
500
+ ? withConfigLock(layout, () => persistLegacyBuiltinMigration(layout, opts, env))
501
+ : initial;
357
502
  return { configDir: layout.configDir, file: layout.configFile, revision: revision(bytes), config: normalizeConfig(raw, layout.root, opts) };
358
503
  }
359
504
  function selectAgent(config, env = process.env) {
@@ -374,8 +519,6 @@ function toStored(config) {
374
519
  const out = { version: 4, serverId: config.serverId, mentionPolicy: config.mentionPolicy, activeAgent: config.activeAgent, agents: {} };
375
520
  for (const [key, agent] of Object.entries(config.agents || {})) {
376
521
  const stored = { runtime: agent.runtime, model: agent.model };
377
- if (agent.piDistribution)
378
- stored.piDistribution = agent.piDistribution;
379
522
  if (typeof agent.effort === "string" && agent.effort)
380
523
  stored.effort = agent.effort;
381
524
  if (agent.mentionPolicy)
@@ -417,7 +560,8 @@ function agentSignature(config, agentId) {
417
560
  throw new Error(`Agent 不存在:${agentId}`);
418
561
  const chats = Object.fromEntries(Object.entries(agent.chatMentionPolicies || {}).sort(([left], [right]) => left.localeCompare(right)));
419
562
  return revision(JSON.stringify({
420
- runtime: agent.runtime, model: agent.model, piDistribution: agent.piDistribution ?? null, effort: agent.effort ?? null,
563
+ schema: 2,
564
+ runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
421
565
  globalMentionPolicy: config.mentionPolicy, agentMentionPolicy: agent.mentionPolicy ?? null, chatMentionPolicies: chats,
422
566
  }));
423
567
  }
@@ -454,12 +598,6 @@ function writeApplyFile(configDir, value) {
454
598
  atomicWriteConfig(applyStateFile(configDir), value);
455
599
  }
456
600
  function rollbackJournalFile(root) { return path.join(root, CONFIG_ROLLBACK_JOURNAL_FILE); }
457
- function migrationJournalFile(root) { return path.join(root, ".pi-profile-migration-journal.json"); }
458
- function assertMigrationTarget(root, agentId, migration) {
459
- if (migration.agentId !== agentId || path.resolve(migration.targetDir) !== path.join(path.resolve(root), "providers", "pi", agentId)) {
460
- throw new Error("Pi profile migration target is invalid");
461
- }
462
- }
463
601
  function restoreTargetApplyState(root, targetAgentId, beforeRevision, before) {
464
602
  const current = readApplyFile(root);
465
603
  const agents = { ...current.agents };
@@ -498,15 +636,9 @@ function recoverRollbackJournal(root) {
498
636
  const beforeRaw = JSON.parse(beforeBytes.toString("utf8"));
499
637
  normalizeConfig(beforeRaw, root);
500
638
  validateApplyState(journal.beforeApplyState);
501
- if (journal.migration !== undefined) {
502
- (0, pi_profile_migration_js_1.validatePiProfileMigrationState)(journal.migration);
503
- assertMigrationTarget(root, journal.targetAgentId, journal.migration);
504
- }
505
639
  const current = readConfigFile(path.join(root, "config.json"), root);
506
640
  const currentRevision = revision(current.bytes);
507
641
  if (currentRevision === journal.expectedAfterRevision) {
508
- if (journal.migration)
509
- (0, pi_profile_migration_js_1.rollbackPiProfileMigration)(journal.migration);
510
642
  atomicWriteBytes(path.join(root, "config.json"), beforeBytes);
511
643
  restoreTargetApplyState(root, journal.targetAgentId, journal.beforeRevision, journal.beforeApplyState);
512
644
  }
@@ -521,47 +653,6 @@ function recoverRollbackJournal(root) {
521
653
  fs.unlinkSync(file);
522
654
  (0, secure_metadata_js_1.fsyncDirectoryOf)(file);
523
655
  }
524
- function recoverMigrationJournal(root) {
525
- const file = migrationJournalFile(root);
526
- const bytes = readPrivateFile(file, root, PROFILE_MIGRATION_ROLLBACK_LIMIT_BYTES, "Pi profile migration journal");
527
- if (bytes === null)
528
- return;
529
- let journal;
530
- try {
531
- journal = JSON.parse(bytes.toString("utf8"));
532
- }
533
- catch {
534
- throw new Error("Pi profile migration journal is invalid");
535
- }
536
- if (journal.version !== 1 || journal.phase !== "forward" || typeof journal.targetAgentId !== "string"
537
- || typeof journal.beforeRevision !== "string" || typeof journal.expectedAfterRevision !== "string" || !journal.migration) {
538
- throw new Error("Pi profile migration journal is invalid");
539
- }
540
- (0, pi_profile_migration_js_1.validatePiProfileMigrationState)(journal.migration);
541
- assertMigrationTarget(root, journal.targetAgentId, journal.migration);
542
- const current = readConfigFile(path.join(root, "config.json"), root);
543
- const currentRevision = revision(current.bytes);
544
- if (currentRevision === journal.beforeRevision) {
545
- // Forward import did not publish config. The migration helper restores only
546
- // files still matching its expected post-import hashes.
547
- try {
548
- (0, pi_profile_migration_js_1.rollbackPiProfileMigration)(journal.migration);
549
- }
550
- catch (error) {
551
- throw new Error(`Pi profile migration recovery refused: ${error instanceof Error ? error.message : String(error)}`);
552
- }
553
- }
554
- else if (currentRevision === journal.expectedAfterRevision) {
555
- // Config won the race; verify the provider target before finalizing.
556
- const migration = journal.migration;
557
- (0, pi_profile_migration_js_1.assertPiProfileMigrationAfterState)(migration);
558
- }
559
- else
560
- throw new Error("Pi profile migration journal conflicts with the current configuration");
561
- (0, pi_profile_migration_js_1.releasePiProfileMigrationLock)(journal.migration);
562
- fs.unlinkSync(file);
563
- (0, secure_metadata_js_1.fsyncDirectoryOf)(file);
564
- }
565
656
  function configApplyState(env, config) {
566
657
  let state;
567
658
  try {
@@ -638,38 +729,16 @@ function applyMutation(config, mutation) {
638
729
  agent.noMentionChats = Object.entries(policies).filter(([, value]) => value === "free").map(([chatId]) => chatId);
639
730
  return { scope: "chat", agentId: mutation.agentId, runtimeChange: false, affectedAgentIds: [mutation.agentId] };
640
731
  }
641
- if (mutation.kind === "set-agent-pi-distribution") {
642
- if (agent.runtime !== "pi")
643
- throw new Error(`Agent ${mutation.agentId} 不是 Pi runtime`);
644
- if (mutation.distribution !== "builtin" && mutation.distribution !== "external")
645
- throw new Error("Pi distribution 只允许 builtin/external");
646
- agent.piDistribution = mutation.distribution;
647
- }
648
- else if (mutation.kind === "set-agent-runtime") {
732
+ if (mutation.kind === "set-agent-runtime") {
649
733
  if (!(0, user_runtime_js_1.isUserRuntime)(mutation.runtime))
650
734
  throw new Error(`未知 runtime:${mutation.runtime}`);
651
735
  const stored = (0, user_runtime_js_1.fromUserRuntime)(mutation.runtime);
652
- if (stored.piDistribution === "builtin") {
653
- try {
654
- (0, pi_provider_config_js_1.assertBuiltinPiAgentDirectory)((0, pi_provider_config_js_1.piAgentDirectory)(config.configDir, mutation.agentId));
655
- }
656
- catch (error) {
657
- throw new Error(`无法切换到 builtin-pi:${error instanceof Error ? error.message : String(error)}。请使用 Dashboard Provider Credentials 或 larkin pi-auth login。`);
658
- }
659
- }
660
736
  assertModel(stored.runtime, mutation.model || "default");
661
737
  agent.runtime = stored.runtime;
662
738
  agent.model = mutation.model || "default";
663
- if (stored.piDistribution)
664
- agent.piDistribution = stored.piDistribution;
665
- else
666
- delete agent.piDistribution;
667
739
  delete agent.effort;
668
740
  }
669
741
  else if (mutation.kind === "set-agent-model") {
670
- if (mutation.requireBuiltinPi && (agent.runtime !== "pi" || agent.piDistribution !== "builtin")) {
671
- throw new Error(`Agent ${mutation.agentId} 不是内置 Pi`);
672
- }
673
742
  if (!mutation.model.trim())
674
743
  throw new Error("model 不能为空");
675
744
  assertModel(agent.runtime, mutation.model);
@@ -875,7 +944,6 @@ function acquireConfigLock(layout) {
875
944
  function withConfigLock(layout, action) {
876
945
  const held = acquireConfigLock(layout);
877
946
  try {
878
- recoverMigrationJournal(layout.root);
879
947
  recoverRollbackJournal(layout.root);
880
948
  return action();
881
949
  }
@@ -890,7 +958,6 @@ async function withConfigMutationLockAsync(env, action) {
890
958
  const layout = root_layout_js_1.TargetRootLayout.fromConfigDir(resolveConfigDir(env));
891
959
  const held = acquireConfigLock(layout);
892
960
  try {
893
- recoverMigrationJournal(layout.root);
894
961
  recoverRollbackJournal(layout.root);
895
962
  return await action();
896
963
  }
@@ -990,10 +1057,6 @@ function readConfigSnapshot(file, root) {
990
1057
  if (JSON.stringify(beforeRaw) !== JSON.stringify(parsed.beforeConfig))
991
1058
  throw new Error("config snapshot before config does not match bytes");
992
1059
  validateApplyState(parsed.beforeApplyState);
993
- if (parsed.migration !== undefined) {
994
- (0, pi_profile_migration_js_1.validatePiProfileMigrationState)(parsed.migration);
995
- assertMigrationTarget(root, parsed.targetAgentId, parsed.migration);
996
- }
997
1060
  return parsed;
998
1061
  }
999
1062
  function atomicWriteBytes(file, bytes, limit = CONFIG_LIMIT_BYTES) {
@@ -1037,12 +1100,6 @@ function mutateConfig(env, mutation, authority, options = {}) {
1037
1100
  throw new Error(`没找到配置 ${layout.configFile},先跑 larkin setup`);
1038
1101
  const config = normalizeConfig(current.raw, layout.root);
1039
1102
  const priorSignatures = Object.fromEntries(Object.keys(config.agents).map((agentId) => [agentId, agentSignature(config, agentId)]));
1040
- if (options.importExternalProfile && (mutation.kind !== "set-agent-pi-distribution" || mutation.distribution !== "builtin")) {
1041
- throw new Error("external Pi profile import is only valid for builtin Pi distribution");
1042
- }
1043
- const migrationPlan = options.importExternalProfile && mutation.kind === "set-agent-pi-distribution"
1044
- ? (0, pi_profile_migration_js_1.preparePiProfileMigration)(env, layout.root, mutation.agentId, "builtin")
1045
- : undefined;
1046
1103
  const changed = applyMutation(config, mutation);
1047
1104
  const stored = toStored(config);
1048
1105
  normalizeConfig(stored, layout.root);
@@ -1063,16 +1120,8 @@ function mutateConfig(env, mutation, authority, options = {}) {
1063
1120
  version: 2, targetAgentId: changed.agentId, beforeRevision: revision(current.bytes), afterRevision: nextRevision,
1064
1121
  afterSignature: agentSignature(config, changed.agentId), beforeConfig: current.raw,
1065
1122
  beforeConfigBytes: current.bytes.toString("base64"), beforeApplyState,
1066
- ...(migrationPlan ? { migration: migrationPlan.state } : {}),
1067
1123
  });
1068
1124
  }
1069
- if (migrationPlan) {
1070
- atomicWriteConfig(migrationJournalFile(layout.root), {
1071
- version: 1, phase: "forward", targetAgentId: changed.agentId, beforeRevision: revision(current.bytes),
1072
- expectedAfterRevision: nextRevision, migration: migrationPlan.state,
1073
- }, PROFILE_MIGRATION_ROLLBACK_LIMIT_BYTES);
1074
- (0, pi_profile_migration_js_1.applyPiProfileMigration)(migrationPlan);
1075
- }
1076
1125
  atomicWriteConfig(layout.configFile, stored);
1077
1126
  let fullyApplied = changed.affectedAgentIds.length === 0;
1078
1127
  try {
@@ -1096,17 +1145,6 @@ function mutateConfig(env, mutation, authority, options = {}) {
1096
1145
  catch {
1097
1146
  fullyApplied = false; /* Config persistence remains authoritative. */
1098
1147
  }
1099
- if (migrationPlan) {
1100
- (0, pi_profile_migration_js_1.releasePiProfileMigrationLock)(migrationPlan.state);
1101
- try {
1102
- fs.unlinkSync(migrationJournalFile(layout.root));
1103
- (0, secure_metadata_js_1.fsyncDirectoryOf)(migrationJournalFile(layout.root));
1104
- }
1105
- catch (error) {
1106
- if (error.code !== "ENOENT")
1107
- throw error;
1108
- }
1109
- }
1110
1148
  return {
1111
1149
  revision: nextRevision, previousRevision: revision(current.bytes), changedScope: changed.scope,
1112
1150
  ...(changed.agentId ? { agentId: changed.agentId } : {}), persisted: true,
@@ -1130,11 +1168,8 @@ function rollbackConfig(env, snapshotFile) {
1130
1168
  const journal = {
1131
1169
  version: 1, targetAgentId: snapshot.targetAgentId, expectedAfterRevision: snapshot.afterRevision,
1132
1170
  beforeRevision: snapshot.beforeRevision, beforeConfigBytes: snapshot.beforeConfigBytes, beforeApplyState: snapshot.beforeApplyState,
1133
- ...(snapshot.migration ? { migration: snapshot.migration } : {}),
1134
1171
  };
1135
1172
  atomicWriteConfig(rollbackJournalFile(layout.root), journal, PROFILE_MIGRATION_ROLLBACK_LIMIT_BYTES);
1136
- if (snapshot.migration)
1137
- (0, pi_profile_migration_js_1.rollbackPiProfileMigration)(snapshot.migration);
1138
1173
  const bytes = atomicWriteBytes(layout.configFile, beforeBytes);
1139
1174
  restoreTargetApplyState(layout.root, snapshot.targetAgentId, snapshot.beforeRevision, snapshot.beforeApplyState);
1140
1175
  fs.unlinkSync(rollbackJournalFile(layout.root));
@@ -1156,7 +1191,7 @@ function commitSetupConfig(env, expectedRevision, nextStored) {
1156
1191
  function safeConfigView(config, onlyAgentId, chatId, applyState) {
1157
1192
  const entries = onlyAgentId ? [[onlyAgentId, config.agents[onlyAgentId]]] : Object.entries(config.agents);
1158
1193
  const agents = entries.filter((entry) => Boolean(entry[1])).map(([agentId, agent]) => ({
1159
- agentId, runtime: agent.runtime, runtimeOption: (0, user_runtime_js_1.toUserRuntime)(agent.runtime, agent.piDistribution), model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
1194
+ agentId, runtime: agent.runtime, runtimeOption: (0, user_runtime_js_1.toUserRuntime)(agent.runtime), model: agent.model, effort: agent.effort ?? null,
1160
1195
  mention: {
1161
1196
  override: agent.mentionPolicy ?? "inherit",
1162
1197
  effective: agent.mentionPolicy ?? config.mentionPolicy,