@ycodium-ai/agent-codex 0.2.3799 → 0.2.3831

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 +130 -14
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -4650,6 +4650,112 @@ function mapServerRequestToEvents(input) {
4650
4650
  ];
4651
4651
  }
4652
4652
 
4653
+ // src/backgroundCommands.ts
4654
+ function commandDescription(item) {
4655
+ const actions = Array.isArray(item["commandActions"]) ? item["commandActions"] : [];
4656
+ const first = isRecord2(actions[0]) ? asString(actions[0]["command"]) : null;
4657
+ return first ?? asString(item["command"]) ?? "";
4658
+ }
4659
+ var CodexBackgroundCommands = class {
4660
+ #commands = /* @__PURE__ */ new Map();
4661
+ #threadId;
4662
+ #providerInstanceId;
4663
+ constructor(threadId, providerInstanceId) {
4664
+ this.#threadId = threadId;
4665
+ this.#providerInstanceId = providerInstanceId;
4666
+ }
4667
+ #completed(taskId, turnId, status, method) {
4668
+ return baseEvent({
4669
+ type: "task.completed",
4670
+ threadId: this.#threadId,
4671
+ turnId,
4672
+ payload: { taskId, status },
4673
+ method,
4674
+ providerInstanceId: this.#providerInstanceId
4675
+ });
4676
+ }
4677
+ #settleTurn(method, params) {
4678
+ const turnId = turnIdOf(params);
4679
+ const events = [];
4680
+ for (const [id, command] of this.#commands) {
4681
+ if (command.background || turnId !== void 0 && command.turnId !== turnId) continue;
4682
+ if (turnCompletedControlFlow(params).state !== "completed") {
4683
+ this.#commands.delete(id);
4684
+ continue;
4685
+ }
4686
+ this.#commands.set(id, { ...command, background: true });
4687
+ events.push(
4688
+ baseEvent({
4689
+ type: "task.started",
4690
+ threadId: this.#threadId,
4691
+ turnId: command.turnId,
4692
+ payload: { taskId: id, taskType: "local_bash", description: command.description },
4693
+ method,
4694
+ providerInstanceId: this.#providerInstanceId
4695
+ })
4696
+ );
4697
+ }
4698
+ return events;
4699
+ }
4700
+ /** 跟着通知走,返回要补发的 task 事件;放在该通知本身的事件之后发。 */
4701
+ observe(method, params) {
4702
+ if (method === "turn/completed") return this.#settleTurn(method, params);
4703
+ if (method === "thread/closed") {
4704
+ const closed = [...this.#commands].filter(([, command2]) => command2.background);
4705
+ this.#commands.clear();
4706
+ return closed.map(([id2, command2]) => this.#completed(id2, command2.turnId, "stopped", method));
4707
+ }
4708
+ if (!isRecord2(params) || !isRecord2(params["item"])) return [];
4709
+ const item = params["item"];
4710
+ const id = item["id"];
4711
+ if (item["type"] !== "commandExecution" || typeof id !== "string") return [];
4712
+ if (method === "item/started") {
4713
+ if (item["source"] === "unifiedExecInteraction") return [];
4714
+ this.#commands.set(id, {
4715
+ turnId: turnIdOf(params),
4716
+ description: commandDescription(item),
4717
+ processId: asString(item["processId"]) ?? void 0,
4718
+ background: false
4719
+ });
4720
+ return [];
4721
+ }
4722
+ if (method !== "item/completed") return [];
4723
+ const command = this.#commands.get(id);
4724
+ this.#commands.delete(id);
4725
+ if (command?.background !== true) return [];
4726
+ const status = itemStatusOf(params);
4727
+ return [
4728
+ this.#completed(
4729
+ id,
4730
+ command.turnId,
4731
+ status === "failed" ? "failed" : status === "declined" ? "stopped" : "completed",
4732
+ method
4733
+ )
4734
+ ];
4735
+ }
4736
+ /** 用户点停止时结束有进程号的后台任务;请求失败的留着,等它自己的 `item/completed`。 */
4737
+ async stop(rpc, nativeThreadId) {
4738
+ const running = [...this.#commands].filter(
4739
+ (entry) => entry[1].background && entry[1].processId !== void 0
4740
+ );
4741
+ const stopped = await Promise.all(
4742
+ running.map(async ([id, command]) => {
4743
+ try {
4744
+ await rpc.request("thread/backgroundTerminals/terminate", {
4745
+ threadId: nativeThreadId,
4746
+ processId: command.processId
4747
+ });
4748
+ } catch {
4749
+ return [];
4750
+ }
4751
+ this.#commands.delete(id);
4752
+ return [this.#completed(id, command.turnId, "stopped")];
4753
+ })
4754
+ );
4755
+ return stopped.flat();
4756
+ }
4757
+ };
4758
+
4653
4759
  // src/openCommands.ts
