@berthdev/berth 0.3.5 → 0.4.2

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 (136) hide show
  1. package/README.md +7 -1
  2. package/dist/src/agents/capabilities.d.ts +13 -0
  3. package/dist/src/agents/capabilities.js +52 -0
  4. package/dist/src/agents/capabilities.js.map +1 -0
  5. package/dist/src/agents/codex-binding.d.ts +10 -0
  6. package/dist/src/agents/codex-binding.js +105 -0
  7. package/dist/src/agents/codex-binding.js.map +1 -0
  8. package/dist/src/agents/commands.d.ts +4 -2
  9. package/dist/src/agents/commands.js +41 -9
  10. package/dist/src/agents/commands.js.map +1 -1
  11. package/dist/src/agents/copilot-state.d.ts +3 -0
  12. package/dist/src/agents/copilot-state.js +94 -0
  13. package/dist/src/agents/copilot-state.js.map +1 -0
  14. package/dist/src/agents/gemini-state.d.ts +3 -0
  15. package/dist/src/agents/gemini-state.js +41 -0
  16. package/dist/src/agents/gemini-state.js.map +1 -0
  17. package/dist/src/agents/hook-files.d.ts +1 -3
  18. package/dist/src/agents/hook-files.js +69 -53
  19. package/dist/src/agents/hook-files.js.map +1 -1
  20. package/dist/src/agents/hook-health.d.ts +4 -0
  21. package/dist/src/agents/hook-health.js +95 -0
  22. package/dist/src/agents/hook-health.js.map +1 -0
  23. package/dist/src/agents/hooks.d.ts +4 -1
  24. package/dist/src/agents/hooks.js +155 -22
  25. package/dist/src/agents/hooks.js.map +1 -1
  26. package/dist/src/agents/jsonl-titles.d.ts +8 -0
  27. package/dist/src/agents/jsonl-titles.js +124 -0
  28. package/dist/src/agents/jsonl-titles.js.map +1 -0
  29. package/dist/src/agents/late-binding.d.ts +2 -0
  30. package/dist/src/agents/late-binding.js +25 -0
  31. package/dist/src/agents/late-binding.js.map +1 -0
  32. package/dist/src/agents/profile.d.ts +14 -0
  33. package/dist/src/agents/profile.js +75 -0
  34. package/dist/src/agents/profile.js.map +1 -0
  35. package/dist/src/agents/registry.d.ts +12 -3
  36. package/dist/src/agents/registry.js +52 -19
  37. package/dist/src/agents/registry.js.map +1 -1
  38. package/dist/src/agents/roots.d.ts +2 -0
  39. package/dist/src/agents/roots.js +21 -0
  40. package/dist/src/agents/roots.js.map +1 -0
  41. package/dist/src/agents/titles.d.ts +14 -0
  42. package/dist/src/agents/titles.js +82 -0
  43. package/dist/src/agents/titles.js.map +1 -0
  44. package/dist/src/agents/transcript-cursor.d.ts +9 -0
  45. package/dist/src/agents/transcript-cursor.js +86 -0
  46. package/dist/src/agents/transcript-cursor.js.map +1 -0
  47. package/dist/src/cli/main.js +40 -7
  48. package/dist/src/cli/main.js.map +1 -1
  49. package/dist/src/config/index.d.ts +6 -0
  50. package/dist/src/config/index.js +37 -6
  51. package/dist/src/config/index.js.map +1 -1
  52. package/dist/src/config/write.d.ts +2 -0
  53. package/dist/src/config/write.js +41 -0
  54. package/dist/src/config/write.js.map +1 -0
  55. package/dist/src/context.js +1 -1
  56. package/dist/src/context.js.map +1 -1
  57. package/dist/src/index.d.ts +11 -5
  58. package/dist/src/index.js +10 -4
  59. package/dist/src/index.js.map +1 -1
  60. package/dist/src/iterm2/install.d.ts +2 -0
  61. package/dist/src/iterm2/install.js +37 -12
  62. package/dist/src/iterm2/install.js.map +1 -1
  63. package/dist/src/present/attach.js +17 -5
  64. package/dist/src/present/attach.js.map +1 -1
  65. package/dist/src/present/state.d.ts +3 -0
  66. package/dist/src/present/state.js +12 -8
  67. package/dist/src/present/state.js.map +1 -1
  68. package/dist/src/process-arguments.d.ts +2 -0
  69. package/dist/src/process-arguments.js +59 -0
  70. package/dist/src/process-arguments.js.map +1 -0
  71. package/dist/src/process-directory.d.ts +2 -0
  72. package/dist/src/process-directory.js +37 -0
  73. package/dist/src/process-directory.js.map +1 -0
  74. package/dist/src/process-owner.d.ts +7 -0
  75. package/dist/src/process-owner.js +51 -0
  76. package/dist/src/process-owner.js.map +1 -0
  77. package/dist/src/registry/directory.d.ts +2 -0
  78. package/dist/src/registry/directory.js +19 -0
  79. package/dist/src/registry/directory.js.map +1 -0
  80. package/dist/src/registry/discover.d.ts +15 -5
  81. package/dist/src/registry/discover.js +135 -38
  82. package/dist/src/registry/discover.js.map +1 -1
  83. package/dist/src/registry/doctor.d.ts +5 -1
  84. package/dist/src/registry/doctor.js +53 -12
  85. package/dist/src/registry/doctor.js.map +1 -1
  86. package/dist/src/registry/hosts.d.ts +28 -0
  87. package/dist/src/registry/hosts.js +73 -0
  88. package/dist/src/registry/hosts.js.map +1 -0
  89. package/dist/src/registry/launch.d.ts +3 -0
  90. package/dist/src/registry/launch.js +29 -0
  91. package/dist/src/registry/launch.js.map +1 -0
  92. package/dist/src/registry/native-state.d.ts +20 -0
  93. package/dist/src/registry/native-state.js +127 -0
  94. package/dist/src/registry/native-state.js.map +1 -0
  95. package/dist/src/registry/sessions.d.ts +21 -3
  96. package/dist/src/registry/sessions.js +178 -37
  97. package/dist/src/registry/sessions.js.map +1 -1
  98. package/dist/src/restore/ensure.d.ts +12 -2
  99. package/dist/src/restore/ensure.js +171 -37
  100. package/dist/src/restore/ensure.js.map +1 -1
  101. package/dist/src/restore/lock.d.ts +3 -5
  102. package/dist/src/restore/lock.js +82 -52
  103. package/dist/src/restore/lock.js.map +1 -1
  104. package/dist/src/restore/status.d.ts +9 -0
  105. package/dist/src/restore/status.js +16 -0
  106. package/dist/src/restore/status.js.map +1 -0
  107. package/dist/src/snapshot/identity.d.ts +7 -0
  108. package/dist/src/snapshot/identity.js +20 -0
  109. package/dist/src/snapshot/identity.js.map +1 -0
  110. package/dist/src/snapshot/lifecycle.d.ts +9 -0
  111. package/dist/src/snapshot/lifecycle.js +37 -0
  112. package/dist/src/snapshot/lifecycle.js.map +1 -0
  113. package/dist/src/snapshot/store.d.ts +11 -1
  114. package/dist/src/snapshot/store.js +28 -9
  115. package/dist/src/snapshot/store.js.map +1 -1
  116. package/dist/src/snapshot/take.d.ts +2 -1
  117. package/dist/src/snapshot/take.js +29 -25
  118. package/dist/src/snapshot/take.js.map +1 -1
  119. package/dist/src/tmux/client.d.ts +6 -2
  120. package/dist/src/tmux/client.js +49 -25
  121. package/dist/src/tmux/client.js.map +1 -1
  122. package/dist/src/tmux/connections.d.ts +22 -0
  123. package/dist/src/tmux/connections.js +70 -0
  124. package/dist/src/tmux/connections.js.map +1 -0
  125. package/dist/src/tmux/fields.d.ts +7 -0
  126. package/dist/src/tmux/fields.js +23 -0
  127. package/dist/src/tmux/fields.js.map +1 -0
  128. package/dist/src/tmux/install.js +31 -18
  129. package/dist/src/tmux/install.js.map +1 -1
  130. package/dist/src/tools.d.ts +3 -1
  131. package/dist/src/tools.js +23 -11
  132. package/dist/src/tools.js.map +1 -1
  133. package/package.json +1 -1
  134. package/dist/src/agents/name-key.d.ts +0 -1
  135. package/dist/src/agents/name-key.js +0 -66
  136. package/dist/src/agents/name-key.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,IAAI,EAAsC,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAgB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAmB,MAAM,mBAAmB,CAAC;AAClI,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAmB,MAAM,wBAAwB,CAAC;AAC7H,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAuC,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAsB,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAc,MAAM,qBAAqB,CAAC;AAC5H,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,IAAI,EAAsC,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAgB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,EAAmB,MAAM,mBAAmB,CAAC;AACxK,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAmB,MAAM,wBAAwB,CAAC;AACrJ,OAAO,EAAE,MAAM,EAAgB,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAuC,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAsB,MAAM,qBAAqB,CAAC;AACvJ,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAc,MAAM,qBAAqB,CAAC;AAC5H,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,eAAe,EAAuB,MAAM,uBAAuB,CAAC;AAEpI,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAA0B,MAAM,qBAAqB,CAAC;AAEnH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAqB,MAAM,qBAAqB,CAAC;AAEjJ,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
