@agentdeck/bridge 1.0.5 → 1.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-server.d.ts","sourceRoot":"","sources":["../src/daemon-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,UAAU,EAA8B,MAAM,kBAAkB,CAAC;AAmF1E,OAAO,EAA8D,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA6P/G;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAC5C,YAAY,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAClD,OAAO,CAST;AAkBD,iBAAS,iBAAiB,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI,CA+C1E;AAYD,iBAAS,yBAAyB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAStD;AAED,iBAAS,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAsBlE;AA6ED,gBAAgB;AAChB,wBAAgB,uBAAuB,CAAC,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKzH;AACD,gBAAgB;AAChB,wBAAgB,wBAAwB,IAAI,IAAI,CAS/C;AACD,gBAAgB;AAChB,eAAO,MAAM,gBAAgB;;;;;CAK5B,CAAC;AAEF,iBAAe,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA8F3H;AAoHD,iEAAiE;AACjE,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAsBtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EACnH,KAAK,EAAE,CAAC,GACP,CAAC,CAWH;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,CAAA;CAAE,CAa3F;AAID,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiHD,wBAAsB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6rHpE"}
1
+ {"version":3,"file":"daemon-server.d.ts","sourceRoot":"","sources":["../src/daemon-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,UAAU,EAA8B,MAAM,kBAAkB,CAAC;AAmF1E,OAAO,EAA8D,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA6P/G;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAC5C,YAAY,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAClD,OAAO,CAST;AAkBD,iBAAS,iBAAiB,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI,CA+C1E;AAYD,iBAAS,yBAAyB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAStD;AAED,iBAAS,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAsBlE;AA6ED,gBAAgB;AAChB,wBAAgB,uBAAuB,CAAC,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKzH;AACD,gBAAgB;AAChB,wBAAgB,wBAAwB,IAAI,IAAI,CAS/C;AACD,gBAAgB;AAChB,eAAO,MAAM,gBAAgB;;;;;CAK5B,CAAC;AAEF,iBAAe,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA8F3H;AAoHD,iEAAiE;AACjE,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAsBtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EACnH,KAAK,EAAE,CAAC,GACP,CAAC,CAWH;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,CAAA;CAAE,CAa3F;AAID,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiHD,wBAAsB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAuwHpE"}
@@ -64,7 +64,7 @@ import { FallbackTaskTimeline } from './fallback-task-timeline.js';
64
64
  import { handleApmeRequest } from './apme/http.js';
65
65
  import { readModelFromTranscript } from './apme/claude-transcript-reader.js';
66
66
  import { transcriptTimelineForSession, lastAssistantTextFromTranscript, lastAssistantTextForSession, } from './session-transcript-timeline.js';
67
- import { DeviceVoiceReplyRouter, speakableReply, pcmFromWav, } from './device-voice-reply.js';
67
+ import { DeviceVoiceReplyRouter, speakableReply, spokenDigest, pcmFromWav, } from './device-voice-reply.js';
68
68
  import { rawSessionId } from '@agentdeck/shared';
69
69
  import { lastAgentMessageFromCodexRollout } from './codex-rollout-response.js';
70
70
  import { callFoundationModelsHelper } from './foundation-models-helper.js';
@@ -75,7 +75,7 @@ import { getConnectedAdbDevices, hasAdb, getAdbDeviceCount } from './adb-reverse
75
75
  import { getPixooDeviceDetails, pixooDeviceCount } from './pixoo/pixoo-bridge.js';
76
76
  import { loadTimeboxDevices } from './timebox/timebox-settings.js';
77
77
  import { getLanIp, stripUnsafeText, cleanRawText, prepareMarkdownDetail, normalizeCommandPrompt, formatDurationSec } from '@agentdeck/shared';
78
- import { injectOpenClawSession } from './openclaw-session.js';
78
+ import { injectOpenClawSession, OPENCLAW_SESSION_ID } from './openclaw-session.js';
79
79
  import { buildCardFeed, buildGlance, applyOutboxDecisions, FeedPullTracker, formatFeedPull, normalizeClientIp, parsePullTelemetry, } from './card-feed.js';
80
80
  import { threadModule } from './card-modules.js';
81
81
  import { AutonomousPocketEngine, createAutonomousPocketModules, parsePocketAutonomyConfig, } from './pocket-autonomy.js';
@@ -983,6 +983,22 @@ export async function startDaemon(opts) {
983
983
  // `gatewaySessionState` / `gatewayModelName` dedicated fields.
984
984
  let gatewaySessionState = 'idle';
985
985
  let gatewayModelName;
986
+ /**
987
+ * Snapshot for an `openclaw` state event: the global machine with the
988
+ * Gateway-local model pinned back on.
989
+ *
990
+ * The Gateway's `model_info` no longer writes `core.stateMachine` (see the
991
+ * parser branch): that machine is fed by every observed Claude/Codex/OpenCode
992
+ * hook, and the hub stamps its broadcasts with the user's focused session —
993
+ * so a model written there by a *different* agent is read as the focused
994
+ * session's own. That is how `GLM-5.2 (1M)` appeared as the model of a Claude
995
+ * session on both decks. Keeping the model out of the shared slot means the
996
+ * Gateway's own events have to carry it explicitly, which is what this does.
997
+ */
998
+ const gatewaySnapshot = (base) => ({
999
+ ...(base ?? core.stateMachine.getSnapshot()),
1000
+ modelName: gatewayModelName ?? null,
1001
+ });
986
1002
  // Wi-Fi WebSocket e-ink panels (XTeink X3/X4 CrossPoint fork) self-register
987
1003
  // their device roster via `client_register{clientType:"eink-device"}` — the
988
1004
  // same volunteer model as the Stream Deck plugin. This handler lived ONLY in
@@ -2475,6 +2491,24 @@ export async function startDaemon(opts) {
2475
2491
  core.wireDisplayMonitor();
2476
2492
  let lastStateEvent = null;
2477
2493
  let userFocusedSessionId = null;
2494
+ /**
2495
+ * Stamp the focused session onto a hub `state_update`.
2496
+ *
2497
+ * This field carries two jobs, and both are load-bearing, which is why it
2498
+ * cannot simply be narrowed:
2499
+ * - attribution: `buildStateEvent` sets no `sessionId` at all, so a
2500
+ * hook-observed session's AWAITING_PERMISSION options reach the decks
2501
+ * attributed only by this field (see session-deck-awaiting tests).
2502
+ * - focus ack: the Apple app toggles creature focus against it and needs it
2503
+ * even for a session that emits nothing of its own.
2504
+ *
2505
+ * The consequence is a standing invariant on the payload: whatever rides along
2506
+ * here is read as the focused session's own. So nothing may write a *different*
2507
+ * agent's per-session field into `core.stateMachine` — the Gateway's model went
2508
+ * in and came out as a Claude session's model. Gateway-specific state lives in
2509
+ * `gatewaySessionState` / `gatewayModelName` and is re-attached by
2510
+ * `gatewaySnapshot()`.
2511
+ */
2478
2512
  const attachFocusedSessionId = (event) => {
2479
2513
  if (event.type !== 'state_update')
2480
2514
  return event;
@@ -2483,6 +2517,28 @@ export async function startDaemon(opts) {
2483
2517
  focusedSessionId: userFocusedSessionId ?? '',
2484
2518
  };
2485
2519
  };
2520
+ /**
2521
+ * Stamp a *Gateway-specific* state event — one whose payload really is the
2522
+ * Gateway's (its parser events, its connection transitions), as opposed to the
2523
+ * hub broadcasts above that merely get labelled `openclaw` while the Gateway is
2524
+ * alive.
2525
+ *
2526
+ * These name their own row (`sessionId`), which is the honest attribution and
2527
+ * the reason a client never has to guess: an event carrying the Gateway's model
2528
+ * now says so. The focus ack rides only when the Gateway IS the focused row —
2529
+ * otherwise this event would claim to describe a Claude session, which is
2530
+ * precisely how `GLM-5.2 (1M)` became that session's model on both decks. The
2531
+ * ack still reaches the Apple app through every hub broadcast.
2532
+ */
2533
+ const attachGatewayEvent = (event) => {
2534
+ if (event.type !== 'state_update')
2535
+ return event;
2536
+ return {
2537
+ ...event,
2538
+ sessionId: OPENCLAW_SESSION_ID,
2539
+ focusedSessionId: userFocusedSessionId === OPENCLAW_SESSION_ID ? OPENCLAW_SESSION_ID : '',
2540
+ };
2541
+ };
2486
2542
  const broadcastFocusedState = () => {
2487
2543
  const gwAlive = gatewayAdapter?.isAlive() ?? false;
2488
2544
  const stateEvent = attachFocusedSessionId(core.buildStateEvent({
@@ -2932,7 +2988,14 @@ export async function startDaemon(opts) {
2932
2988
  core.stateMachine.handleHookEvent('SessionEnd', {});
2933
2989
  break;
2934
2990
  case 'parser':
2935
- core.stateMachine.handleParserEvent(evt.event, evt.data);
2991
+ // `model_info` is deliberately NOT forwarded: the global machine holds
2992
+ // one modelName for the whole hub, fed by every observed hook session,
2993
+ // and its broadcasts are stamped with the user's focused session — so a
2994
+ // model written here by the Gateway is read as that session's own
2995
+ // (GLM on a Claude row). It lives in `gatewayModelName` instead, and
2996
+ // `gatewaySnapshot()` puts it back on the Gateway's own events.
2997
+ if (evt.event !== 'model_info')
2998
+ core.stateMachine.handleParserEvent(evt.event, evt.data);
2936
2999
  // Gateway-local activity state for the virtual session row (mirror of
2937
3000
  // Swift `gatewaySessionState`). Kept separate from the global
2938
3001
  // `core.stateMachine` above so the OpenClaw row reflects only the
@@ -2961,10 +3024,10 @@ export async function startDaemon(opts) {
2961
3024
  if (models) {
2962
3025
  core.cachedModelCatalog = models;
2963
3026
  const snap = core.stateMachine.getSnapshot();
2964
- const stateEvent = attachFocusedSessionId(core.buildStateEvent({
3027
+ const stateEvent = attachGatewayEvent(core.buildStateEvent({
2965
3028
  agentType: 'openclaw',
2966
3029
  agentCapabilities: OPENCLAW_CAPABILITIES,
2967
- snapshot: snap,
3030
+ snapshot: gatewaySnapshot(snap),
2968
3031
  }));
2969
3032
  lastStateEvent = stateEvent;
2970
3033
  core.broadcast(stateEvent);
@@ -3017,10 +3080,10 @@ export async function startDaemon(opts) {
3017
3080
  }
3018
3081
  // Force full state broadcast
3019
3082
  const snap = core.stateMachine.getSnapshot();
3020
- const gwStateEvent = attachFocusedSessionId(core.buildStateEvent({
3083
+ const gwStateEvent = attachGatewayEvent(core.buildStateEvent({
3021
3084
  agentType: 'openclaw',
3022
3085
  agentCapabilities: OPENCLAW_CAPABILITIES,
3023
- snapshot: snap,
3086
+ snapshot: gatewaySnapshot(snap),
3024
3087
  }));
3025
3088
  lastStateEvent = gwStateEvent;
3026
3089
  core.wsServer.broadcast(gwStateEvent);
@@ -3434,10 +3497,10 @@ export async function startDaemon(opts) {
3434
3497
  if (target === 'openclaw' && gatewayAdapter?.isAlive()) {
3435
3498
  // Force broadcast OpenClaw state to all clients
3436
3499
  const snap = core.stateMachine.getSnapshot();
3437
- const gwStateEvent = attachFocusedSessionId(core.buildStateEvent({
3500
+ const gwStateEvent = attachGatewayEvent(core.buildStateEvent({
3438
3501
  agentType: 'openclaw',
3439
3502
  agentCapabilities: OPENCLAW_CAPABILITIES,
3440
- snapshot: snap,
3503
+ snapshot: gatewaySnapshot(snap),
3441
3504
  }));
3442
3505
  lastStateEvent = gwStateEvent;
3443
3506
  core.wsServer.broadcast(gwStateEvent);
@@ -3799,7 +3862,18 @@ export async function startDaemon(opts) {
3799
3862
  const voiceCfg = loadDaemonSettings().voice;
3800
3863
  if (voiceCfg?.speakReplies === false)
3801
3864
  return; // opt-out, default on
3802
- const spoken = speakableReply(text);
3865
+ // Speech gets the lead, not the transcript: an explicit summary line if the
3866
+ // answer has one, else its first sentence. The full answer is already on the
3867
+ // screen the user is sitting at, where it can be skimmed — read aloud it is
3868
+ // a minute of monologue nobody can skip. `voice.speakFullReply` opts back in.
3869
+ const full = voiceCfg?.speakFullReply === true;
3870
+ const spoken = spokenDigest(text, { full });
3871
+ if (!full) {
3872
+ const readable = speakableReply(text, Number.MAX_SAFE_INTEGER);
3873
+ if (spoken && readable.length > spoken.length) {
3874
+ log(`[agentdeck] voice: reply digested ${readable.length} -> ${spoken.length} chars`);
3875
+ }
3876
+ }
3803
3877
  if (!spoken) {
3804
3878
  // A code-only answer has nothing worth reading aloud; say that instead of
3805
3879
  // spelling out punctuation, so the user still knows the turn finished.