@arcships/morula-runtime 0.1.0-alpha.13 → 0.1.0-alpha.14

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
@@ -740,7 +740,7 @@ var BUILD_ID, BUILD_CONTEXTS, BUILD_DEFAULT_CONTEXT;
740
740
  var init_build_env = __esm({
741
741
  "../morula-cli-core/dist/build-env.js"() {
742
742
  "use strict";
743
- BUILD_ID = "20260922161603.81b6d5a6";
743
+ BUILD_ID = "20260923140226.31d77ab2";
744
744
  BUILD_CONTEXTS = {
745
745
  "local": "http://localhost:8080/api",
746
746
  "test": "https://morula.basecastle.com/api",
@@ -4424,7 +4424,7 @@ init_dist2();
4424
4424
  init_dist2();
4425
4425
 
4426
4426
  // src/build-env.ts
4427
- var BUILD_VERSION2 = "0.1.0-alpha.13";
4427
+ var BUILD_VERSION2 = "0.1.0-alpha.14";
4428
4428
 
4429
4429
  // src/commands/daemon.ts
4430
4430
  init_dist();
@@ -8677,7 +8677,6 @@ var SessionHost = class {
8677
8677
  env,
8678
8678
  turnNo: 0,
8679
8679
  promptInFlight: null,
8680
- cancelRequested: false,
8681
8680
  // resume 成功 → dim 会话里已有上一进程注入的管家指令,不重复前置
8682
8681
  instructed: resumed,
8683
8682
  appliedConfig: { ...appliedConfig, permission },
@@ -8710,7 +8709,6 @@ var SessionHost = class {
8710
8709
  if (!cmd.prompt?.trim())
8711
8710
  return { ok: false, error: "SESSION_PROMPT_EMPTY" };
8712
8711
  managed.turnNo += 1;
8713
- managed.cancelRequested = false;
8714
8712
  this.touch(managed);
8715
8713
  const turnNo = managed.turnNo;
8716
8714
  const mapper = new AcpEventMapper();
@@ -8906,12 +8904,16 @@ ${fileRefs.join("\n")}` : text }];
8906
8904
  }
8907
8905
  return { blocks, fileRefs };
8908
8906
  }
8909
- /** 取消当前回合(ACP session/cancel;会话保持打开——长驻语义) */
8907
+ /**
8908
+ * 取消当前回合(ACP `session/cancel` 通知;会话保持打开——长驻语义)。
8909
+ *
8910
+ * **软中断**:dim 在安全点(step 之间)生效,不是硬杀进程;daemon 只负责把通知发出去,
8911
+ * 本轮的终态仍由 ACP `session/prompt` 响应驱动(`cancelled` → `turn_completed`)。
8912
+ */
8910
8913
  cancel(cmd) {
8911
8914
  const managed = this.sessions.get(cmd.sessionId);
8912
8915
  if (!managed)
8913
8916
  return { ok: false, error: "SESSION_NOT_OPEN" };
8914
- managed.cancelRequested = true;
8915
8917
  managed.conn.cancel(managed.dimSessionId);
8916
8918
  this.touch(managed);
8917
8919
  return { ok: true };
@@ -10107,16 +10109,25 @@ var DaemonRunner = class {
10107
10109
  /**
10108
10110
  * 会话宿主命令分发。
10109
10111
  *
10110
- * 两道闸:
10112
+ * 三道闸:
10111
10113
  * ① **runtimeId + daemonId 自过滤**(tenant 广播 → 只有归属设备处理;字段缺失时不过滤,兼容旧服务端)。
10112
10114
  * daemonId 是**实例**维度(issues/duplicate-daemon-same-runtime-double-execution.md):同机多实例
10113
10115
  * 注册时会复用同一行 AgentRuntime(同一 runtimeId),缺了它两个实例会各自 `open` 同一 sessionId
10114
10116
  * 并各跑一轮 prompt(实测:同一轮两份正文事件、两个 daemon 都「会话已打开」);
10115
- * ② **按会话串行**:服务端每轮 prompt 前会补发 `open` 做兜底,而 socket.io 只保证「送达有序」,
10116
- * 不保证「异步处理有序」——`void handleSessionCommand()` 会让 open/prompt 交错执行,
10117
- * prompt 可能先于 open 完成而拿到 SESSION_NOT_OPEN。故按 sessionId 串成一条 promise 链。
10117
+ * ② **按会话串行**(open / prompt / config):服务端每轮 prompt 前会补发 `open` 做兜底,而 socket.io
10118
+ * 只保证「送达有序」,不保证「异步处理有序」——`void handleSessionCommand()` 会让 open/prompt
10119
+ * 交错执行,prompt 可能先于 open 完成而拿到 SESSION_NOT_OPEN。故按 sessionId 串成一条 promise 链。
10120
+ * ③ **抢占类命令(cancel / close)不进链**:`prompt` 的 promise 直到本轮 `end_turn` 才 resolve
10121
+ * (`session-host.prompt()` await 的是 ACP `session/prompt` 响应),排在它后面 = 取消永远发生在
10122
+ * 回合结束之后,用户看到的是「点了停止但没停」(issues/steward-session-cancel-queued-behind-prompt.md)。
10123
+ * 它们只等该会话的 **open 门**——open 未完成时会话还不存在,取消只会拿到 SESSION_NOT_OPEN。
10118
10124
  */
10119
10125
  sessionCommandChains = /* @__PURE__ */ new Map();
10126
+ /**
10127
+ * 各会话**在飞 `open` 命令**的完成信号(抢占类命令的唯一前置门)。
10128
+ * 只在 open 入链时登记,open 落定即清理——它表达的是「会话在设备上就绪」,与后续 prompt 无关。
10129
+ */
10130
+ sessionOpenGates = /* @__PURE__ */ new Map();
10120
10131
  handleSessionCommand(kind, payload) {
10121
10132
  const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : void 0;
10122
10133
  if (runtimeId && this.runtimeId && runtimeId !== this.runtimeId)
@@ -10129,15 +10140,25 @@ var DaemonRunner = class {
10129
10140
  this.log(`[daemon] \u4F1A\u8BDD\u547D\u4EE4 ${kind} \u7F3A\u5C11 sessionId\uFF08\u5FFD\u7565\uFF09`);
10130
10141
  return Promise.resolve();
10131
10142
  }
10143
+ const logFailure = (err) => {
10144
+ this.log(`[daemon] \u4F1A\u8BDD\u547D\u4EE4 ${kind} \u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
10145
+ };
10146
+ if (kind === "cancel" || kind === "close") {
10147
+ const gate = this.sessionOpenGates.get(sessionId) ?? Promise.resolve();
10148
+ return gate.catch(() => {
10149
+ }).then(() => this.runSessionCommand(kind, payload)).catch(logFailure);
10150
+ }
10132
10151
  const prev = this.sessionCommandChains.get(sessionId) ?? Promise.resolve();
10133
10152
  const next = prev.catch(() => {
10134
- }).then(() => this.runSessionCommand(kind, payload)).catch((err) => {
10135
- this.log(`[daemon] \u4F1A\u8BDD\u547D\u4EE4 ${kind} \u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
10136
- });
10153
+ }).then(() => this.runSessionCommand(kind, payload)).catch(logFailure);
10137
10154
  this.sessionCommandChains.set(sessionId, next);
10155
+ if (kind === "open")
10156
+ this.sessionOpenGates.set(sessionId, next);
10138
10157
  void next.finally(() => {
10139
10158
  if (this.sessionCommandChains.get(sessionId) === next)
10140
10159
  this.sessionCommandChains.delete(sessionId);
10160
+ if (this.sessionOpenGates.get(sessionId) === next)
10161
+ this.sessionOpenGates.delete(sessionId);
10141
10162
  });
10142
10163
  return next;
10143
10164
  }
@@ -10153,9 +10174,13 @@ var DaemonRunner = class {
10153
10174
  await host.open(payload);
10154
10175
  else if (kind === "prompt")
10155
10176
  await host.prompt(payload);
10156
- else if (kind === "cancel")
10157
- host.cancel(payload);
10158
- else if (kind === "config")
10177
+ else if (kind === "cancel") {
10178
+ const res = host.cancel(payload);
10179
+ if (res.ok)
10180
+ this.log(`[daemon] \u4F1A\u8BDD ${String(payload.sessionId ?? "")} \u53D6\u6D88\u5DF2\u4E0B\u53D1\uFF08ACP session/cancel\uFF09`);
10181
+ else
10182
+ this.log(`[daemon] \u4F1A\u8BDD ${String(payload.sessionId ?? "")} \u53D6\u6D88\u5931\u8D25: ${res.error ?? "UNKNOWN"}`);
10183
+ } else if (kind === "config")
10159
10184
  await host.config(payload);
10160
10185
  else if (kind === "close")
10161
10186
  await host.close(payload);
package/dist/morula.js CHANGED
@@ -704,8 +704,8 @@ function page(ok, message) {
704
704
  }
705
705
 
706
706
  // ../morula-cli-core/dist/build-env.js
707
- var BUILD_VERSION = "0.1.0-alpha.13";
708
- var BUILD_ID = "20260922161603.81b6d5a6";
707
+ var BUILD_VERSION = "0.1.0-alpha.14";
708
+ var BUILD_ID = "20260923140226.31d77ab2";
709
709
  var BUILD_CONTEXTS = {
710
710
  "local": "http://localhost:8080/api",
711
711
  "test": "https://morula.basecastle.com/api",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morula-cli",
3
- "version": "0.1.0-alpha.13",
3
+ "version": "0.1.0-alpha.14",
4
4
  "description": "Use Morula CLI task management from DimCode.",
5
5
  "interface": {
6
6
  "displayName": "Morula CLI",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcships/morula-runtime",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.13",
4
+ "version": "0.1.0-alpha.14",
5
5
  "description": "Device-level CLI for the Morula local agent runtime (daemon/portal/models/auth) and the Morula business CLI (morula), distributed via npm",
6
6
  "bin": {
7
7
  "morula-runtime": "./bin/morula-runtime.js",