@@ -16,6 +16,8 @@ export interface Tools {
16
16
  cli: string;
17
17
  tmux: string;
18
18
  socket?: string;
19
+ socketPath?: string;
20
+ env?: Record<string, string>;
19
21
  /** PATH at install time: AutoLaunch scripts get a minimal one, without tmux or the agents. */
20
22
  path?: string;
21
23
  }
@@ -17,7 +17,7 @@ export function attachCommand(ctx, tools, session) {
17
17
  const template = ctx.config.iterm2Attach;
18
18
  if (template)
19
19
  return renderTemplate(template, { session });
20
- const socket = tools.socket ? ` -L ${shellQuote(tools.socket)}` : '';
20
+ const socket = tools.socketPath ? ` -S ${shellQuote(tools.socketPath)}` : tools.socket ? ` -L ${shellQuote(tools.socket)}` : '';
21
21
  return `${shellQuote(tools.tmux)}${socket} attach-session -t ${shellQuote(`=${session}`)}`;
22
22
  }
23
23
  /** `berth iterm2 reattach <session>`: rebuild the session if missing, return its attach command. */
@@ -29,7 +29,7 @@ export function reattach(ctx, tools, session) {
29
29
  return attachCommand(ctx, tools, session);
30
30
  }
31
31
  export function autoLaunchScript(tools) {
32
- const berth = JSON.stringify([tools.node, tools.cli, ...(tools.socket ? ['--socket', tools.socket] : [])]);
32
+ const berth = JSON.stringify([tools.node, tools.cli, ...(tools.socketPath ? ['--socket-path', tools.socketPath] : tools.socket ? ['--socket', tools.socket] : [])]);
33
33
  return `#!/usr/bin/env python3
34
34
  """Berth for iTerm2: put each pane iTerm2 restored back in its agent session.
35
35
 
@@ -48,7 +48,8 @@ import iterm2
48
48
  BERTH = ${berth}
49
49
  VARIABLE = "user.${PANE_VARIABLE}"
50
50
  # Recorded at install: iTerm2's PATH for AutoLaunch has neither tmux nor the agent CLIs.
51
- ENV = dict(os.environ, PATH=${JSON.stringify(tools.path ?? '')} or os.environ.get("PATH", ""))
51
+ ENV = dict(os.environ, **${JSON.stringify(tools.env ?? {})})
52
+ ENV["PATH"] = ${JSON.stringify(tools.path ?? '')} or os.environ.get("PATH", "")
52
53
 
53
54
 
54
55
  def attach_command(session_name):
@@ -67,6 +68,38 @@ async def panes(app):
67
68
  yield session
68
69
 
69
70
 
71
+ async def ready_prompt(connection, session, name):
72
+ if await session.async_get_variable(VARIABLE) != name:
73
+ return None
74
+ job = await session.async_get_variable("jobName")
75
+ if os.path.basename(job or "").lstrip("-") not in {"sh", "bash", "zsh", "fish", "tcsh", "xonsh"}:
76
+ return None
77
+ prompt = await iterm2.async_get_last_prompt(connection, session.session_id)
78
+ if prompt and prompt.state == iterm2.PromptState.EDITING and prompt.command == "":
79
+ return prompt.unique_id
80
+ return None
81
+
82
+
83
+ async def restore_pane(connection, session, name):
84
+ try:
85
+ async with iterm2.Transaction(connection):
86
+ prompt_id = await ready_prompt(connection, session, name)
87
+ if not prompt_id:
88
+ print("Berth: reattach deferred for %s; use berth iterm2 reattach from a ready shell." % name)
89
+ return
90
+ command = await asyncio.get_running_loop().run_in_executor(None, attach_command, name)
91
+ if not command:
92
+ print("Berth: could not restore %s; inspect berth doctor." % name)
93
+ return
94
+ async with iterm2.Transaction(connection):
95
+ if await ready_prompt(connection, session, name) == prompt_id:
96
+ await session.async_send_text(command + "\\n")
97
+ else:
98
+ print("Berth: pane changed while restoring %s; automatic attachment deferred." % name)
99
+ except (AttributeError, iterm2.RPCException) as error:
100
+ print("Berth: cannot verify shell readiness for %s: %s" % (name, error))
101
+
102
+
70
103
  async def main(connection):
71
104
  app = await iterm2.async_get_app(connection)
72
105
  # Window restoration may still be running when AutoLaunch starts.
@@ -79,15 +112,7 @@ async def main(connection):
79
112
  name = await session.async_get_variable(VARIABLE)
80
113
  if not name:
81
114
  continue
82
- job = await session.async_get_variable("jobName")
83
- if job == "tmux":
84
- continue # still attached (iTerm2 kept the process alive)
85
- command = await asyncio.get_running_loop().run_in_executor(None, attach_command, name)
86
- if command:
87
- await session.async_send_text(command + "\\n")
88
- else:
89
- # Shown on the screen only, never run by the shell.
90
- await session.async_inject(("berth: could not restore session %s\\r\\n" % name).encode())
115
+ await restore_pane(connection, session, name)
91
116
 
92
117
 
93
118
  iterm2.run_until_complete(main)
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/iterm2/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAerD,MAAM,UAAU,WAAW,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,CAAC,CAAC;IACrF,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,CAAC;QACvD,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,iBAAiB,EAAE,YAAY,CAAC;KAC5D,CAAC;AACJ,CAAC;AAWD,0FAA0F;AAC1F,MAAM,UAAU,aAAa,CAAC,GAAY,EAAE,KAAY,EAAE,OAAe;IACvE,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC;IACzC,IAAI,QAAQ;QAAE,OAAO,cAAc,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,sBAAsB,UAAU,CAAC,IAAI,OAAO,EAAE,CAAC,EAAE,CAAC;AAC7F,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,QAAQ,CAAC,GAAY,EAAE,KAAY,EAAE,OAAe;IAClE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,MAAM,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,0BAA0B,OAAO,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChH,OAAO,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAY;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3G,OAAO;;;;;wBAKe,aAAa;;;;;;;;;;UAU3B,KAAK;mBACI,aAAa;;8BAEF,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2C7D,CAAC;AACF,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAY,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG;IAC3D,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG;IAC/C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG;IAC/C,OAAO,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/iterm2/install.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
+ import { withLock } from '../restore/lock.js';
2
+ import { createHash } from 'node:crypto';
3
+ import { profileEnvironment, readProfile } from '../agents/profile.js';
1
4
  import { appendFileSync } from 'node:fs';
2
5
  import { stamp } from '../registry/sessions.js';
3
- import { isIterm2, presentState } from './state.js';
6
+ import { clientsOf, isIterm2, presentState } from './state.js';
4
7
  import { hookHint, hookStatus } from '../agents/hooks.js';
5
8
  import { adoptDiscovered } from '../snapshot/take.js';
6
9
  /** iTerm2 user variable naming the session a pane shows (spec §7.3). */
@@ -15,6 +18,13 @@ export function setUserVar(name, value) {
15
18
  * client is shown the session's current state.
16
19
  */
17
20
  export function clientAttached(ctx, client) {
21
+ withLock(ctx.config.stateDir, () => attachCurrentClient(ctx, client), 3000);
22
+ }
23
+ function attachCurrentClient(ctx, event) {
24
+ const current = clientsOf(ctx.tmux, event.session).find((client) => client.tty === event.tty);
25
+ if (!current)
26
+ return;
27
+ const client = { ...current, session: event.session };
18
28
  adoptDiscovered(ctx);
19
29
  const live = ctx.tmux.listSessions().find((s) => s.name === client.session);
20
30
  if (!live?.meta.agent)
@@ -31,17 +41,19 @@ export function clientAttached(ctx, client) {
31
41
  }
32
42
  presentState(ctx.tmux, client.session, live.meta.state ?? '', { note: live.meta.note, clients: [{ tty: client.tty, termtype: client.termtype }] });
33
43
  if (live.meta.origin !== 'vscode')
34
- hintHooksOnce(ctx, live.meta.agent, client.tty);
44
+ hintHooksOnce(ctx, live.meta.agent, client.tty, live.meta.profile);
35
45
  }
36
46
  const HINT_SECONDS = 10;
37
47
  /**
38
48
  * Terminals other than VS Code (which asks in its own UI) get the hooks hint
39
49
  * in tmux's status line, once per agent per tmux server.
40
50
  */
41
- function hintHooksOnce(ctx, agent, tty) {
42
- if (hookStatus(agent, ctx.env) !== 'missing')
51
+ function hintHooksOnce(ctx, agent, tty, rawProfile) {
52
+ const profile = readProfile(agent, rawProfile);
53
+ if (!profile || hookStatus(agent, profileEnvironment(agent, profile, ctx.env)) !== 'missing')
43
54
  return;
44
- const flag = `@berth_hinted_${agent}`;
55
+ const key = createHash('sha256').update(JSON.stringify(profile.environment)).digest('hex').slice(0, 12);
56
+ const flag = `@berth_hinted_${agent}_${key}`;
45
57
  if (ctx.tmux.run(['show-option', '-gqv', flag], true).stdout.trim())
46
58
  return;
47
59
  ctx.tmux.run(['set-option', '-g', flag, '1'], true);
@@ -1 +1 @@
1
- {"version":3,"file":"attach.js","sourceRoot":"","sources":["../../../src/present/attach.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attach.js","sourceRoot":"","sources":["../../../src/present/attach.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { Context } from '../context.js';
1
2
  import type { Tmux } from '../tmux/client.js';
2
3
  export declare function isIterm2(termtype: string): boolean;
3
4
  export declare function sequencesFor(state: string, session: string, iterm2: boolean, note?: string): string;
@@ -12,3 +13,5 @@ export interface PresentOptions {
12
13
  }
13
14
  /** Write the state to each attached terminal; failures are ignored (best effort). */
14
15
  export declare function presentState(tmux: Tmux, session: string, state: string, options?: PresentOptions): number;
16
+ /** Attachment must not replay an inventory state older than a hook or acknowledgement. */
17
+ export declare function presentCurrentState(ctx: Context, session: string): void;
@@ -1,3 +1,5 @@
1
+ import { withLock } from '../restore/lock.js';
2
+ import { formatFields, readFields } from '../tmux/fields.js';
1
3
  import { appendFileSync } from 'node:fs';
2
4
  /**
3
5
  * Show a session's agent state on every terminal attached to it (spec §6, §7.4).
@@ -34,16 +36,10 @@ export function sequencesFor(state, session, iterm2, note = '') {
34
36
  return `${OSC}9;4;${PROGRESS[state] ?? '0'};0${ST}${OSC}2;${glyph ? `${glyph} ` : ''}${session}${ST}`;
35
37
  }
36
38
  export function clientsOf(tmux, session) {
37
- const result = tmux.run(['list-clients', '-t', `=${session}`, '-F', '#{client_tty}\t#{client_termtype}'], true);
39
+ const result = tmux.run(['list-clients', '-t', `=${session}`, '-F', formatFields(['client_tty', 'client_termtype'])], true);
38
40
  if (!result.ok)
39
41
  return [];
40
- return result.stdout
41
- .split('\n')
42
- .filter(Boolean)
43
- .map((line) => {
44
- const [tty, termtype = ''] = line.split('\t');
45
- return { tty, termtype };
46
- });
42
+ return readFields(result.stdout, 2).map(([tty, termtype]) => ({ tty, termtype }));
47
43
  }
48
44
  /** Write the state to each attached terminal; failures are ignored (best effort). */
49
45
  export function presentState(tmux, session, state, options = {}) {
@@ -60,4 +56,12 @@ export function presentState(tmux, session, state, options = {}) {
60
56
  }
61
57
  return written;
62
58
  }
59
+ /** Attachment must not replay an inventory state older than a hook or acknowledgement. */
60
+ export function presentCurrentState(ctx, session) {
61
+ withLock(ctx.config.stateDir, () => {
62
+ const live = ctx.tmux.listSessions().find((row) => row.name === session);
63
+ if (live)
64
+ presentState(ctx.tmux, session, live.meta.state ?? '', { note: live.meta.note });
65
+ }, 3000);
66
+ }
63
67
  //# sourceMappingURL=state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/present/state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/present/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Process display strings are not a shell command and cannot recover word boundaries. */
2
+ export declare function processArguments(pid: number): string[] | undefined;
@@ -0,0 +1,59 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ const MAX_BYTES = 16 * 1024 * 1024;
4
+ // Darwin: SYS___sysctl=202, CTL_KERN=1, KERN_PROCARGS2=49.
5
+ // Keep the bridge inline so the CLI and extension bundle share the same reader.
6
+ // It returns argc argv entries only; environment strings never leave this process.
7
+ // XNU exec_extract_strings aligns the saved executable path to 8 bytes for
8
+ // supported 64-bit macOS processes. kern_procargs2 removes the 16-byte
9
+ // executable_path= prefix and prepends argc (4 bytes). Do not skip NULs: they
10
+ // may be empty argv entries, and skipping them would expose environment data.
11
+ const DARWIN_ARGV = String.raw `use strict;
12
+ use warnings;
13
+ my $pid = shift @ARGV;
14
+ my $mib = pack('i3', 1, 49, $pid);
15
+ my $size = pack('Q', 0);
16
+ syscall(202, $mib, 3, 0, $size, 0, 0) == 0 or die "size";
17
+ my $length = unpack('Q', $size);
18
+ $length > 4 && $length <= 16777216 or die "bound";
19
+ my $buf = "\0" x $length;
20
+ syscall(202, $mib, 3, $buf, $size, 0, 0) == 0 or die "read";
21
+ $buf = substr($buf, 0, unpack('Q', $size));
22
+ my $argc = unpack('i', $buf);
23
+ my $path_end = index($buf, "\0", 4);
24
+ $path_end >= 4 && $argc > 0 && $argc <= 65536 or die "header";
25
+ my $start = 4 + int(($path_end - 4 + 1 + 7) / 8) * 8;
26
+ $start < length($buf) or die "alignment";
27
+ substr($buf, $path_end, $start - $path_end) !~ /[^\0]/ or die "padding";
28
+ my $end = $start;
29
+ for (1 .. $argc) { $end = index($buf, "\0", $end); $end >= 0 or die "truncated"; $end++; }
30
+ print substr($buf, $start, $end - $start);
31
+ `;
32
+ /** Process display strings are not a shell command and cannot recover word boundaries. */
33
+ export function processArguments(pid) {
34
+ if (!Number.isSafeInteger(pid) || pid < 1 || pid > 0x7fffffff)
35
+ return undefined;
36
+ try {
37
+ let bytes;
38
+ if (process.platform === 'linux')
39
+ bytes = readFileSync(`/proc/${pid}/cmdline`);
40
+ else if (process.platform === 'darwin') {
41
+ const result = spawnSync('/usr/bin/perl', ['-e', DARWIN_ARGV, String(pid)], {
42
+ timeout: 1000, killSignal: 'SIGKILL', maxBuffer: MAX_BYTES, encoding: 'buffer',
43
+ });
44
+ if (result.status !== 0 || result.error)
45
+ return undefined;
46
+ bytes = result.stdout;
47
+ }
48
+ else
49
+ return undefined;
50
+ if (!bytes.length || bytes.length > MAX_BYTES || bytes.at(-1) !== 0)
51
+ return undefined;
52
+ const words = new TextDecoder('utf-8', { fatal: true }).decode(bytes).slice(0, -1).split('\0');
53
+ return words[0] ? words : undefined;
54
+ }
55
+ catch {
56
+ return undefined;
57
+ }
58
+ }
59
+ //# sourceMappingURL=process-arguments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-arguments.js","sourceRoot":"","sources":["../../src/process-arguments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACnC,2DAA2D;AAC3D,gFAAgF;AAChF,mFAAmF;AACnF,2EAA2E;AAC3E,uEAAuE;AACvE,8EAA8E;AAC9E,8EAA8E;AAC9E,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;CAoB7B,CAAC;AAEF,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,UAAU;QAAE,OAAO,SAAS,CAAC;IAChF,IAAI,CAAC;QACH,IAAI,KAAa,CAAC;QAClB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;YAAE,KAAK,GAAG,YAAY,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC;aAC1E,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,SAAS,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE;gBAC1E,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ;aAC/E,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;YAC1D,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;QACxB,CAAC;;YAAM,OAAO,SAAS,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACtF,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/F,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,SAAS,CAAC;IAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** Read directory identity, never a terminal-escaped rendering of its name. */
2
+ export declare function processDirectory(pid: number): string | undefined;
@@ -0,0 +1,37 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readlinkSync } from 'node:fs';
3
+ // Darwin proc_info(336), PROC_INFO_CALL_PIDINFO(2), PROC_PIDVNODEPATHINFO(9).
4
+ // proc_vnodepathinfo contains two vnode_info_path structures; each ends with
5
+ // vip_path[MAXPATHLEN=1024]. Derive the offset from the returned structure size.
6
+ const DARWIN_CWD = String.raw `
7
+ use strict;
8
+ use warnings;
9
+ my $pid = int(shift @ARGV);
10
+ my $buffer = "\0" x 16384;
11
+ my $size = syscall(336, 2, $pid, 9, 0, $buffer, 16384);
12
+ $size > 2048 && $size <= 16384 && $size % 2 == 0 or die "process directory unavailable";
13
+ my $path = substr($buffer, $size / 2 - 1024, 1024);
14
+ my $end = index($path, "\0");
15
+ $end > 0 or die "invalid directory";
16
+ print substr($path, 0, $end);
17
+ `;
18
+ /** Read directory identity, never a terminal-escaped rendering of its name. */
19
+ export function processDirectory(pid) {
20
+ if (!Number.isSafeInteger(pid) || pid < 1 || pid > 0x7fffffff)
21
+ return undefined;
22
+ try {
23
+ if (process.platform === 'linux')
24
+ return readlinkSync(`/proc/${pid}/cwd`);
25
+ if (process.platform !== 'darwin')
26
+ return undefined;
27
+ const result = spawnSync('/usr/bin/perl', ['-e', DARWIN_CWD, String(pid)], { timeout: 1000, killSignal: 'SIGKILL', maxBuffer: 16384 });
28
+ if (result.status !== 0 || result.error || !result.stdout.length)
29
+ return undefined;
30
+ const path = new TextDecoder('utf-8', { fatal: true }).decode(result.stdout);
31
+ return path.startsWith('/') ? path : undefined;
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ //# sourceMappingURL=process-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-directory.js","sourceRoot":"","sources":["../../src/process-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,8EAA8E;AAC9E,6EAA6E;AAC7E,iFAAiF;AACjF,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;CAW5B,CAAC;AAEF,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,UAAU;QAAE,OAAO,SAAS,CAAC;IAChF,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;YAAE,OAAO,YAAY,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;QAC1E,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACpD,MAAM,MAAM,GAAG,SAAS,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACvI,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QACnF,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,SAAS,CAAC;IAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface ProcessLifetime {
2
+ key: string;
3
+ bornAt: number;
4
+ }
5
+ /** Process birth plus boot identity, not PID existence alone. */
6
+ export declare function processLifetime(pid: number): ProcessLifetime | undefined;
7
+ export declare function processOwnerAlive(pid: number, lifetime: string | undefined, recordedAt: number): boolean;
@@ -0,0 +1,51 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ let own;
4
+ let boot;
5
+ function bootIdentity() {
6
+ if (boot)
7
+ return boot;
8
+ if (process.platform === 'linux')
9
+ boot = readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim();
10
+ else {
11
+ const result = spawnSync('sysctl', ['-n', 'kern.boottime'], { encoding: 'utf8', timeout: 1000, killSignal: 'SIGKILL' });
12
+ if (result.status !== 0)
13
+ throw new Error('Cannot verify machine boot identity.');
14
+ boot = result.stdout.trim();
15
+ }
16
+ return boot;
17
+ }
18
+ /** Process birth plus boot identity, not PID existence alone. */
19
+ export function processLifetime(pid) {
20
+ if (!Number.isSafeInteger(pid) || pid <= 0)
21
+ return undefined;
22
+ if (pid === process.pid && own)
23
+ return own;
24
+ const result = spawnSync('ps', ['-p', String(pid), '-o', 'lstart='], {
25
+ env: { ...process.env, LC_ALL: 'C' }, encoding: 'utf8', timeout: 1000, killSignal: 'SIGKILL',
26
+ });
27
+ if (result.status !== 0 || !result.stdout.trim()) {
28
+ try {
29
+ process.kill(pid, 0);
30
+ }
31
+ catch (error) {
32
+ if (error.code === 'ESRCH')
33
+ return undefined;
34
+ }
35
+ throw new Error(`Cannot verify process lifetime for ${pid}.`);
36
+ }
37
+ const bornAt = Date.parse(result.stdout.trim());
38
+ if (!Number.isFinite(bornAt))
39
+ throw new Error(`Invalid process lifetime for ${pid}.`);
40
+ const lifetime = { key: `${bootIdentity()}:${pid}:${bornAt}`, bornAt };
41
+ if (pid === process.pid)
42
+ own = lifetime;
43
+ return lifetime;
44
+ }
45
+ export function processOwnerAlive(pid, lifetime, recordedAt) {
46
+ const current = processLifetime(pid);
47
+ if (!current)
48
+ return false;
49
+ return lifetime ? current.key === lifetime : current.bornAt <= recordedAt;
50
+ }
51
+ //# sourceMappingURL=process-owner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-owner.js","sourceRoot":"","sources":["../../src/process-owner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,IAAI,GAAgC,CAAC;AACrC,IAAI,IAAwB,CAAC;AAC7B,SAAS,YAAY;IACnB,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,IAAI,GAAG,YAAY,CAAC,iCAAiC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;SACnG,CAAC;QACJ,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QACxH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACjF,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7D,IAAI,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAC3C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE;QACnE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS;KAC7F,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACjD,IAAI,CAAC;YAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAC3C,IAAK,KAA+B,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO,SAAS,CAAC;QAC1E,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,GAAG,CAAC,CAAC;IACtF,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,YAAY,EAAE,IAAI,GAAG,IAAI,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC;IACvE,IAAI,GAAG,KAAK,OAAO,CAAC,GAAG;QAAE,GAAG,GAAG,QAAQ,CAAC;IACxC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,QAA4B,EAAE,UAAkB;IAC7F,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,OAAO,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function usableDirectory(path: string): boolean;
2
+ export declare function sameDirectory(left: string, right: string): boolean;
@@ -0,0 +1,19 @@
1
+ import { accessSync, constants, realpathSync, statSync } from 'node:fs';
2
+ export function usableDirectory(path) {
3
+ try {
4
+ accessSync(path, constants.X_OK);
5
+ return statSync(path).isDirectory();
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ export function sameDirectory(left, right) {
12
+ try {
13
+ return realpathSync(left) === realpathSync(right);
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ //# sourceMappingURL=directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directory.js","sourceRoot":"","sources":["../../../src/registry/directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExE,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC;QAAC,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAAC,CAAC;IAC9E,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AACzB,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAa;IACvD,IAAI,CAAC;QAAC,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,YAAY,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;IAC1D,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AACzB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import type { Context } from '../context.js';
2
2
  /**
3
- * Discovery (spec §4.3): a session nobody stamped is recognised as an agent
4
- * session from the processes in its active pane, so any launcher - or none -
3
+ * Discovery (spec §4.3): an unbound session or managed shell is recognised as
4
+ * an agent session from its active pane, so any launcher - or none -
5
5
  * gets its sessions managed without knowing Berth.
6
6
  */
7
7
  export interface Detected {
@@ -9,19 +9,30 @@ export interface Detected {
9
9
  /** The agent's command line, starting with its binary. */
10
10
  argv: string;
11
11
  pid: number;
12
+ argvIssue?: string;
12
13
  }
13
- interface Proc {
14
+ export interface Proc {
14
15
  pid: number;
15
16
  ppid: number;
16
17
  args: string;
18
+ native?: boolean;
17
19
  }
20
+ export declare const ARGUMENTS_UNAVAILABLE = "Launch arguments could not be verified. Check process access (macOS also requires /usr/bin/perl) before restoring.";
21
+ export declare const BINDING_UNVERIFIED = "Recovery binding needs verification. No agent process identity was recorded; verified identity evidence must confirm the conversation.";
22
+ export declare const PRODUCER_CHANGED = "Agent producer changed. Verify the conversation before restoring.";
23
+ export declare function processes(): Proc[];
18
24
  /**
19
25
  * The agent among a process and its descendants: a program named like an
20
26
  * agent binary, or a script so named run by an interpreter (`node <cli>`).
21
27
  * Names are what matter: some agents run as a binary named after its version,
22
28
  * so tmux's pane_current_command does not tell.
23
29
  */
30
+ export declare function agentsAmong(rootPid: number, procs: Proc[], depth?: number): Detected[];
24
31
  export declare function agentAmong(rootPid: number, procs: Proc[]): Detected | undefined;
32
+ export declare function sessionProducers(ctx: Context, session: string, procs?: Proc[], allPanes?: string): (Detected & {
33
+ pane: string;
34
+ cwd: string;
35
+ })[];
25
36
  /**
26
37
  * A process's working directory. The pane's current path is not it: tmux
27
38
  * reads the foreground process group's leader, which is a launcher's shell
@@ -31,8 +42,7 @@ export declare function cwdOf(pid: number): string | undefined;
31
42
  /** The agent running in a session's active pane, if any. */
32
43
  export declare function detectAgent(ctx: Context, session: string): Detected | undefined;
33
44
  /**
34
- * Stamp every unmanaged live session whose active pane runs an agent. Returns
45
+ * Stamp every unmanaged session or managed shell whose active pane runs an agent. Returns
35
46
  * the sessions stamped; the caller snapshots. Never throws.
36
47
  */
37
48
  export declare function discover(ctx: Context): string[];
38
- export {};