@ycodium-ai/agent-claude 0.2.3681 → 0.2.3766

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/dist/index.js CHANGED
@@ -1,5 +1,12 @@
1
1
  // src/index.ts
2
+ import * as NodePath20 from "node:path";
2
3
  import { definePlugin } from "@ycodium-ai/plugin-api";
4
+ import {
5
+ EXECUTOR_EFFECTIVE_PAYLOAD_SCHEMA,
6
+ executorEnvironmentValue,
7
+ executorHomeEffectiveValue,
8
+ readExecutorEffectiveRequest
9
+ } from "@ycodium-ai/plugin-api/executor";
3
10
 
4
11
  // src/card.ts
5
12
  var CLAUDE_DRIVER_KIND = "claudeAgent";
@@ -13,6 +20,8 @@ var CLAUDE_CARD = {
13
20
  launchArgs: "",
14
21
  // Claude 的隔离走 CLAUDE_CONFIG_DIR,不注入 HOME(钥匙串查找跟着 HOME)。
15
22
  homeEnvVar: "",
23
+ // 设置表单里留空的格子此刻会用什么,见 index.ts 的同名命令。
24
+ effectiveCommand: "effectiveConfig",
16
25
  // 写的是凭据目录(`credentialsPath`),不是共用工作目录:写错字段等于把两个账号的会话历史
17
26
  // 也分开,同一 thread 里就换不了账号了。
18
27
  accountDirectory: {
@@ -279,8 +288,8 @@ var waitForJsonlHistory = (input) => {
279
288
  signal.throwIfAborted();
280
289
  if (error !== void 0) throw error;
281
290
  if (!dirty)
282
- await new Promise((resolve5) => {
283
- wake = resolve5;
291
+ await new Promise((resolve6) => {
292
+ wake = resolve6;
284
293
  });
285
294
  signal.throwIfAborted();
286
295
  dirty = false;
@@ -1349,8 +1358,8 @@ async function openJsonlHistory(path, layout, signal) {
1349
1358
  readSignal.throwIfAborted();
1350
1359
  if (failure !== void 0) throw failure;
1351
1360
  if (!dirty)
1352
- await new Promise((resolve5) => {
1353
- wake = resolve5;
1361
+ await new Promise((resolve6) => {
1362
+ wake = resolve6;
1354
1363
  });
1355
1364
  readSignal.throwIfAborted();
1356
1365
  dirty = false;
@@ -23252,9 +23261,9 @@ var PromptQueue = class {
23252
23261
  const queued = this.#queued.shift();
23253
23262
  if (queued !== void 0) return Promise.resolve(queued);
23254
23263
  if (this.#closed) return Promise.resolve(void 0);
23255
- return new Promise((resolve5) => {
23264
+ return new Promise((resolve6) => {
23256
23265
  this.#waiters.push((result) => {
23257
- resolve5(result.done ? void 0 : result.value);
23266
+ resolve6(result.done ? void 0 : result.value);
23258
23267
  });
23259
23268
  });
23260
23269
  }
@@ -24352,12 +24361,12 @@ function makeClaudeProcessSpawner(host, stderr, config = {}) {
24352
24361
  // src/sdkRead.ts
24353
24362
  var silentPrompt = (signal) => ({
24354
24363
  [Symbol.asyncIterator]: () => ({
24355
- next: () => new Promise((resolve5) => {
24364
+ next: () => new Promise((resolve6) => {
24356
24365
  if (signal.aborted) {
24357
- resolve5({ done: true, value: void 0 });
24366
+ resolve6({ done: true, value: void 0 });
24358
24367
  return;
24359
24368
  }
24360
- signal.addEventListener("abort", () => resolve5({ done: true, value: void 0 }), {
24369
+ signal.addEventListener("abort", () => resolve6({ done: true, value: void 0 }), {
24361
24370
  once: true
24362
24371
  });
24363
24372
  }),
@@ -25808,7 +25817,7 @@ var makeSessionControls = (deps) => {
25808
25817
  context.query.interrupt().then(() => settled.promise).then(() => {
25809
25818
  acknowledged = true;
25810
25819
  }),
25811
- new Promise((resolve5) => setTimeout(resolve5, CLAUDE_INTERRUPT_GRACE_MS))
25820
+ new Promise((resolve6) => setTimeout(resolve6, CLAUDE_INTERRUPT_GRACE_MS))
25812
25821
  ]);
25813
25822
  } catch (cause) {
25814
25823
  throw toRequestError(threadId, "turn/interrupt", cause);
@@ -25940,8 +25949,8 @@ var makeSessionLifecycle = (deps) => {
25940
25949
  const claim = context.turnState?.resultClaim;
25941
25950
  if (claim === void 0) return;
25942
25951
  let timer;
25943
- const timeout = new Promise((resolve5) => {
25944
- timer = setTimeout(() => resolve5("timeout"), RESULT_CLAIM_WAIT_MS);
25952
+ const timeout = new Promise((resolve6) => {
25953
+ timer = setTimeout(() => resolve6("timeout"), RESULT_CLAIM_WAIT_MS);
25945
25954
  });
25946
25955
  try {
25947
25956
  const outcome = await Promise.race([claim.then(() => "settled"), timeout]);
@@ -26020,7 +26029,7 @@ var makeSessionLifecycle = (deps) => {
26020
26029
  if (streamFiber) {
26021
26030
  await Promise.race([
26022
26031
  streamFiber.catch(() => void 0),
26023
- new Promise((resolve5) => setTimeout(resolve5, 1e3))
26032
+ new Promise((resolve6) => setTimeout(resolve6, 1e3))
26024
26033
  ]);
26025
26034
  }
26026
26035
  const updatedAt = nowIso();
@@ -26316,8 +26325,8 @@ var makeStartSession = (deps) => {
26316
26325
  try {
26317
26326
  return await Promise.race([
26318
26327
  contextDeferred.promise,
26319
- new Promise((resolve5) => {
26320
- timer = setTimeout(() => resolve5(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS);
26328
+ new Promise((resolve6) => {
26329
+ timer = setTimeout(() => resolve6(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS);
26321
26330
  })
26322
26331
  ]);
26323
26332
  } catch {
@@ -26916,10 +26925,35 @@ var claudeSlashCommandsFromSdk = (commands) => Array.isArray(commands) ? command
26916
26925
  ];
26917
26926
  }) : [];
26918
26927
 
26928
+ // src/adapter/reportedModel.ts
26929
+ var makeReportedModelEmitter = (emitter) => {
26930
+ const { eventHeader, offerRuntimeEvent } = emitter;
26931
+ return (context, model) => {
26932
+ const turn = context.turnState;
26933
+ if (turn === void 0 || typeof model !== "string") return;
26934
+ const reported = model.replace(/\[[^\]]*\]\s*$/, "").trim();
26935
+ if (reported.length === 0) return;
26936
+ const key = `${turn.turnId}|${reported}`;
26937
+ if ((context.reportedModelKeys ??= /* @__PURE__ */ new Set()).has(key)) return;
26938
+ context.reportedModelKeys.add(key);
26939
+ offerRuntimeEvent(context, {
26940
+ type: "thread.metadata.updated",
26941
+ ...eventHeader(context, {
26942
+ eventId: `${turn.turnId}:model:${reported}`,
26943
+ createdAt: turn.startedAt
26944
+ }),
26945
+ turnId: asCanonicalTurnId(turn.turnId),
26946
+ payload: { model: reported, modelSource: "reported" },
26947
+ providerRefs: {}
26948
+ });
26949
+ };
26950
+ };
26951
+
26919
26952
  // src/adapter/systemMessages.ts
26920
26953
  var makeSystemMessageHandler = (deps) => {
26921
26954
  const { emitter, host, emitRuntimeError, emitRuntimeWarning, emitThreadTokenUsage } = deps;
26922
26955
  const { randomUUIDv4, makeEventStamp, eventHeader, offerRuntimeEvent } = emitter;
26956
+ const emitReportedModel = makeReportedModelEmitter(emitter);
26923
26957
  const emitWorkflowMemberProgress = async (context, base, message) => {
26924
26958
  const progress = parseWorkflowProgress(
26925
26959
  message.workflow_progress
@@ -27030,6 +27064,7 @@ var makeSystemMessageHandler = (deps) => {
27030
27064
  config: message
27031
27065
  }
27032
27066
  });
27067
+ emitReportedModel(context, message.model);
27033
27068
  const threadId = context.session.threadId;
27034
27069
  const names = claudeSlashCommandsFromNames(message.slash_commands);
27035
27070
  if (names.length > 0) host.reportSessionInventory?.({ threadId, slashCommands: names });
@@ -27675,6 +27710,7 @@ var makeTurnMessageHandlers = (deps) => {
27675
27710
  backfillAssistantTextBlocksFromSnapshot
27676
27711
  } = deps;
27677
27712
  const { makeEventStamp, eventHeader, offerRuntimeEvent } = emitter;
27713
+ const emitReportedModel = makeReportedModelEmitter(emitter);
27678
27714
  const handleUserMessage = async (context, message) => {
27679
27715
  if (message.type !== "user") {
27680
27716
  return;
@@ -27869,6 +27905,7 @@ var makeTurnMessageHandlers = (deps) => {
27869
27905
  return;
27870
27906
  }
27871
27907
  await ensureSyntheticTurn(context);
27908
+ emitReportedModel(context, message.message?.model);
27872
27909
  const content = message.message?.content;
27873
27910
  if (Array.isArray(content)) {
27874
27911
  for (const block of content) {
@@ -28000,8 +28037,8 @@ var EventBus = class {
28000
28037
  const queued = this.#queued.shift();
28001
28038
  if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
28002
28039
  if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
28003
- return new Promise((resolve5) => {
28004
- this.#waiters.push(resolve5);
28040
+ return new Promise((resolve6) => {
28041
+ this.#waiters.push(resolve6);
28005
28042
  });
28006
28043
  }
28007
28044
  };
@@ -29628,14 +29665,56 @@ var executors = [
29628
29665
  createInstance: async (config, host) => {
29629
29666
  host.log.info("createInstance");
29630
29667
  return createClaudeInstance(config, host, capturedAttachments, void 0, capturedCache);
29631
- }
29668
+ },
29669
+ // 厂商脚本装,宿主卸不了;登录要在那台机器的终端里跑 `claude auth login`,宿主只回这条命令。
29670
+ lifecycle: [
29671
+ { kind: "install", reversible: false },
29672
+ { kind: "detect", reversible: true },
29673
+ { kind: "sign-in", reversible: false },
29674
+ { kind: "update", reversible: false }
29675
+ ]
29632
29676
  }
29633
29677
  ];
29634
29678
  var index_default = definePlugin({
29635
29679
  id: "agent-claude",
29636
- apiVersion: 52,
29680
+ apiVersion: 56,
29637
29681
  executors,
29638
29682
  activate(ctx) {
29683
+ ctx.command({
29684
+ name: "effectiveConfig",
29685
+ description: "Settings form: the config and credentials directories empty fields resolve to.",
29686
+ scope: "read",
29687
+ payloadSchema: EXECUTOR_EFFECTIVE_PAYLOAD_SCHEMA,
29688
+ handle: async (payload) => {
29689
+ const request = readExecutorEffectiveRequest(payload);
29690
+ const home = executorHomeEffectiveValue(
29691
+ "CLAUDE_CONFIG_DIR",
29692
+ request,
29693
+ process.env,
29694
+ (value) => resolveClaudeConfigDirPath("", value === void 0 ? {} : { CLAUDE_CONFIG_DIR: value })
29695
+ );
29696
+ const credentialsVariable = "CLAUDE_SECURESTORAGE_CONFIG_DIR";
29697
+ const configuredHome = typeof request.config["homePath"] === "string" ? request.config["homePath"] : "";
29698
+ const configVariable = executorEnvironmentValue("CLAUDE_CONFIG_DIR", request, process.env);
29699
+ const configDirectory = resolveClaudeConfigDirPath(
29700
+ configuredHome,
29701
+ configVariable === null ? {} : { CLAUDE_CONFIG_DIR: configVariable.value }
29702
+ );
29703
+ const keychain = (
29704
+ // eslint-disable-next-line ycodium/no-global-process-runtime -- 钥匙串只在 macOS 上用。
29705
+ process.platform === "darwin" && configuredHome.trim() === "" && configVariable === null
29706
+ );
29707
+ return {
29708
+ homePath: home,
29709
+ credentialsPath: executorEnvironmentValue(credentialsVariable, request, process.env) !== null ? executorHomeEffectiveValue(
29710
+ credentialsVariable,
29711
+ request,
29712
+ process.env,
29713
+ (value) => NodePath20.resolve(value ?? "")
29714
+ ) : keychain ? { detailKey: "form.credentialsPath.keychain" } : { value: configDirectory, detailKey: "form.credentialsPath.sameAsHome" }
29715
+ };
29716
+ }
29717
+ });
29639
29718
  capturedAttachments = ctx.attachments;
29640
29719
  capturedCache = ctx.cache;
29641
29720
  ctx.log.info("claude executor ready");
package/locales/en.json CHANGED
@@ -1,4 +1,8 @@
1
1
  {
2
+ "presentation": {
3
+ "name": "Claude Code",
4
+ "description": "Use Claude Code in Ycodium, switching models mid-session."
5
+ },
2
6
  "form": {
3
7
  "binaryPath": {
4
8
  "title": "Program path",
@@ -13,17 +17,24 @@
13
17
  "credentialsPath": {
14
18
  "title": "Credentials directory",
15
19
  "description": "Account-specific login directory. Keeps credentials separate while sharing the config directory.",
16
- "placeholder": "~/.claude-personal"
20
+ "placeholder": "~/.claude-personal",
21
+ "sameAsHome": "same as the config directory",
22
+ "keychain": "macOS Keychain"
17
23
  },
18
24
  "autoCompactWindow": {
19
25
  "title": "Auto-compact after",
20
26
  "description": "Compact after 100,000 to 1,000,000 tokens. Leave empty to use Claude's default.",
21
- "placeholder": "e.g. 300000"
27
+ "placeholder": "Claude's default"
22
28
  },
23
29
  "launchArgs": {
24
30
  "title": "Launch arguments",
25
31
  "description": "Additional CLI arguments passed on session start. --permission-mode and --dangerously-skip-permissions are ignored here; choose the permission mode in the chat input.",
26
32
  "placeholder": "e.g. --chrome"
33
+ },
34
+ "effective": {
35
+ "provider": "{{name}} in this provider",
36
+ "machine": "{{name}} on this machine",
37
+ "default": "default"
27
38
  }
28
39
  },
29
40
  "probe": {
@@ -1,4 +1,8 @@
1
1
  {
2
+ "presentation": {
3
+ "name": "Claude Code",
4
+ "description": "在 Ycodium 里使用 Claude Code,会话中途也能换模型。"
5
+ },
2
6
  "form": {
3
7
  "binaryPath": {
4
8
  "title": "程序路径",
@@ -13,17 +17,24 @@
13
17
  "credentialsPath": {
14
18
  "title": "凭据目录",
15
19
  "description": "账号专用的登录目录。凭据单独保存,配置目录仍可共用。",
16
- "placeholder": "~/.claude-personal"
20
+ "placeholder": "~/.claude-personal",
21
+ "sameAsHome": "与配置目录相同",
22
+ "keychain": "macOS 钥匙串"
17
23
  },
18
24
  "autoCompactWindow": {
19
25
  "title": "自动压缩阈值",
20
26
  "description": "在 100,000 到 1,000,000 个 token 后压缩。留空则使用 Claude 的默认值。",
21
- "placeholder": "例如 300000"
27
+ "placeholder": "Claude 的默认值"
22
28
  },
23
29
  "launchArgs": {
24
30
  "title": "启动参数",
25
31
  "description": "会话启动时传入的额外命令行参数。其中的 --permission-mode 与 --dangerously-skip-permissions 会被忽略,权限模式请在输入区选择。",
26
32
  "placeholder": "例如 --chrome"
33
+ },
34
+ "effective": {
35
+ "provider": "本 provider 的 {{name}}",
36
+ "machine": "本机的 {{name}}",
37
+ "default": "默认"
27
38
  }
28
39
  },
29
40
  "probe": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-claude",
3
- "version": "0.2.3681",
3
+ "version": "0.2.3766",
4
4
  "description": "Claude executor: Claude Code CLI via @anthropic-ai/claude-agent-sdk.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -24,15 +24,17 @@
24
24
  "@types/node": "24.12.4",
25
25
  "esbuild": "0.28.1",
26
26
  "vite-plus": "0.2.2",
27
- "@ycodium-ai/plugin-api": "0.2.3681",
28
27
  "ycodium-composer-tokens": "0.0.0",
29
28
  "ycodium-executor-testing": "0.0.0",
29
+ "@ycodium-ai/plugin-api": "0.2.3766",
30
30
  "ycodium-jsonl-stream": "0.0.0"
31
31
  },
32
32
  "ycodium": {
33
33
  "plugin": "./dist/index.js",
34
34
  "displayName": "Claude Code",
35
- "description": "Run Claude Code with in-session model and runtime-mode switching, native compaction, and session import.",
35
+ "displayNameKey": "presentation.name",
36
+ "description": "Use Claude Code in Ycodium, switching models mid-session.",
37
+ "descriptionKey": "presentation.description",
36
38
  "icon": "./assets/icon.svg",
37
39
  "permissions": [
38
40
  {