4654
4760
  import {
4655
4761
  isPosixShellCommandFlag,
@@ -4804,6 +4910,21 @@ function readSubAgentActivity(method, params) {
4804
4910
  agentPath: asString(item["agentPath"]) ?? void 0
4805
4911
  };
4806
4912
  }
4913
+ async function interruptForeignTurns(rpc, turns) {
4914
+ const started = Date.now();
4915
+ await Promise.all(
4916
+ [...turns.entries()].slice(0, 8).map(async ([thread, childTurn]) => {
4917
+ if (Date.now() - started > 1e4) return;
4918
+ try {
4919
+ await Promise.race([
4920
+ rpc.request("turn/interrupt", { threadId: thread, turnId: childTurn }, 0),
4921
+ new Promise((resolve4) => setTimeout(resolve4, 3e3))
4922
+ ]);
4923
+ } catch {
4924
+ }
4925
+ })
4926
+ );
4927
+ }
4807
4928
  var CodexSessionCollab = class {
4808
4929
  runtime;
4809
4930
  constructor(runtime) {
@@ -5299,6 +5420,7 @@ var CodexSessionRuntime = class {
5299
5420
  closeOptions;
5300
5421
  #collab = new CodexSessionCollab(this);
5301
5422
  #requests = new CodexSessionRequests(this);
5423
+ #background;
5302
5424
  /**
5303
5425
  * 对外 requestId 的前缀,每个会话(即每个 app-server 进程)一份。JSON-RPC id 每个进程
5304
5426
  * 都从 0 数起,宿主按 requestId 记「已接受」,不加前缀的话重启后的新请求会撞上旧请求。
@@ -5333,6 +5455,7 @@ var CodexSessionRuntime = class {
5333
5455
  });
5334
5456
  });
5335
5457
  this.emit = (event) => this.#lifecycle.emit(event);
5458
+ this.#background = new CodexBackgroundCommands(input.threadId, host.instanceId);
5336
5459
  this.threadId = input.threadId;
5337
5460
  this.cwd = cwd;
5338
5461
  this.runtimeMode = input.runtimeMode;
@@ -5483,6 +5606,7 @@ var CodexSessionRuntime = class {
5483
5606
  })) {
5484
5607
  this.emit(event);
5485
5608
  }
5609
+ for (const event of this.#background.observe(method, params)) this.emit(event);
5486
5610
  if (method === "turn/started" && this.#lifecycle.acceptsLifecycle) {
5487
5611
  const turnId = turnIdOf(params);
5488
5612
  if (turnId) this.activeTurnId = turnId;
@@ -5678,21 +5802,13 @@ var CodexSessionRuntime = class {
5678
5802
  };
5679
5803
  }
5680
5804
  async interruptTurn(turnId) {
5681
- const childTurns = [...this.liveForeignTurns.entries()];
5682
- const started = Date.now();
5683
- await Promise.all(
5684
- childTurns.slice(0, 8).map(async ([thread, childTurn]) => {
5685
- if (Date.now() - started > 1e4) return;
5686
- try {
5687
- await Promise.race([
5688
- this.rpc.request("turn/interrupt", { threadId: thread, turnId: childTurn }, 0),
5689
- sleep(3e3)
5690
- ]);
5691
- } catch {
5692
- }
5693
- })
5694
- );
5805
+ await interruptForeignTurns(this.rpc, this.liveForeignTurns);
5695
5806
  if (!this.nativeThreadId) return;
5807
+ if (turnId === void 0) {
5808
+ for (const event of await this.#background.stop(this.rpc, this.nativeThreadId)) {
5809
+ this.emit(event);
5810
+ }
5811
+ }
5696
5812
  const target = turnId ?? this.activeTurnId;
5697
5813
  if (target) {
5698
5814
  await this.rpc.request("turn/interrupt", { threadId: this.nativeThreadId, turnId: target }, 0).catch(() => void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-codex",
3
- "version": "0.2.3799",
3
+ "version": "0.2.3831",
4
4
  "description": "Codex executor: Codex CLI over the app-server JSON-RPC protocol.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -29,9 +29,9 @@
29
29
  "tailwindcss": "^4.0.0",
30
30
  "typescript": "~6.0.3",
31
31
  "vite-plus": "0.2.2",
32
- "@ycodium-ai/plugin-api": "0.2.3799",
33
32
  "ycodium-executor-testing": "0.0.0",
34
- "ycodium-jsonl-stream": "0.0.0"
33
+ "ycodium-jsonl-stream": "0.0.0",
34
+ "@ycodium-ai/plugin-api": "0.2.3831"
35
35
  },
36
36
  "ycodium": {
37
37
  "plugin": "./dist/index.js",