@linkshell/host 0.4.68 → 0.4.70

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 (82) hide show
  1. package/dist/host/src/drivers/acp/antigravity.d.ts +13 -0
  2. package/dist/host/src/drivers/acp/antigravity.js +13 -0
  3. package/dist/host/src/drivers/acp/antigravity.js.map +1 -1
  4. package/dist/host/src/drivers/acp/driver.d.ts +23 -2
  5. package/dist/host/src/drivers/acp/driver.js +89 -5
  6. package/dist/host/src/drivers/acp/driver.js.map +1 -1
  7. package/dist/host/src/drivers/acp/mapper.d.ts +2 -0
  8. package/dist/host/src/drivers/acp/mapper.js +6 -1
  9. package/dist/host/src/drivers/acp/mapper.js.map +1 -1
  10. package/dist/host/src/drivers/claude/app-sessions.d.ts +58 -0
  11. package/dist/host/src/drivers/claude/app-sessions.js +183 -0
  12. package/dist/host/src/drivers/claude/app-sessions.js.map +1 -0
  13. package/dist/host/src/drivers/claude/driver.d.ts +37 -1
  14. package/dist/host/src/drivers/claude/driver.js +199 -9
  15. package/dist/host/src/drivers/claude/driver.js.map +1 -1
  16. package/dist/host/src/drivers/claude/holders.d.ts +3 -1
  17. package/dist/host/src/drivers/claude/holders.js +4 -0
  18. package/dist/host/src/drivers/claude/holders.js.map +1 -1
  19. package/dist/host/src/drivers/claude/routing.d.ts +19 -0
  20. package/dist/host/src/drivers/claude/routing.js +145 -0
  21. package/dist/host/src/drivers/claude/routing.js.map +1 -0
  22. package/dist/host/src/drivers/claude/transcript.d.ts +10 -0
  23. package/dist/host/src/drivers/claude/transcript.js +97 -22
  24. package/dist/host/src/drivers/claude/transcript.js.map +1 -1
  25. package/dist/host/src/drivers/codex/driver.d.ts +45 -2
  26. package/dist/host/src/drivers/codex/driver.js +288 -18
  27. package/dist/host/src/drivers/codex/driver.js.map +1 -1
  28. package/dist/host/src/drivers/codex/mapper.d.ts +41 -7
  29. package/dist/host/src/drivers/codex/mapper.js +167 -26
  30. package/dist/host/src/drivers/codex/mapper.js.map +1 -1
  31. package/dist/host/src/drivers/codex/mcp-apps.d.ts +69 -0
  32. package/dist/host/src/drivers/codex/mcp-apps.js +340 -0
  33. package/dist/host/src/drivers/codex/mcp-apps.js.map +1 -0
  34. package/dist/host/src/drivers/codex/settings.d.ts +13 -0
  35. package/dist/host/src/drivers/codex/settings.js +22 -6
  36. package/dist/host/src/drivers/codex/settings.js.map +1 -1
  37. package/dist/host/src/drivers/types.d.ts +82 -1
  38. package/dist/host/src/host.js +2 -1
  39. package/dist/host/src/host.js.map +1 -1
  40. package/dist/host/src/hub.d.ts +48 -1
  41. package/dist/host/src/hub.js +235 -13
  42. package/dist/host/src/hub.js.map +1 -1
  43. package/dist/host/src/rpc/mcp.d.ts +8 -0
  44. package/dist/host/src/rpc/mcp.js +33 -0
  45. package/dist/host/src/rpc/mcp.js.map +1 -0
  46. package/dist/host/src/rpc/server.js +27 -3
  47. package/dist/host/src/rpc/server.js.map +1 -1
  48. package/dist/host/src/side.d.ts +28 -0
  49. package/dist/host/src/side.js +142 -0
  50. package/dist/host/src/side.js.map +1 -0
  51. package/dist/host/src/store.d.ts +7 -0
  52. package/dist/host/src/store.js +27 -3
  53. package/dist/host/src/store.js.map +1 -1
  54. package/dist/host/src/terminals.d.ts +6 -0
  55. package/dist/host/src/terminals.js +29 -2
  56. package/dist/host/src/terminals.js.map +1 -1
  57. package/dist/host/src/usage.d.ts +5 -0
  58. package/dist/host/src/usage.js +10 -2
  59. package/dist/host/src/usage.js.map +1 -1
  60. package/dist/host/tsconfig.tsbuildinfo +1 -1
  61. package/dist/wire/src/envelopes.d.ts +53 -0
  62. package/dist/wire/src/envelopes.js +251 -0
  63. package/dist/wire/src/envelopes.js.map +1 -0
  64. package/dist/wire/src/index.d.ts +3 -0
  65. package/dist/wire/src/index.js +3 -0
  66. package/dist/wire/src/index.js.map +1 -1
  67. package/dist/wire/src/model.d.ts +54 -0
  68. package/dist/wire/src/model.js +28 -0
  69. package/dist/wire/src/model.js.map +1 -1
  70. package/dist/wire/src/rpc-mcp.d.ts +199 -0
  71. package/dist/wire/src/rpc-mcp.js +78 -0
  72. package/dist/wire/src/rpc-mcp.js.map +1 -0
  73. package/dist/wire/src/rpc.d.ts +823 -6
  74. package/dist/wire/src/rpc.js +64 -1
  75. package/dist/wire/src/rpc.js.map +1 -1
  76. package/dist/wire/src/side.d.ts +83 -0
  77. package/dist/wire/src/side.js +26 -0
  78. package/dist/wire/src/side.js.map +1 -0
  79. package/dist/wire/src/updates.d.ts +211 -0
  80. package/dist/wire/src/updates.js +14 -1
  81. package/dist/wire/src/updates.js.map +1 -1
  82. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"host.js","sourceRoot":"","sources":["../../../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAwC,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEhE,4DAA4D;AAC5D,MAAM,eAAe,GAAG,GAAG,CAAC;AAU5B,wEAAwE;AACxE,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAI,GAAG,WAAW,EAAE;IAC5C,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,GAAG,eAAe,EAAE,CAAC;QACpG,2FAA2F;QAC3F,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO;QACL,IAAI;QACJ,MAAM;QACN,UAAU,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;QAChG,WAAW,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;QACnG,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;KACjC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAA4B,CAAC;QACjF,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO,MAAM,CAAC,SAAS,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IACD,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAC/B,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzH,OAAO,SAAS,CAAC;AACnB,CAAC;AA8CD,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC;IACpD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACvD,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAoB;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,MAAM,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5C,yFAAyF;IACzF,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,wBAAwB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1F,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;QAC7B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;QACxB,CAAC,CAAC,cAAc,CAAC;YACb,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE;YAC5B,GAAG;YACH,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,aAAa,EAAE,OAAO,CAAC,aAAa;SACrC,CAAC,CAAC;IACP,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE;QAC1D,QAAQ,EAAE,WAAW;QACrB,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QACnF,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACnE,MAAM,MAAM,GAAG,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;gBACpD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;oBAC/C,IAAI,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE,CAAC;wBAAC,IAAI,EAAE,CAAC;wBAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;oBAAC,CAAC;gBACrH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;QAC7C,CAAC;KACF,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAEtE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC9F,MAAM,WAAW,GAAG,GAAgB,EAAE,CAAC,CAAC;QACtC,SAAS;QACT,0FAA0F;QAC1F,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,QAAQ,EAAE;QAC1D,QAAQ,EAAE,QAAQ,EAAE;QACpB,WAAW,EAAE,OAAO,CAAC,OAAO;QAC5B,IAAI,EAAE,OAAO,EAAE;QACf,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QACpB,qGAAqG;QACrG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,IAAI,iBAAiB,CAAC;QAC3C,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,sBAAsB,CAAC,EAAE,GAAG,EAAE,SAAS;QAC9D,YAAY,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ;QAC1C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B;QAClD,GAAG;KACJ,CAAC,CAAC;IACH,cAAc,CAAC,KAAK,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,OAAgC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC;QAC/B,GAAG;QACH,SAAS;QACT,WAAW;QACX,MAAM;QACN,mEAAmE;QACnE,eAAe,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE;QAChD,KAAK,EAAE,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QAClN,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG;QACH,cAAc;KACf,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAErB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAC/B,mGAAmG;IACnG,MAAM,WAAW,GAAG,GAAkB,EAAE;QACtC,MAAM,GAAG,GAAG,OAAO,MAAM,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC;QAC3E,IAAI,OAAO,IAAI,OAAO,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACnC,OAAO,CAAC,cAAc,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,OAAO,GAAG,GAAG;YACX,CAAC,CAAC,IAAI,WAAW,CAAC;gBACd,GAAG;gBACH,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,MAAM,EAAE,KAAK;gBACpB,IAAI,EAAE,MAAM,EAAE,IAAI;gBAClB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO;gBAC5B,KAAK,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;gBAC/C,oDAAoD;gBACpD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;oBACnB,IAAI,OAAO,EAAE,GAAG,KAAK,GAAG;wBAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;gBACxE,CAAC;gBACD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC;gBAClE,GAAG;aACJ,CAAC;YACJ,CAAC,CAAC,SAAS,CAAC;QACd,QAAQ,EAAE,IAAI,EAAE,CAAC;QACjB,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,EAAE,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAc,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAC5E,IAAI,QAAQ;YAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACrC,WAAW,EAAE,CAAC;IAEd,IAAI,QAAmC,CAAC;IACxC,OAAO;QACL,KAAK;QACL,GAAG;QACH,MAAM;QACN,IAAI,OAAO;YACT,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,WAAW;QACX,IAAI;YACF,QAAQ,KAAK,CAAC,KAAK,IAAI,EAAE;gBACvB,OAAO,EAAE,IAAI,EAAE,CAAC;gBAChB,cAAc,CAAC,IAAI,EAAE,CAAC;gBACtB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBACpB,SAAS,CAAC,IAAI,EAAE,CAAC;gBACjB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBACjB,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,CAAC,CAAC,EAAE,CAAC;YACL,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAwC,MAAM,iBAAiB,CAAC;AAC3G,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEhE,4DAA4D;AAC5D,MAAM,eAAe,GAAG,GAAG,CAAC;AAU5B,wEAAwE;AACxE,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAI,GAAG,WAAW,EAAE;IAC5C,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,GAAG,eAAe,EAAE,CAAC;QACpG,2FAA2F;QAC3F,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO;QACL,IAAI;QACJ,MAAM;QACN,UAAU,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;QAChG,WAAW,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;QACnG,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;KACjC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAA4B,CAAC;QACjF,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO,MAAM,CAAC,SAAS,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IACD,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAC/B,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzH,OAAO,SAAS,CAAC;AACnB,CAAC;AA8CD,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC;IACpD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACvD,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAoB;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,MAAM,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5C,yFAAyF;IACzF,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,wBAAwB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1F,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;QAC7B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;QACxB,CAAC,CAAC,cAAc,CAAC;YACb,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE;YAC5B,GAAG;YACH,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,aAAa,EAAE,OAAO,CAAC,aAAa;SACrC,CAAC,CAAC;IACP,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE;QAC1D,QAAQ,EAAE,WAAW;QACrB,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QACnF,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACnE,MAAM,MAAM,GAAG,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;gBACpD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;oBAC/C,IAAI,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE,CAAC;wBAAC,IAAI,EAAE,CAAC;wBAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;oBAAC,CAAC;gBACrH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;QAC7C,CAAC;KACF,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAEtE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC9F,MAAM,WAAW,GAAG,GAAgB,EAAE,CAAC,CAAC;QACtC,SAAS;QACT,0FAA0F;QAC1F,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,QAAQ,EAAE;QAC1D,QAAQ,EAAE,QAAQ,EAAE;QACpB,WAAW,EAAE,OAAO,CAAC,OAAO;QAC5B,QAAQ,EAAE,aAAa;QACvB,IAAI,EAAE,OAAO,EAAE;QACf,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QACpB,qGAAqG;QACrG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,IAAI,iBAAiB,CAAC;QAC3C,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,sBAAsB,CAAC,EAAE,GAAG,EAAE,SAAS;QAC9D,YAAY,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ;QAC1C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B;QAClD,GAAG;KACJ,CAAC,CAAC;IACH,cAAc,CAAC,KAAK,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,OAAgC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC;QAC/B,GAAG;QACH,SAAS;QACT,WAAW;QACX,MAAM;QACN,mEAAmE;QACnE,eAAe,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE;QAChD,KAAK,EAAE,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QAClN,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG;QACH,cAAc;KACf,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAErB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAC/B,mGAAmG;IACnG,MAAM,WAAW,GAAG,GAAkB,EAAE;QACtC,MAAM,GAAG,GAAG,OAAO,MAAM,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC;QAC3E,IAAI,OAAO,IAAI,OAAO,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACnC,OAAO,CAAC,cAAc,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,OAAO,GAAG,GAAG;YACX,CAAC,CAAC,IAAI,WAAW,CAAC;gBACd,GAAG;gBACH,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,MAAM,EAAE,KAAK;gBACpB,IAAI,EAAE,MAAM,EAAE,IAAI;gBAClB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO;gBAC5B,KAAK,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;gBAC/C,oDAAoD;gBACpD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;oBACnB,IAAI,OAAO,EAAE,GAAG,KAAK,GAAG;wBAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;gBACxE,CAAC;gBACD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC;gBAClE,GAAG;aACJ,CAAC;YACJ,CAAC,CAAC,SAAS,CAAC;QACd,QAAQ,EAAE,IAAI,EAAE,CAAC;QACjB,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,EAAE,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAc,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAC5E,IAAI,QAAQ;YAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACrC,WAAW,EAAE,CAAC;IAEd,IAAI,QAAmC,CAAC;IACxC,OAAO;QACL,KAAK;QACL,GAAG;QACH,MAAM;QACN,IAAI,OAAO;YACT,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,WAAW;QACX,IAAI;YACF,QAAQ,KAAK,CAAC,KAAK,IAAI,EAAE;gBACvB,OAAO,EAAE,IAAI,EAAE,CAAC;gBAChB,cAAc,CAAC,IAAI,EAAE,CAAC;gBACtB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBACpB,SAAS,CAAC,IAAI,EAAE,CAAC;gBACjB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBACjB,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,CAAC,CAAC,EAAE,CAAC;YACL,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import { type GoalChange, type BackgroundTask, type AgentInfo, type ContentBlock, type SessionEvent, type SessionSummary, type SubagentInfo, type QuestionAnswer, type GitInfo, type ProjectSummary, type WorktreeEntry, type SessionNotice, type AcpAgentSettings, type AcpRemoteAgent, type PendingPermissionSummary, type EditorRequest } from "@linkshell/wire";
1
+ import { type GoalChange, type BackgroundTask, type AgentInfo, type ContentBlock, type SessionEvent, type SessionSummary, type SubagentInfo, type QuestionAnswer, type GitInfo, type ProjectSummary, type WorktreeEntry, type SessionNotice, type AcpAgentSettings, type AcpRemoteAgent, type PendingPermissionSummary, type EditorRequest, type SideChat, type MethodResult } from "@linkshell/wire";
2
2
  import type { AcpSettingsStore } from "./acp-settings.js";
3
3
  import type { AgentDriver, DesktopController, DesktopLaunchContext, DriverHost, LaunchSpec } from "./drivers/types.js";
4
4
  import { type HostStore } from "./store.js";
@@ -9,6 +9,17 @@ export interface Subscriber {
9
9
  /** The backlog that follows starts after `startSeq`, not where the subscriber left off. */
10
10
  window?(startSeq: number): void;
11
11
  }
12
+ /**
13
+ * Archived here or in the app, unless it was unarchived here after the app
14
+ * archived it (`local.at` 0: chosen before times were kept, so it never outranks the app).
15
+ */
16
+ export declare function mergedArchive(local: {
17
+ archived: boolean;
18
+ at: number;
19
+ }, app: {
20
+ archived: boolean;
21
+ since: number;
22
+ } | undefined): boolean;
12
23
  /**
13
24
  * The host's source of truth for sessions. Drivers push native events in;
14
25
  * the hub logs them with a per-session seq, keeps summaries current and fans
@@ -26,6 +37,9 @@ export declare class SessionHub {
26
37
  private readonly removedListeners;
27
38
  private readonly interactionListeners;
28
39
  private readonly agentListeners;
40
+ private readonly sideListeners;
41
+ private readonly sides;
42
+ private readonly idleTimers;
29
43
  private readonly auth;
30
44
  /** Terminals (`linkshell <agent>`) currently driving handoff sessions. */
31
45
  private readonly desktops;
@@ -54,6 +68,12 @@ export declare class SessionHub {
54
68
  private reconcileNativeSession;
55
69
  private hideMissingSession;
56
70
  stop(): Promise<void>;
71
+ onSide(listener: (sessionId: string, side: SideChat, closed?: boolean) => void): () => void;
72
+ side(sessionId: string, sideId: string | undefined, text: string): SideChat;
73
+ listSides(sessionId: string): SideChat[];
74
+ cancelSide(sessionId: string, sideId: string): void;
75
+ closeSide(sessionId: string, sideId: string): Promise<void>;
76
+ private sideTarget;
57
77
  agents(): AgentInfo[];
58
78
  onInteraction(listener: (agent: string, request: PendingPermissionSummary | {
59
79
  requestId: string;
@@ -306,6 +326,11 @@ export declare class SessionHub {
306
326
  /** Asked for every project each time the list is: remembered for a moment. */
307
327
  private branchOf;
308
328
  getSession(sessionId: string): SessionSummary;
329
+ /** A session and its agent's driver, unchecked: for what one driver offers itself (`mcp.*`), which says what it needs of it. */
330
+ sessionDriver(sessionId: string): {
331
+ session: SessionSummary;
332
+ driver: AgentDriver | undefined;
333
+ };
309
334
  /** Adds the live-only fields (current activity, first pending permission) to a stored summary. */
310
335
  /** Marks a session that works in one of LinkShell's worktrees. */
311
336
  private withWorktree;
@@ -313,7 +338,20 @@ export declare class SessionHub {
313
338
  private worktrees;
314
339
  private readonly pendingQuestions;
315
340
  private questionsOf;
341
+ /** Per session, the session it was forked from (null: none), read from the store once. */
342
+ private readonly forkOrigins;
343
+ /** Per session, the sessions whose lineage names it: their rows change when it comes or goes. */
344
+ private readonly related;
345
+ private forkOrigin;
346
+ /** Where the session came from, naming only sessions this host has: a list can't nest under one it doesn't show. */
347
+ private withLineage;
348
+ /** As the Claude app nests: a session still in use stands alone when the one it came from is archived. */
349
+ private firstKnown;
350
+ /** The session came, went or was (un)archived: the rows that name it as parent or origin say so now. */
351
+ private announceRelated;
316
352
  private decorate;
353
+ /** What a new session of `agent` could run on (see `agents.models`): nothing from a driver that can't tell before a session exists. */
354
+ agentModels(agent: string, cwd?: string): Promise<MethodResult<"agents.models">>;
317
355
  createSession(input: {
318
356
  agent: string;
319
357
  cwd: string;
@@ -387,6 +425,12 @@ export declare class SessionHub {
387
425
  /** What still holds at `seq` from the events before it: settings, an open turn, unanswered approvals. */
388
426
  private standingBefore;
389
427
  unsubscribe(sessionId: string, subscriber: Subscriber): void;
428
+ /**
429
+ * Detaches a session nobody has open, from a driver that pays to follow it, once it has been
430
+ * left alone a while with nothing under way. Without this every session opened since the host
431
+ * started stays followed for good.
432
+ */
433
+ private detachWhenIdle;
390
434
  prompt(sessionId: string, clientMessageId: string, content: ContentBlock[], whenBusy?: "queue"): Promise<"started" | "steered" | "queued" | "duplicate">;
391
435
  /** Retries attaching a session that just became joinable. */
392
436
  private attachWhenReady;
@@ -401,6 +445,9 @@ export declare class SessionHub {
401
445
  private sendHeld;
402
446
  onRemoved(listener: (sessionId: string) => void): () => void;
403
447
  archive(sessionId: string, archived: boolean): Promise<SessionSummary>;
448
+ /** Folds in the archive state of the agent's app that LinkShell only reads (see `mergedArchive`). */
449
+ private reconcileAppArchive;
450
+ private localArchive;
404
451
  rename(sessionId: string, title: string): Promise<SessionSummary>;
405
452
  /**
406
453
  * Deletes a session. A worktree only it used goes too when nothing in it
@@ -1,4 +1,4 @@
1
- import { RpcError, questionReplies, questionAnswerError, updateAsyncQuestions, sessionIdFor, } from "@linkshell/wire";
1
+ import { RpcError, questionReplies, readUserText, questionAnswerError, updateAsyncQuestions, sessionIdFor, } from "@linkshell/wire";
2
2
  import { inOrder } from "./drivers/acp/driver.js";
3
3
  import { canForkNatively } from "./drivers/behavior.js";
4
4
  import { homedir } from "node:os";
@@ -10,6 +10,7 @@ import { slimEvent } from "./slim.js";
10
10
  import { outputRange } from "./task-output.js";
11
11
  import { ComputerPreviews } from "./computer-preview.js";
12
12
  import { MacPreviewCapture } from "./computer-preview-capture.js";
13
+ import { SideChats } from "./side.js";
13
14
  /**
14
15
  * Whether an update is something happening in the session. Names, settings
15
16
  * and usage figures arrive when a session is merely opened or renamed, and
@@ -56,6 +57,24 @@ function worktreeLossMessage(state) {
56
57
  const RENAMED = "renamed";
57
58
  /** Driver-state key: the conversation a forked session's agent is still to be told, with its first message. */
58
59
  const CARRY = "carry";
60
+ /** Driver-state key: the session a session was forked from (made here, or as its agent recorded it). */
61
+ const FORKED_FROM = "forkedFrom";
62
+ /**
63
+ * Driver-state keys, for agents with an app LinkShell only reads (`appArchive`):
64
+ * LinkShell's own archive choice and when it was made, and the app's state as
65
+ * last seen and since when.
66
+ */
67
+ const LOCAL_ARCHIVE = "archive-local";
68
+ const APP_ARCHIVE = "archive-app";
69
+ /**
70
+ * Archived here or in the app, unless it was unarchived here after the app
71
+ * archived it (`local.at` 0: chosen before times were kept, so it never outranks the app).
72
+ */
73
+ export function mergedArchive(local, app) {
74
+ if (local.archived)
75
+ return true;
76
+ return !!app?.archived && !(local.at > app.since);
77
+ }
59
78
  /** A returning client catches up from where it was, unless it missed more than this. */
60
79
  const CATCH_UP = { events: 800, bytes: 4 * 1024 * 1024 };
61
80
  /** State a client needs from before its window: the latest of each applies. */
@@ -68,6 +87,8 @@ const STANDING = [
68
87
  "session_info_update",
69
88
  "ls_driver",
70
89
  ];
90
+ /** How long a session nobody has open stays attached to a driver that pays to follow it. */
91
+ const IDLE_DETACH_MS = 10 * 60_000;
71
92
  const PREVIEW_LENGTH = 160;
72
93
  const TITLE_LENGTH = 60;
73
94
  function compact(text, max) {
@@ -99,6 +120,9 @@ export class SessionHub {
99
120
  removedListeners = new Set();
100
121
  interactionListeners = new Set();
101
122
  agentListeners = new Set();
123
+ sideListeners = new Set();
124
+ sides;
125
+ idleTimers = new Map();
102
126
  auth = new Map();
103
127
  /** Terminals (`linkshell <agent>`) currently driving handoff sessions. */
104
128
  desktops = new Map();
@@ -166,6 +190,17 @@ export class SessionHub {
166
190
  this.emitSummary(stored);
167
191
  },
168
192
  removed: (agent, nativeId) => this.forget(sessionIdFor(agent, nativeId)),
193
+ nativeRecordsChanged: (agent, nativeIds) => {
194
+ for (const nativeId of nativeIds) {
195
+ const stored = this.store.getSession(sessionIdFor(agent, nativeId));
196
+ if (!stored)
197
+ continue;
198
+ const merged = this.reconcileAppArchive(stored);
199
+ this.emitSummary(merged);
200
+ if (merged.archived !== stored.archived)
201
+ this.announceRelated(merged.id);
202
+ }
203
+ },
169
204
  desktop: (agent, nativeId) => this.desktops.get(sessionIdFor(agent, nativeId)),
170
205
  state: (agent, nativeId) => {
171
206
  const sessionId = sessionIdFor(agent, nativeId);
@@ -174,6 +209,10 @@ export class SessionHub {
174
209
  set: (key, value) => this.store.setDriverState(sessionId, key, value),
175
210
  };
176
211
  },
212
+ agentState: (agent) => ({
213
+ get: (key) => this.store.getAgentState(agent, key),
214
+ set: (key, value) => this.store.setAgentState(agent, key, value),
215
+ }),
177
216
  log: (message) => this.log(message),
178
217
  };
179
218
  constructor(store, drivers, log = () => { },
@@ -195,6 +234,10 @@ export class SessionHub {
195
234
  });
196
235
  for (const driver of drivers)
197
236
  this.drivers.set(driver.id, driver);
237
+ this.sides = new SideChats((sessionId, side, closed) => {
238
+ for (const listener of this.sideListeners)
239
+ listener(sessionId, side, closed);
240
+ }, log);
198
241
  }
199
242
  async start(options = {}) {
200
243
  for (const session of this.store.errorSessions()) {
@@ -281,6 +324,7 @@ export class SessionHub {
281
324
  else if (availability === "available" && this.store.isNativeMissing(session.id)) {
282
325
  this.store.setNativeMissing(session.id, false);
283
326
  this.emitSummary(session);
327
+ this.announceRelated(session.id);
284
328
  }
285
329
  }
286
330
  hideMissingSession(session) {
@@ -295,14 +339,44 @@ export class SessionHub {
295
339
  driver?.watched?.(session.nativeId, false);
296
340
  this.live.delete(session.id);
297
341
  this.emitRemoved(session.id);
342
+ this.announceRelated(session.id);
298
343
  void driver?.detach(session.nativeId).catch((error) => this.log(`[hub] detach missing ${session.id}: ${String(error)}`));
299
344
  }
300
345
  async stop() {
301
346
  if (this.discoveryTimer)
302
347
  clearInterval(this.discoveryTimer);
348
+ for (const timer of this.idleTimers.values())
349
+ clearTimeout(timer);
350
+ this.idleTimers.clear();
351
+ await this.sides.stop((sessionId) => this.sideTarget(sessionId));
303
352
  await Promise.allSettled([...this.drivers.values()].map((driver) => driver.stop()));
304
353
  await this.previews.stop();
305
354
  }
355
+ // ── side conversations ────────────────────────────────────────────
356
+ onSide(listener) {
357
+ this.sideListeners.add(listener);
358
+ return () => this.sideListeners.delete(listener);
359
+ }
360
+ side(sessionId, sideId, text) {
361
+ const summary = this.getSession(sessionId);
362
+ return this.sides.ask(sessionId, { driver: this.requireDriver(summary.agent), nativeId: summary.nativeId, cwd: summary.cwd }, sideId, text);
363
+ }
364
+ listSides(sessionId) {
365
+ this.getSession(sessionId);
366
+ return this.sides.list(sessionId);
367
+ }
368
+ cancelSide(sessionId, sideId) {
369
+ this.sides.cancel(sessionId, sideId);
370
+ }
371
+ async closeSide(sessionId, sideId) {
372
+ const target = this.sideTarget(sessionId);
373
+ await this.sides.close(sessionId, sideId, target?.driver, target?.nativeId ?? "");
374
+ }
375
+ sideTarget(sessionId) {
376
+ const summary = this.store.getSession(sessionId);
377
+ const driver = summary && this.drivers.get(summary.agent);
378
+ return summary && driver ? { driver, nativeId: summary.nativeId } : undefined;
379
+ }
306
380
  agents() {
307
381
  return [...this.drivers.values()].map((driver) => {
308
382
  const status = driver.status();
@@ -386,6 +460,8 @@ export class SessionHub {
386
460
  await existing.configureAcp?.(services.settings.settings(id));
387
461
  await existing.stop();
388
462
  this.drivers.delete(id);
463
+ // What the old definition's agent reported (its models) needn't hold for the new one.
464
+ this.store.clearAgentState(id);
389
465
  }
390
466
  if (save && driver) {
391
467
  services.settings.saveAgent(save);
@@ -454,6 +530,11 @@ export class SessionHub {
454
530
  throw RpcError.app("not_found", "这个会话的原始记录已不存在,已从列表隐藏;本地缓存仍保留。");
455
531
  return this.decorate(summary);
456
532
  }
533
+ /** A session and its agent's driver, unchecked: for what one driver offers itself (`mcp.*`), which says what it needs of it. */
534
+ sessionDriver(sessionId) {
535
+ const session = this.getSession(sessionId);
536
+ return { session, driver: this.drivers.get(session.agent) };
537
+ }
457
538
  /** Adds the live-only fields (current activity, first pending permission) to a stored summary. */
458
539
  /** Marks a session that works in one of LinkShell's worktrees. */
459
540
  withWorktree(summary) {
@@ -473,8 +554,55 @@ export class SessionHub {
473
554
  }
474
555
  return questions;
475
556
  }
557
+ /** Per session, the session it was forked from (null: none), read from the store once. */
558
+ forkOrigins = new Map();
559
+ /** Per session, the sessions whose lineage names it: their rows change when it comes or goes. */
560
+ related = new Map();
561
+ forkOrigin(sessionId) {
562
+ let origin = this.forkOrigins.get(sessionId);
563
+ if (origin === undefined) {
564
+ origin = this.store.getDriverState(sessionId, FORKED_FROM) ?? null;
565
+ this.forkOrigins.set(sessionId, origin);
566
+ }
567
+ return origin ?? undefined;
568
+ }
569
+ /** Where the session came from, naming only sessions this host has: a list can't nest under one it doesn't show. */
570
+ withLineage(summary) {
571
+ const native = this.drivers.get(summary.agent)?.lineage?.(summary.nativeId);
572
+ const ids = (nativeIds) => nativeIds?.map((nativeId) => sessionIdFor(summary.agent, nativeId)) ?? [];
573
+ const origin = this.forkOrigin(summary.id);
574
+ const parentId = this.firstKnown(summary, ids(native?.parent));
575
+ const forkedFrom = this.firstKnown(summary, [...(origin ? [origin] : []), ...ids(native?.forkedFrom)]);
576
+ const appId = native?.appId;
577
+ if (!parentId && !forkedFrom && !appId)
578
+ return summary;
579
+ return { ...summary, ...(parentId ? { parentId } : {}), ...(forkedFrom ? { forkedFrom } : {}), ...(appId ? { appId } : {}) };
580
+ }
581
+ /** As the Claude app nests: a session still in use stands alone when the one it came from is archived. */
582
+ firstKnown(summary, candidates) {
583
+ let found;
584
+ for (const candidate of candidates) {
585
+ if (candidate === summary.id)
586
+ continue;
587
+ let children = this.related.get(candidate);
588
+ if (!children)
589
+ this.related.set(candidate, (children = new Set()));
590
+ children.add(summary.id);
591
+ if (found)
592
+ continue;
593
+ const other = this.store.getSession(candidate);
594
+ if (other && !(other.archived && !summary.archived) && !this.store.isNativeMissing(candidate))
595
+ found = candidate;
596
+ }
597
+ return found;
598
+ }
599
+ /** The session came, went or was (un)archived: the rows that name it as parent or origin say so now. */
600
+ announceRelated(sessionId) {
601
+ for (const child of this.related.get(sessionId) ?? [])
602
+ this.announce(child);
603
+ }
476
604
  decorate(summary) {
477
- summary = { ...summary, asyncQuestions: this.questionsOf(summary.id) };
605
+ summary = this.withLineage({ ...summary, asyncQuestions: this.questionsOf(summary.id) });
478
606
  const live = this.live.get(summary.id);
479
607
  if (!live)
480
608
  return this.withWorktree(summary);
@@ -522,6 +650,11 @@ export class SessionHub {
522
650
  }
523
651
  return decorated;
524
652
  }
653
+ /** What a new session of `agent` could run on (see `agents.models`): nothing from a driver that can't tell before a session exists. */
654
+ async agentModels(agent, cwd) {
655
+ const driver = this.requireDriver(agent);
656
+ return (await driver.listModels?.(cwd)) ?? { models: [] };
657
+ }
525
658
  async createSession(input) {
526
659
  const driver = this.requireDriver(input.agent);
527
660
  const worktree = input.worktree ? await this.newWorktree(input.cwd, promptLabel(input.prompt)) : undefined;
@@ -576,7 +709,7 @@ export class SessionHub {
576
709
  }
577
710
  if (!discovered)
578
711
  return await this.forkByReplay(summary, driver, cwd, cut);
579
- this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title });
712
+ this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title, forkedFrom: summary.nativeId });
580
713
  return this.getSession(sessionIdFor(driver.id, discovered.nativeId));
581
714
  }
582
715
  catch (error) {
@@ -594,7 +727,7 @@ export class SessionHub {
594
727
  const start = this.store.pageStart(summary.id, cut);
595
728
  const events = this.store.readEvents(summary.id, start, PAGE.maxEvents + 1, cut).filter((event) => isConversation(event.update));
596
729
  const discovered = await driver.createSession({ cwd });
597
- this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title });
730
+ this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title, forkedFrom: summary.nativeId });
598
731
  const forkId = sessionIdFor(driver.id, discovered.nativeId);
599
732
  await this.ensureAttached(forkId);
600
733
  const live = this.liveFor(forkId);
@@ -731,6 +864,8 @@ export class SessionHub {
731
864
  cursor = batch[batch.length - 1].seq;
732
865
  }
733
866
  live.subscribers.add(subscriber);
867
+ clearTimeout(this.idleTimers.get(sessionId));
868
+ this.idleTimers.delete(sessionId);
734
869
  if (live.subscribers.size === 1)
735
870
  driver?.watched?.(summary.nativeId, true);
736
871
  return { session: this.getSession(sessionId), startSeq };
@@ -962,6 +1097,38 @@ export class SessionHub {
962
1097
  const summary = this.store.getSession(sessionId);
963
1098
  if (summary)
964
1099
  this.drivers.get(summary.agent)?.watched?.(summary.nativeId, false);
1100
+ this.detachWhenIdle(sessionId);
1101
+ }
1102
+ /**
1103
+ * Detaches a session nobody has open, from a driver that pays to follow it, once it has been
1104
+ * left alone a while with nothing under way. Without this every session opened since the host
1105
+ * started stays followed for good.
1106
+ */
1107
+ detachWhenIdle(sessionId, delay = IDLE_DETACH_MS) {
1108
+ const summary = this.store.getSession(sessionId);
1109
+ const driver = summary && this.drivers.get(summary.agent);
1110
+ if (!summary || !driver?.detachWhenIdle)
1111
+ return;
1112
+ clearTimeout(this.idleTimers.get(sessionId));
1113
+ const timer = setTimeout(() => {
1114
+ this.idleTimers.delete(sessionId);
1115
+ const live = this.live.get(sessionId);
1116
+ if (!live || live.subscribers.size > 0 || !live.attached || live.attaching || live.detaching)
1117
+ return;
1118
+ const tasks = this.tasksOf(sessionId, live);
1119
+ const working = live.turnActive || live.permissions.size > 0 || !!live.queue?.length || live.held.length > 0
1120
+ || [...tasks.values()].some((task) => task.state === "running")
1121
+ || [...(live.subagents?.values() ?? [])].some((state) => subagentRunning(state));
1122
+ // Something is under way: looked at again later.
1123
+ if (working)
1124
+ return this.detachWhenIdle(sessionId, delay);
1125
+ live.attached = false;
1126
+ live.detaching = driver.detach(summary.nativeId)
1127
+ .catch((error) => this.log(`[hub] idle detach ${sessionId}: ${String(error)}`))
1128
+ .finally(() => { live.detaching = undefined; });
1129
+ }, delay);
1130
+ timer.unref?.();
1131
+ this.idleTimers.set(sessionId, timer);
965
1132
  }
966
1133
  async prompt(sessionId, clientMessageId, content, whenBusy) {
967
1134
  const summary = this.getSession(sessionId);
@@ -1112,10 +1279,40 @@ export class SessionHub {
1112
1279
  const driver = this.drivers.get(summary.agent);
1113
1280
  if (driver?.archive)
1114
1281
  await driver.archive(summary.nativeId, archived);
1115
- const updated = this.store.patchSession(sessionId, { archived });
1282
+ // Kept apart from the app's, with when: unarchiving here outranks an older archive there.
1283
+ if (driver?.appArchive)
1284
+ this.store.setDriverState(sessionId, LOCAL_ARCHIVE, JSON.stringify({ archived, at: Date.now() }));
1285
+ const updated = this.reconcileAppArchive(this.store.patchSession(sessionId, { archived }));
1116
1286
  this.emitSummary(updated);
1287
+ this.announceRelated(sessionId);
1117
1288
  return this.decorate(updated);
1118
1289
  }
1290
+ /** Folds in the archive state of the agent's app that LinkShell only reads (see `mergedArchive`). */
1291
+ reconcileAppArchive(summary) {
1292
+ const reported = this.drivers.get(summary.agent)?.appArchive?.(summary.nativeId);
1293
+ // Not in that app (or not read yet): the flag is LinkShell's alone, or stays as last merged.
1294
+ if (!reported)
1295
+ return summary;
1296
+ const saved = this.store.getDriverState(summary.id, APP_ARCHIVE);
1297
+ const seen = saved ? JSON.parse(saved) : undefined;
1298
+ if (!seen && !reported.archived)
1299
+ return summary;
1300
+ // Only a change counts as the app archiving it: the app rewrites its record for much else.
1301
+ const app = seen?.archived === reported.archived ? seen : { archived: reported.archived, since: reported.at };
1302
+ if (app !== seen)
1303
+ this.store.setDriverState(summary.id, APP_ARCHIVE, JSON.stringify(app));
1304
+ const archived = mergedArchive(this.localArchive(summary), app);
1305
+ return archived === summary.archived ? summary : this.store.patchSession(summary.id, { archived });
1306
+ }
1307
+ localArchive(summary) {
1308
+ const saved = this.store.getDriverState(summary.id, LOCAL_ARCHIVE);
1309
+ if (saved)
1310
+ return JSON.parse(saved);
1311
+ // Until the app's state is first merged in, the flag is LinkShell's own choice.
1312
+ const local = { archived: summary.archived, at: 0 };
1313
+ this.store.setDriverState(summary.id, LOCAL_ARCHIVE, JSON.stringify(local));
1314
+ return local;
1315
+ }
1119
1316
  async rename(sessionId, title) {
1120
1317
  const summary = this.getSession(sessionId);
1121
1318
  const name = compact(title, TITLE_LENGTH);
@@ -1170,10 +1367,15 @@ export class SessionHub {
1170
1367
  forget(sessionId) {
1171
1368
  if (!this.store.getSession(sessionId))
1172
1369
  return;
1370
+ clearTimeout(this.idleTimers.get(sessionId));
1371
+ this.idleTimers.delete(sessionId);
1173
1372
  this.live.delete(sessionId);
1174
1373
  this.previews.forget(sessionId);
1374
+ this.sides.forget(sessionId);
1175
1375
  this.store.removeSession(sessionId);
1376
+ this.forkOrigins.delete(sessionId);
1176
1377
  this.emitRemoved(sessionId);
1378
+ this.announceRelated(sessionId);
1177
1379
  }
1178
1380
  emitRemoved(sessionId) {
1179
1381
  for (const listener of this.removedListeners) {
@@ -1328,6 +1530,8 @@ export class SessionHub {
1328
1530
  /** Never throws synchronously: failures always arrive as a rejected promise. */
1329
1531
  ensureAttached(sessionId) {
1330
1532
  const live = this.liveFor(sessionId);
1533
+ if (live.detaching)
1534
+ return live.detaching.then(() => this.ensureAttached(sessionId));
1331
1535
  if (live.attached)
1332
1536
  return Promise.resolve();
1333
1537
  if (live.attaching)
@@ -1353,10 +1557,19 @@ export class SessionHub {
1353
1557
  // Items without a recorded time get the time of the one before, never "now".
1354
1558
  // (An agent that only says when a session last changed gives that as its creation too.)
1355
1559
  let ts = Math.min(summary.createdAt, history.find((item) => item.ts !== undefined)?.ts ?? summary.createdAt);
1356
- for (const item of history) {
1560
+ // The log only grows at its end. A message before what it already has is one an earlier
1561
+ // reading skipped (a reader that now shows more), not something that happened since:
1562
+ // added now it would land at the bottom, out of place, so it is only marked as seen.
1563
+ const logged = history.map((item) => this.store.isItemLogged(sessionId, item.itemId));
1564
+ const lastLogged = logged.lastIndexOf(true);
1565
+ for (const [index, item] of history.entries()) {
1357
1566
  ts = Math.min(Math.max(item.ts ?? ts, ts), summary.updatedAt);
1358
- if (this.store.isItemLogged(sessionId, item.itemId))
1567
+ if (logged[index])
1568
+ continue;
1569
+ if (index < lastLogged && item.updates.every((update) => update.sessionUpdate === "user_message_chunk")) {
1570
+ this.store.markItemLogged(sessionId, item.itemId);
1359
1571
  continue;
1572
+ }
1360
1573
  for (const update of item.updates)
1361
1574
  this.commit(sessionId, update, undefined, ts);
1362
1575
  this.store.markItemLogged(sessionId, item.itemId);
@@ -1422,9 +1635,9 @@ export class SessionHub {
1422
1635
  updatedAt: session.updatedAt,
1423
1636
  });
1424
1637
  const { created } = stored;
1425
- const summary = session.archived !== undefined && session.archived !== stored.summary.archived
1638
+ const summary = this.reconcileAppArchive(session.archived !== undefined && session.archived !== stored.summary.archived
1426
1639
  ? this.store.patchSession(stored.summary.id, { archived: session.archived })
1427
- : stored.summary;
1640
+ : stored.summary);
1428
1641
  const changed = !!before &&
1429
1642
  (before.title !== summary.title ||
1430
1643
  before.state !== summary.state ||
@@ -1439,8 +1652,16 @@ export class SessionHub {
1439
1652
  if (questionsChanged)
1440
1653
  this.store.setDriverState(summary.id, "asyncQuestions", JSON.stringify(session.asyncQuestions));
1441
1654
  }
1442
- if (created || changed || questionsChanged || restored)
1655
+ const origin = session.forkedFrom === undefined ? undefined : sessionIdFor(agent, session.forkedFrom);
1656
+ const forked = origin !== undefined && origin !== summary.id && origin !== this.forkOrigin(summary.id);
1657
+ if (forked) {
1658
+ this.store.setDriverState(summary.id, FORKED_FROM, origin);
1659
+ this.forkOrigins.set(summary.id, origin);
1660
+ }
1661
+ if (created || changed || questionsChanged || restored || forked)
1443
1662
  this.emitSummary(summary);
1663
+ if (created || restored || before?.archived !== summary.archived)
1664
+ this.announceRelated(summary.id);
1444
1665
  }
1445
1666
  ingest(sessionId, update, itemId, ts) {
1446
1667
  if (this.store.isNativeMissing(sessionId))
@@ -1517,8 +1738,9 @@ export class SessionHub {
1517
1738
  if (update.parentToolCallId || !update.content)
1518
1739
  break;
1519
1740
  const text = update.content.filter((block) => block.type === "text").map((block) => block.text).join("");
1520
- if (update.role === "user" && !before.title && text.trim())
1521
- patch.title = compact(text, TITLE_LENGTH);
1741
+ // A title is the person's words, not the context their client or Claude wrapped around them.
1742
+ if (update.role === "user" && !before.title && readUserText(text).text.trim())
1743
+ patch.title = compact(readUserText(text).text, TITLE_LENGTH);
1522
1744
  if (update.role === "agent" && text.trim())
1523
1745
  patch.preview = compact(text, PREVIEW_LENGTH);
1524
1746
  if (live.turnActive && update.role === "agent")
@@ -1526,7 +1748,7 @@ export class SessionHub {
1526
1748
  break;
1527
1749
  }
1528
1750
  case "user_message_chunk": {
1529
- const text = update.content.type === "text" ? update.content.text : "";
1751
+ const text = update.content.type === "text" ? readUserText(update.content.text).text : "";
1530
1752
  if (text && !before.title)
1531
1753
  patch.title = compact(text, TITLE_LENGTH);
1532
1754
  if (text && !before.preview)