@sjhmars/happy-bridge 0.3.4 → 0.3.6

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/lib/client.js CHANGED
@@ -17,21 +17,21 @@ window.__ModuleLoader__.load({
17
17
  document.head.appendChild(tag);
18
18
  }
19
19
  var HappyBridgeCard_module_css_default = {
20
- "status": "l9rBEq_status",
21
20
  "description": "l9rBEq_description",
22
- "row": "l9rBEq_row",
21
+ "headText": "l9rBEq_headText",
23
22
  "error": "l9rBEq_error",
23
+ "select": "l9rBEq_select",
24
+ "row": "l9rBEq_row",
25
+ "body": "l9rBEq_body",
26
+ "status": "l9rBEq_status",
27
+ "label": "l9rBEq_label",
24
28
  "header": "l9rBEq_header",
25
- "checkbox": "l9rBEq_checkbox",
26
29
  "card": "l9rBEq_card",
27
- "body": "l9rBEq_body",
28
- "qr": "l9rBEq_qr",
29
30
  "button": "l9rBEq_button",
30
- "headText": "l9rBEq_headText",
31
- "name": "l9rBEq_name",
31
+ "checkbox": "l9rBEq_checkbox",
32
32
  "chevron": "l9rBEq_chevron",
33
- "label": "l9rBEq_label",
34
- "select": "l9rBEq_select"
33
+ "qr": "l9rBEq_qr",
34
+ "name": "l9rBEq_name"
35
35
  };
36
36
  //#endregion
37
37
  //#region src/client/HappyBridgeCard.tsx
package/lib/index.js CHANGED
@@ -303,11 +303,12 @@ async function buildSessionMetadata(ctx, source, machineId, title, selection, _g
303
303
  resume: false,
304
304
  rpcMethods: [
305
305
  "permission",
306
+ "communication",
306
307
  "killSession",
307
308
  "abort"
308
309
  ],
309
310
  shell: false,
310
- steering: false
311
+ steering: true
311
312
  },
312
313
  currentOperatingModeCode: currentPreset === "custom" ? operatingModes[0]?.code ?? "workspace-write" : currentPreset,
313
314
  ...selected === void 0 ? {} : {
@@ -3788,7 +3789,8 @@ var HappyBridge = class {
3788
3789
  const effort = messageEffort(meta);
3789
3790
  const permissionMode = meta.permissionMode;
3790
3791
  const publishedModel = this.lastPublished.get(agent.id)?.model;
3791
- if (model !== void 0 && publishedModel !== void 0 && sameCatalogPick({ model: publishedModel }, { model })) {} else if (model !== void 0) {
3792
+ const modelIsEcho = model !== void 0 && publishedModel !== void 0 && sameCatalogPick({ model: publishedModel }, { model });
3793
+ if (model !== void 0 && !modelIsEcho) {
3792
3794
  if (!grantAtLeast(this.config.remoteGrant, "full")) link.socket.sendText("service", "改模型需要远程档「完整」,这条消息仍会发出。");
3793
3795
  else this.applyModel(agent, model, typeof effort === "string" ? effort : effort === null ? null : void 0);
3794
3796
  } else if (effort === null || typeof effort === "string") {
@@ -3953,6 +3955,9 @@ var HappyBridge = class {
3953
3955
  * Queue the phone text as a user followup. The App already shows the typed
3954
3956
  * bubble; do not send a second user envelope. Images ride as DSH
3955
3957
  * attachments; other files land under `happy-inbox` for Harness `read`.
3958
+ * A turn that is still running gets the text as steering (`agent.steer`),
3959
+ * matching the `steering: true` capability we advertise — otherwise the
3960
+ * App unlocks the composer only to have the message wait a whole turn.
3956
3961
  */
3957
3962
  async followup(link, agent, text, files) {
3958
3963
  const blocks = [];
@@ -3977,10 +3982,12 @@ var HappyBridge = class {
3977
3982
  if (blocks.length === 0) return;
3978
3983
  link.skipNextUser += 1;
3979
3984
  link.agent = agent;
3980
- agent.followup(createUserMessage({
3985
+ const message = createUserMessage({
3981
3986
  content: blocks,
3982
3987
  source: { kind: "user" }
3983
- }));
3988
+ });
3989
+ if (agent.status === "running" && link.pendingHuman === void 0) agent.steer(message);
3990
+ else agent.followup(message);
3984
3991
  }
3985
3992
  /**
3986
3993
  * Claim every download started before this text, wait, keep the successes.
@@ -4266,7 +4273,10 @@ var HappyBridge = class {
4266
4273
  onPermission(dshId, rpc) {
4267
4274
  const link = this.links.get(dshId);
4268
4275
  const pending = link?.pendingHuman;
4269
- if (link === void 0 || pending === void 0 || pending.id !== rpc.id) return;
4276
+ if (link === void 0 || pending === void 0 || pending.id !== rpc.id) {
4277
+ this.log(`收到无法匹配的审批答复 id=${rpc.id}(旧卡片,或另一个实例抢走了本会话的 RPC)`);
4278
+ return;
4279
+ }
4270
4280
  if (pending.kind === "approval") {
4271
4281
  if (rpc.approved) {
4272
4282
  if (rpc.decision === "approved_for_session") link.alwaysAllow.add(pending.toolName);
@@ -4314,8 +4324,10 @@ var HappyBridge = class {
4314
4324
  onCommunication(dshId, rpc) {
4315
4325
  const link = this.links.get(dshId);
4316
4326
  const pending = link?.pendingHuman;
4317
- if (link === void 0 || pending === void 0 || pending.kind !== "ask") return;
4318
- if (pending.id !== rpc.id || pending.communication === void 0) return;
4327
+ if (link === void 0 || pending === void 0 || pending.kind !== "ask" || pending.id !== rpc.id || pending.communication === void 0) {
4328
+ this.log(`收到无法匹配的表单答复 id=${rpc.id}(旧卡片,或另一个实例抢走了本会话的 RPC)`);
4329
+ return;
4330
+ }
4319
4331
  if (rpc.status !== "answered" || rpc.answers === void 0) {
4320
4332
  pending.deferred ??= {};
4321
4333
  this.completeCommunication(link, rpc.id, pending.communication, "cancelled");
@@ -188,6 +188,9 @@ export declare class HappyBridge {
188
188
  * Queue the phone text as a user followup. The App already shows the typed
189
189
  * bubble; do not send a second user envelope. Images ride as DSH
190
190
  * attachments; other files land under `happy-inbox` for Harness `read`.
191
+ * A turn that is still running gets the text as steering (`agent.steer`),
192
+ * matching the `steering: true` capability we advertise — otherwise the
193
+ * App unlocks the composer only to have the message wait a whole turn.
191
194
  */
192
195
  private followup;
193
196
  /**
@@ -92,7 +92,8 @@ export interface SessionMetadata {
92
92
  resume: false;
93
93
  rpcMethods: string[];
94
94
  shell: false;
95
- steering: false;
95
+ /** True: mid-turn phone text steers the running turn (`agent.steer`). */
96
+ steering: boolean;
96
97
  };
97
98
  tools?: string[];
98
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjhmars/happy-bridge",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Remote-control bridge: pair a running dsh web/desktop client with the Happy mobile app by QR code, then remote-control the same harness sessions from the phone (send messages, read replies, approve tool calls, switch models)",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -121,6 +121,6 @@
121
121
  "scripts": {
122
122
  "build": "tsc -p tsconfig.json && tsdown -c tsdown.config.ts",
123
123
  "typecheck": "tsc -p tsconfig.json --noEmit",
124
- "test": "node --experimental-strip-types --test tests/*.test.ts"
124
+ "test": "node --experimental-strip-types --experimental-test-isolation=none --import ./tests/peer-stubs/register.mjs --test tests/*.test.ts"
125
125
  }
126
126
  }