@ycodium-ai/agent-claude 0.2.3056 → 0.2.3070

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 (2) hide show
  1. package/dist/index.js +169 -8
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -22173,16 +22173,16 @@ function tokenizeShellWords(value) {
22173
22173
  if (!input) return [];
22174
22174
  const tokens = [];
22175
22175
  let current = "";
22176
- let quote;
22176
+ let quote2;
22177
22177
  let quoted = false;
22178
22178
  for (let index = 0; index < input.length; index++) {
22179
22179
  const char = input[index];
22180
22180
  if (char === void 0) continue;
22181
- if (quote) {
22182
- if (char === quote) {
22183
- quote = void 0;
22181
+ if (quote2) {
22182
+ if (char === quote2) {
22183
+ quote2 = void 0;
22184
22184
  quoted = true;
22185
- } else if (char === "\\" && quote === '"') {
22185
+ } else if (char === "\\" && quote2 === '"') {
22186
22186
  const next = input[index + 1];
22187
22187
  if (next !== void 0 && ['"', "\\", "$", "`"].includes(next)) {
22188
22188
  current += next;
@@ -22196,7 +22196,7 @@ function tokenizeShellWords(value) {
22196
22196
  continue;
22197
22197
  }
22198
22198
  if (char === "'" || char === '"') {
22199
- quote = char;
22199
+ quote2 = char;
22200
22200
  quoted = true;
22201
22201
  } else if (/\s/.test(char)) {
22202
22202
  if (current || quoted) {
@@ -23532,6 +23532,7 @@ var makeSessionLifecycle = (deps) => {
23532
23532
  await completeTurn(context, "interrupted", "Session stopped.");
23533
23533
  }
23534
23534
  if (closeFailure) throw closeFailure;
23535
+ context.cliProcess.clear();
23535
23536
  context.promptQueue.terminate();
23536
23537
  const streamFiber = context.streamFiber;
23537
23538
  context.streamFiber = void 0;
@@ -23650,6 +23651,106 @@ var makeSessionLifecycle = (deps) => {
23650
23651
  };
23651
23652
  };
23652
23653
 
23654
+ // src/claudeProcess.ts
23655
+ import * as NodeEvents from "node:events";
23656
+ import * as NodeStream from "node:stream";
23657
+ import { adoptChildStdin, nodeWritableFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
23658
+ var asError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
23659
+ var ClaudeProcess = class extends NodeEvents.EventEmitter {
23660
+ stdin;
23661
+ stdout;
23662
+ killed = false;
23663
+ exitCode = null;
23664
+ handle;
23665
+ ended = false;
23666
+ constructor(host, options, stderr) {
23667
+ super();
23668
+ const env2 = {
23669
+ ...Object.fromEntries(Object.keys(process.env).map((key) => [key, void 0])),
23670
+ ...options.env
23671
+ };
23672
+ const ready = host.process.spawn({
23673
+ program: options.command,
23674
+ args: options.args,
23675
+ ...options.cwd === void 0 ? {} : { cwd: options.cwd },
23676
+ env: env2,
23677
+ owner: { bindingId: host.bindingId }
23678
+ });
23679
+ this.stdin = nodeWritableFromHandle({
23680
+ writeStdin: (chunk) => ready.then((handle) => handle.writeStdin(chunk)),
23681
+ closeStdin: () => ready.then((handle) => handle.closeStdin())
23682
+ });
23683
+ this.stdout = NodeStream.Readable.from(
23684
+ (async function* () {
23685
+ const handle = await ready.catch(() => void 0);
23686
+ if (handle) yield* handle.stdout;
23687
+ })(),
23688
+ { objectMode: false }
23689
+ );
23690
+ adoptChildStdin(this.stdin, {
23691
+ onError: (error) => {
23692
+ this.emit("error", error);
23693
+ }
23694
+ });
23695
+ this.stdout.on("error", (error) => this.emit("error", error));
23696
+ this.requestKill = (signal) => {
23697
+ void ready.then((handle) => this.ended ? void 0 : handle.kill(signal)).catch((cause) => this.emit("error", asError(cause)));
23698
+ };
23699
+ const abort = () => {
23700
+ this.kill("SIGTERM");
23701
+ };
23702
+ options.signal.addEventListener("abort", abort, { once: true });
23703
+ if (options.signal.aborted) abort();
23704
+ void ready.then(async (handle) => {
23705
+ this.handle = handle;
23706
+ const drainStderr = (async () => {
23707
+ const decoder = new TextDecoder();
23708
+ for await (const chunk of handle.stderr) stderr(decoder.decode(chunk, { stream: true }));
23709
+ const tail = decoder.decode();
23710
+ if (tail) stderr(tail);
23711
+ })();
23712
+ void drainStderr.catch((cause) => this.emit("error", asError(cause)));
23713
+ try {
23714
+ const code = await handle.exited;
23715
+ this.ended = true;
23716
+ this.handle = void 0;
23717
+ this.exitCode = code;
23718
+ this.emit("exit", code, null);
23719
+ } finally {
23720
+ options.signal.removeEventListener("abort", abort);
23721
+ }
23722
+ }).catch((cause) => {
23723
+ this.ended = true;
23724
+ this.handle = void 0;
23725
+ options.signal.removeEventListener("abort", abort);
23726
+ this.emit("error", asError(cause));
23727
+ });
23728
+ }
23729
+ requestKill;
23730
+ kill(signal) {
23731
+ if (this.ended || signal !== "SIGTERM" && signal !== "SIGKILL") return false;
23732
+ this.killed = true;
23733
+ this.requestKill(signal);
23734
+ return true;
23735
+ }
23736
+ };
23737
+ function makeClaudeProcessSpawner(host, stderr) {
23738
+ let process2;
23739
+ let launch;
23740
+ return {
23741
+ spawn: (options) => {
23742
+ launch = options;
23743
+ process2 = new ClaudeProcess(host, options, stderr);
23744
+ return process2;
23745
+ },
23746
+ current: () => process2?.handle && launch ? { handle: process2.handle, command: launch.command, args: launch.args } : void 0,
23747
+ clear: () => {
23748
+ process2 = void 0;
23749
+ launch = void 0;
23750
+ }
23751
+ };
23752
+ }
23753
+
23653
23754
  // src/readOnlyDirectories.ts
23654
23755
  var GLOB_METACHARACTERS = /[*?[\]{}]/;
23655
23756
  function toClaudeAbsolutePath(directory) {
@@ -23832,7 +23933,9 @@ var makeStartSession = (deps) => {
23832
23933
  querySettings.permissions = { allow: [...readAllowRules] };
23833
23934
  }
23834
23935
  const stderrTail = createOutputTail(CLAUDE_STDERR_TAIL_CHARS);
23936
+ const cliProcess = makeClaudeProcessSpawner(deps.host, stderrTail.append);
23835
23937
  const queryOptions = {
23938
+ spawnClaudeCodeProcess: cliProcess.spawn,
23836
23939
  ...input.cwd ? { cwd: input.cwd } : {},
23837
23940
  ...apiModelId ? { model: apiModelId } : {},
23838
23941
  pathToClaudeCodeExecutable: claudeSdkExecutablePath,
@@ -23847,7 +23950,6 @@ var makeStartSession = (deps) => {
23847
23950
  ...existingResumeSessionId ? { resume: existingResumeSessionId } : {},
23848
23951
  ...newSessionId ? { sessionId: newSessionId } : {},
23849
23952
  includePartialMessages: true,
23850
- stderr: stderrTail.append,
23851
23953
  canUseTool,
23852
23954
  onUserDialog,
23853
23955
  hooks: {
@@ -23878,6 +23980,7 @@ var makeStartSession = (deps) => {
23878
23980
  options: queryOptions
23879
23981
  });
23880
23982
  } catch (cause) {
23983
+ cliProcess.clear();
23881
23984
  throw new Error("Failed to start Claude runtime session.", { cause });
23882
23985
  }
23883
23986
  const session = {
@@ -23906,6 +24009,7 @@ var makeStartSession = (deps) => {
23906
24009
  },
23907
24010
  promptQueue,
23908
24011
  query: queryRuntime,
24012
+ cliProcess,
23909
24013
  stderrTail,
23910
24014
  streamFiber: void 0,
23911
24015
  startedAt,
@@ -23925,6 +24029,7 @@ var makeStartSession = (deps) => {
23925
24029
  pendingUserInputs,
23926
24030
  turns: [],
23927
24031
  inFlightTools,
24032
+ openBashTools: /* @__PURE__ */ new Map(),
23928
24033
  claudeTasks,
23929
24034
  taskAgents,
23930
24035
  pendingTaskModels,
@@ -24107,6 +24212,7 @@ var makeStreamEventHandler = (deps) => {
24107
24212
  };
24108
24213
  const nextFingerprint = parsedInput && Object.keys(parsedInput).length > 0 ? toolInputFingerprint(parsedInput) : void 0;
24109
24214
  context.inFlightTools.set(event.index, nextTool);
24215
+ if (nextTool.toolName === "Bash") context.openBashTools.set(nextTool.itemId, nextTool);
24110
24216
  if (!parsedInput || !nextFingerprint || tool.lastEmittedInputFingerprint === nextFingerprint) {
24111
24217
  return;
24112
24218
  }
@@ -24183,7 +24289,9 @@ var makeStreamEventHandler = (deps) => {
24183
24289
  const inputFingerprint = Object.keys(toolInput).length > 0 ? toolInputFingerprint(toolInput) : void 0;
24184
24290
  const parentToolUseId = message.parent_tool_use_id ?? void 0;
24185
24291
  const owningAgentId = agentIdForParentToolUse(context.taskAgents, parentToolUseId);
24292
+ const stamp = await makeEventStamp();
24186
24293
  const tool = {
24294
+ startedAtMs: Date.parse(stamp.createdAt),
24187
24295
  itemId,
24188
24296
  itemType,
24189
24297
  toolName,
@@ -24196,7 +24304,7 @@ var makeStreamEventHandler = (deps) => {
24196
24304
  ...parentToolUseId ? { parentToolUseId } : {}
24197
24305
  };
24198
24306
  context.inFlightTools.set(index, tool);
24199
- const stamp = await makeEventStamp();
24307
+ if (toolName === "Bash") context.openBashTools.set(itemId, tool);
24200
24308
  offerRuntimeEvent(context, {
24201
24309
  type: "item.started",
24202
24310
  ...eventHeader(context, stamp),
@@ -25079,6 +25187,7 @@ var makeTurnLifecycle = (deps) => {
25079
25187
  context.inFlightTools.delete(index);
25080
25188
  }
25081
25189
  context.inFlightTools.clear();
25190
+ context.openBashTools.clear();
25082
25191
  for (const block of turnState.assistantTextBlockOrder) {
25083
25192
  await completeAssistantTextBlock(context, block, {
25084
25193
  force: true,
@@ -25149,6 +25258,7 @@ var makeTurnMessageHandlers = (deps) => {
25149
25258
  return;
25150
25259
  }
25151
25260
  for (const toolResult of toolResultBlocksFromUserMessage(message)) {
25261
+ context.openBashTools.delete(toolResult.toolUseId);
25152
25262
  const toolEntry = Array.from(context.inFlightTools.entries()).find(
25153
25263
  ([, tool2]) => tool2.itemId === toolResult.toolUseId
25154
25264
  );
@@ -27594,6 +27704,54 @@ var createClaudeUsage = (input) => {
27594
27704
  };
27595
27705
  };
27596
27706
 
27707
+ // src/openCommands.ts
27708
+ import { sameArgv } from "@ycodium-ai/plugin-api/executor";
27709
+ var quote = (command) => "'" + command.replaceAll("'", `'"'"'`) + "'";
27710
+ var basename = (path) => path.split(/[\\/]/).at(-1)?.toLowerCase() ?? "";
27711
+ function claudeCommandMatches(command, argv) {
27712
+ if (argv.length !== 3 || !["bash.exe", "bash"].includes(basename(argv[0] ?? "")) || argv[1] !== "-c")
27713
+ return false;
27714
+ const script = argv[2] ?? "";
27715
+ const source = /^source \S+\/shell-snapshots\/snapshot-bash-\d+-[a-zA-Z0-9]+\.sh /;
27716
+ const prefix = source.exec(script);
27717
+ if (!prefix) return false;
27718
+ const body = script.slice(prefix[0].length);
27719
+ if (!body.startsWith("2>/dev/null || true && ")) return false;
27720
+ const evalPrefix = " && eval ";
27721
+ const marker = evalPrefix + quote(command) + " < /dev/null && pwd -P >| ";
27722
+ const index = body.indexOf(marker);
27723
+ if (index < 0 || body.indexOf(evalPrefix) !== index || body.indexOf(evalPrefix, index + evalPrefix.length) !== -1)
27724
+ return false;
27725
+ return /^\S+\/claude-[a-fA-F0-9]{4}-cwd$/.test(body.slice(index + marker.length));
27726
+ }
27727
+ function openCommandsOf(context) {
27728
+ if (!context || context.stopped || !context.turnState) return void 0;
27729
+ const current = context.cliProcess.current();
27730
+ if (!current) return void 0;
27731
+ const commands = [...context.openBashTools.values()].flatMap((tool) => {
27732
+ const command = tool.input.command;
27733
+ return typeof command !== "string" || tool.input.run_in_background === true ? [] : [
27734
+ {
27735
+ toolCallId: tool.itemId,
27736
+ startedAtMs: tool.startedAtMs,
27737
+ matches: (argv, _platform) => claudeCommandMatches(command, argv)
27738
+ }
27739
+ ];
27740
+ });
27741
+ if (commands.length === 0) return void 0;
27742
+ return {
27743
+ rootPid: current.handle.pid,
27744
+ sessionProcess: (argv, platform2) => {
27745
+ const normalize2 = (value) => platform2 === "win32" ? value.replaceAll("\\", "/").toLowerCase() : value;
27746
+ const expected = normalize2(current.command);
27747
+ const actual = normalize2(argv[0] ?? "");
27748
+ const sameProgram = expected.includes("/") ? actual === expected : platform2 === "win32" ? basename(actual).replace(/\.exe$/, "") === expected.replace(/\.exe$/, "") : actual.split("/").at(-1) === expected;
27749
+ return sameProgram && sameArgv(argv.slice(1), current.args);
27750
+ },
27751
+ commands
27752
+ };
27753
+ }
27754
+
27597
27755
  // src/claudeInstance.ts
27598
27756
  function createClaudeAdapter(recipe, host, options) {
27599
27757
  const boundInstanceId = options?.instanceId ?? host.instanceId ?? "claudeAgent";
@@ -27709,6 +27867,7 @@ function createClaudeAdapter(recipe, host, options) {
27709
27867
  emitProposedPlanCompleted
27710
27868
  });
27711
27869
  const { startSession } = makeStartSession({
27870
+ host,
27712
27871
  emitter,
27713
27872
  recipe,
27714
27873
  log: options?.log ?? host.log,
@@ -27768,6 +27927,7 @@ function createClaudeAdapter(recipe, host, options) {
27768
27927
  setRuntimeMode,
27769
27928
  setNativeMode,
27770
27929
  stopSession,
27930
+ openCommandProcesses: async (threadId) => openCommandsOf(sessions.get(threadId)),
27771
27931
  events: runtimeEventQueue,
27772
27932
  dispose: async () => {
27773
27933
  const results = await Promise.allSettled(
@@ -27818,6 +27978,7 @@ function createClaudeInstance(config, host, attachments, overrides, usageCache)
27818
27978
  setNativeMode: adapter.setNativeMode,
27819
27979
  compactContext: adapter.compactContext,
27820
27980
  stopSession: adapter.stopSession,
27981
+ openCommandProcesses: adapter.openCommandProcesses,
27821
27982
  readThread: adapter.readThread,
27822
27983
  events: adapter.events,
27823
27984
  dispose: adapter.dispose,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-claude",
3
- "version": "0.2.3056",
3
+ "version": "0.2.3070",
4
4
  "description": "Claude executor: Claude Code CLI via @anthropic-ai/claude-agent-sdk.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -24,7 +24,7 @@
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.3056",
27
+ "@ycodium-ai/plugin-api": "0.2.3070",
28
28
  "ycodium-jsonl-stream": "0.0.0",
29
29
  "ycodium-composer-tokens": "0.0.0",
30
30
  "ycodium-executor-testing": "0.0.0"