@haywoodmarx/agent-pager 0.1.0

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 (178) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/dist/src/broker/address.d.ts +49 -0
  4. package/dist/src/broker/address.js +110 -0
  5. package/dist/src/broker/broker.d.ts +665 -0
  6. package/dist/src/broker/broker.js +2270 -0
  7. package/dist/src/broker/database.d.ts +81 -0
  8. package/dist/src/broker/database.js +69 -0
  9. package/dist/src/broker/operator-projection.d.ts +291 -0
  10. package/dist/src/broker/operator-projection.js +722 -0
  11. package/dist/src/broker/protocol.d.ts +237 -0
  12. package/dist/src/broker/protocol.js +311 -0
  13. package/dist/src/broker/schema.d.ts +94 -0
  14. package/dist/src/broker/schema.js +454 -0
  15. package/dist/src/broker/snapshot.d.ts +171 -0
  16. package/dist/src/broker/snapshot.js +78 -0
  17. package/dist/src/configuration-defaults.d.ts +64 -0
  18. package/dist/src/configuration-defaults.js +48 -0
  19. package/dist/src/configuration.d.ts +76 -0
  20. package/dist/src/configuration.js +156 -0
  21. package/dist/src/implementations/claude/environment.d.ts +8 -0
  22. package/dist/src/implementations/claude/environment.js +9 -0
  23. package/dist/src/implementations/claude/gateway-cli.d.ts +2 -0
  24. package/dist/src/implementations/claude/gateway-cli.js +136 -0
  25. package/dist/src/implementations/claude/gateway-identity.d.ts +134 -0
  26. package/dist/src/implementations/claude/gateway-identity.js +103 -0
  27. package/dist/src/implementations/claude/gateway.d.ts +148 -0
  28. package/dist/src/implementations/claude/gateway.js +236 -0
  29. package/dist/src/implementations/claude/instructions.d.ts +33 -0
  30. package/dist/src/implementations/claude/instructions.js +64 -0
  31. package/dist/src/implementations/claude/manifest.d.ts +33 -0
  32. package/dist/src/implementations/claude/manifest.js +108 -0
  33. package/dist/src/implementations/claude/plugin-directory.d.ts +80 -0
  34. package/dist/src/implementations/claude/plugin-directory.js +137 -0
  35. package/dist/src/implementations/claude/session-continuation.d.ts +25 -0
  36. package/dist/src/implementations/claude/session-continuation.js +125 -0
  37. package/dist/src/implementations/claude/session-identity.d.ts +25 -0
  38. package/dist/src/implementations/claude/session-identity.js +31 -0
  39. package/dist/src/implementations/claude/session-start-cli.d.ts +2 -0
  40. package/dist/src/implementations/claude/session-start-cli.js +16 -0
  41. package/dist/src/implementations/claude/socket-delivery.d.ts +52 -0
  42. package/dist/src/implementations/claude/socket-delivery.js +159 -0
  43. package/dist/src/implementations/model.d.ts +162 -0
  44. package/dist/src/implementations/model.js +1 -0
  45. package/dist/src/implementations/opencode/adapter.d.ts +71 -0
  46. package/dist/src/implementations/opencode/adapter.js +94 -0
  47. package/dist/src/implementations/opencode/environment.d.ts +8 -0
  48. package/dist/src/implementations/opencode/environment.js +9 -0
  49. package/dist/src/implementations/opencode/manifest.d.ts +30 -0
  50. package/dist/src/implementations/opencode/manifest.js +93 -0
  51. package/dist/src/implementations/opencode/plugin-api.d.ts +120 -0
  52. package/dist/src/implementations/opencode/plugin-api.js +1 -0
  53. package/dist/src/implementations/opencode/plugin.d.ts +38 -0
  54. package/dist/src/implementations/opencode/plugin.js +303 -0
  55. package/dist/src/implementations/opencode/self-close-tool.d.ts +9 -0
  56. package/dist/src/implementations/opencode/self-close-tool.js +31 -0
  57. package/dist/src/implementations/orca/terminal.d.ts +48 -0
  58. package/dist/src/implementations/orca/terminal.js +154 -0
  59. package/dist/src/implementations/pi/broker-client.d.ts +56 -0
  60. package/dist/src/implementations/pi/broker-client.js +156 -0
  61. package/dist/src/implementations/pi/environment.d.ts +8 -0
  62. package/dist/src/implementations/pi/environment.js +9 -0
  63. package/dist/src/implementations/pi/extension.d.ts +57 -0
  64. package/dist/src/implementations/pi/extension.js +253 -0
  65. package/dist/src/implementations/pi/host-slots.d.ts +18 -0
  66. package/dist/src/implementations/pi/host-slots.js +27 -0
  67. package/dist/src/implementations/pi/manifest.d.ts +11 -0
  68. package/dist/src/implementations/pi/manifest.js +61 -0
  69. package/dist/src/implementations/pi/self-close-tool.d.ts +7 -0
  70. package/dist/src/implementations/pi/self-close-tool.js +27 -0
  71. package/dist/src/implementations/registry.d.ts +47 -0
  72. package/dist/src/implementations/registry.js +157 -0
  73. package/dist/src/implementations/session-key.d.ts +13 -0
  74. package/dist/src/implementations/session-key.js +19 -0
  75. package/dist/src/index.d.ts +27 -0
  76. package/dist/src/index.js +26 -0
  77. package/dist/src/launch/broker-lock.d.ts +257 -0
  78. package/dist/src/launch/broker-lock.js +644 -0
  79. package/dist/src/launch/cli.d.ts +37 -0
  80. package/dist/src/launch/cli.js +317 -0
  81. package/dist/src/launch/environment.d.ts +45 -0
  82. package/dist/src/launch/environment.js +48 -0
  83. package/dist/src/launch/launch.d.ts +235 -0
  84. package/dist/src/launch/launch.js +223 -0
  85. package/dist/src/launch/on-demand.d.ts +202 -0
  86. package/dist/src/launch/on-demand.js +435 -0
  87. package/dist/src/launch/runtime-composition.d.ts +88 -0
  88. package/dist/src/launch/runtime-composition.js +253 -0
  89. package/dist/src/launch/runtime.d.ts +79 -0
  90. package/dist/src/launch/runtime.js +196 -0
  91. package/dist/src/launch/service-system.d.ts +11 -0
  92. package/dist/src/launch/service-system.js +39 -0
  93. package/dist/src/launch/service.d.ts +173 -0
  94. package/dist/src/launch/service.js +492 -0
  95. package/dist/src/launch/skill.d.ts +57 -0
  96. package/dist/src/launch/skill.js +81 -0
  97. package/dist/src/launch/terminal.d.ts +26 -0
  98. package/dist/src/launch/terminal.js +43 -0
  99. package/dist/src/launch/workspace-branch.d.ts +10 -0
  100. package/dist/src/launch/workspace-branch.js +29 -0
  101. package/dist/src/messaging/delivery-client.d.ts +144 -0
  102. package/dist/src/messaging/delivery-client.js +261 -0
  103. package/dist/src/messaging/delivery.d.ts +75 -0
  104. package/dist/src/messaging/delivery.js +22 -0
  105. package/dist/src/messaging/inbox-socket.d.ts +98 -0
  106. package/dist/src/messaging/inbox-socket.js +278 -0
  107. package/dist/src/messaging/mcp.d.ts +53 -0
  108. package/dist/src/messaging/mcp.js +501 -0
  109. package/dist/src/messaging/notifications.d.ts +231 -0
  110. package/dist/src/messaging/notifications.js +489 -0
  111. package/dist/src/messaging/push-delivery.d.ts +77 -0
  112. package/dist/src/messaging/push-delivery.js +187 -0
  113. package/dist/src/messaging/tools.d.ts +11 -0
  114. package/dist/src/messaging/tools.js +102 -0
  115. package/dist/src/messaging/wake.d.ts +23 -0
  116. package/dist/src/messaging/wake.js +39 -0
  117. package/dist/src/operator/agent-projection.d.ts +102 -0
  118. package/dist/src/operator/agent-projection.js +249 -0
  119. package/dist/src/operator/commands.d.ts +50 -0
  120. package/dist/src/operator/commands.js +199 -0
  121. package/dist/src/operator/diagnostics.d.ts +21 -0
  122. package/dist/src/operator/diagnostics.js +22 -0
  123. package/dist/src/operator/protocol.d.ts +511 -0
  124. package/dist/src/operator/protocol.js +49 -0
  125. package/dist/src/operator/repository-system.d.ts +14 -0
  126. package/dist/src/operator/repository-system.js +150 -0
  127. package/dist/src/operator/repository.d.ts +79 -0
  128. package/dist/src/operator/repository.js +72 -0
  129. package/dist/src/operator/traffic-projection.d.ts +19 -0
  130. package/dist/src/operator/traffic-projection.js +229 -0
  131. package/dist/src/operator/transport.d.ts +87 -0
  132. package/dist/src/operator/transport.js +549 -0
  133. package/dist/src/operator/update-store.d.ts +53 -0
  134. package/dist/src/operator/update-store.js +162 -0
  135. package/dist/src/operator/updates.d.ts +94 -0
  136. package/dist/src/operator/updates.js +280 -0
  137. package/dist/src/operator/validation.d.ts +114 -0
  138. package/dist/src/operator/validation.js +916 -0
  139. package/dist/src/shared/guards.d.ts +59 -0
  140. package/dist/src/shared/guards.js +119 -0
  141. package/dist/src/shared/http.d.ts +30 -0
  142. package/dist/src/shared/http.js +50 -0
  143. package/dist/src/shared/row.d.ts +2 -0
  144. package/dist/src/shared/row.js +1 -0
  145. package/dist/src/shared/shell.d.ts +6 -0
  146. package/dist/src/shared/shell.js +8 -0
  147. package/dist/src/tui/actions.d.ts +166 -0
  148. package/dist/src/tui/actions.js +253 -0
  149. package/dist/src/tui/agent-tree.d.ts +125 -0
  150. package/dist/src/tui/agent-tree.js +663 -0
  151. package/dist/src/tui/app.d.ts +38 -0
  152. package/dist/src/tui/app.js +378 -0
  153. package/dist/src/tui/chrome.d.ts +86 -0
  154. package/dist/src/tui/chrome.js +85 -0
  155. package/dist/src/tui/client.d.ts +41 -0
  156. package/dist/src/tui/client.js +279 -0
  157. package/dist/src/tui/composition.d.ts +41 -0
  158. package/dist/src/tui/composition.js +146 -0
  159. package/dist/src/tui/message-views.d.ts +94 -0
  160. package/dist/src/tui/message-views.js +515 -0
  161. package/dist/src/tui/model.d.ts +414 -0
  162. package/dist/src/tui/model.js +758 -0
  163. package/dist/src/tui/presentation.d.ts +350 -0
  164. package/dist/src/tui/presentation.js +541 -0
  165. package/dist/src/tui/production.d.ts +79 -0
  166. package/dist/src/tui/production.js +253 -0
  167. package/dist/src/tui/run-terminal.d.ts +32 -0
  168. package/dist/src/tui/run-terminal.js +54 -0
  169. package/dist/src/tui/runtime.d.ts +113 -0
  170. package/dist/src/tui/runtime.js +189 -0
  171. package/dist/src/tui/theme.d.ts +49 -0
  172. package/dist/src/tui/theme.js +55 -0
  173. package/dist/src/tui/transport.d.ts +31 -0
  174. package/dist/src/tui/transport.js +255 -0
  175. package/dist/src/version.d.ts +11 -0
  176. package/dist/src/version.js +52 -0
  177. package/package.json +142 -0
  178. package/skills/pager/SKILL.md +112 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Haywood-Marx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # Pager
2
+
3
+ Pager is a durable local message broker for agent implementations. Every implementation sends through one loopback MCP endpoint; implementation providers own process launch, lifecycle binding, and how their sessions are told a message is waiting.
4
+
5
+ The broker owns identity, addressing, durable inboxes, claims, acknowledgements, delivery state, and operational diagnostics. It never infers an implementation from an agent id and does not enumerate installed implementations.
6
+
7
+ ## Architecture
8
+
9
+ ```text
10
+ implementation ─ shared MCP ─ broker ─ wake ─ implementation provider
11
+ │
12
+ ├────── snapshot ────── HTTP consumers
13
+ └────── operator stream ─ TUI
14
+ ```
15
+
16
+ A send commits before its wake is published. Every session registers the address of an inbox its implementation opens, and the broker claims on the recipient's behalf and has the provider write those claims to that inbox, so the message itself arrives in the notification. The recipient acknowledges each claim once it holds it, and the acknowledgement lists every token it did not apply with the reason, so a recipient knows to fetch again. Delivery is at least once: an unacknowledged claim returns to the queue, so a recipient that crashes after acting but before acknowledging is redelivered, and exactly-once action is not guaranteed.
17
+
18
+ Implementation metadata is open and opaque. The neutral runtime discovers provider manifests by convention, and shared launch or notification code resolves capabilities through `ImplementationRegistry` rather than switching on provider ids.
19
+
20
+ Pager runs on Bun, at or above the release named by `packageManager` in `package.json`, and requires it on `PATH`. Embedded GUI hosts that do not inherit the interactive shell environment require `PAGER_BUN_PATH` to name the validated absolute Bun executable. The store directory and every path derived from it are resolved for macOS only; the CLI refuses to start on another platform.
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ npm install -g @haywoodmarx/agent-pager
26
+ ```
27
+
28
+ ## Install from a local checkout
29
+
30
+ ```sh
31
+ bun install
32
+ bun run build
33
+ ```
34
+
35
+ The `pager` shim launches Bun through its shebang.
36
+
37
+ The package exposes the operational CLI as `pager`:
38
+
39
+ ```sh
40
+ pager
41
+ pager exit
42
+ pager restart
43
+ pager status
44
+ pager <implementation-id> [--session-name <name>] -- <implementation-arguments>
45
+ ```
46
+
47
+ The bare implementation form starts or adopts the loopback service automatically. A command already running in a hosted terminal keeps the implementation in that terminal with inherited stdio. Otherwise, it prefers an available discovered terminal provider and falls back to inherited stdio; implementation exit status is propagated to the invoking shell. A session name is also the agent's routable alias and is unique within the working directory across implementations. Supplying it again rejoins that broker identity once its holder's harness has shut down gracefully and its connector has said so, its attachment heartbeat lease has lapsed, or the holder was archived; while the holder is attached, the launch exits non-zero naming the holder and the harness does not start. Omitting it creates an ephemeral identity that is reaped once its attachment heartbeat lease lapses, or, when a conversation key makes it resumable, once it has been detached for the resumable retention window, shortened to the unused window while it has never sent a message or received one addressed to it alone. An ephemeral identity that never attached is reaped once its attach reservations have lapsed. Archiving an identity from the operator TUI ends its attachments and hides it, while messages sent to it wait; attaching again un-archives it. A named identity detached for the named archive window is archived too, and comes back when its conversation attaches or its holder resumes its attachment. Otherwise an archived identity, ephemeral or named, is reaped once the archive retention window has passed since its first archive, and reaping dead-letters its unsettled mail. The first sweep after an upgrade reaps every identity already archived for longer than that window.
48
+
49
+ The authenticated `whoami` MCP tool returns the agent id, current alias, and previous aliases. `update_agent_alias` atomically replaces the current alias and durable session name; assigning one to an unnamed agent makes that identity durable. Prior names remain reserved. Sending to one is rejected with `recipient_alias_changed` and the current alias rather than silently redirecting the message.
50
+
51
+ The package also exposes its Pi integration as a Pi package. `pi install npm:@haywoodmarx/agent-pager` enables directly started Pi sessions to discover or start the broker and register themselves; `pager pi -- <pi-arguments>` loads the same extension explicitly for a wrapper-managed session.
52
+
53
+ Pager does not change a harness's permission settings. A session that has to wait for an operator to approve each Pager tool call cannot acknowledge a claim, so its message returns to the queue and is delivered again. Consider approving the Pager tools in each harness you run unattended, using that harness's own permission configuration.
54
+
55
+ ## Service operations
56
+
57
+ The broker starts on demand: `pager` with no command, and every harness connecting over MCP, adopts the running broker or starts one. `pager status` reports whether the broker is running, its pid, its MCP endpoint, and the database it serves. `pager exit` stops it, and `pager restart` stops it and starts a fresh one. `pager serve` runs the broker in the foreground.
58
+
59
+ Callers adopt a running service only when its database-adjacent lock and `/health` agree on the service identity; concurrent starters race for that lock and every caller adopts the winner. The adoption rules are declared in [`src/launch/on-demand.ts`](src/launch/on-demand.ts) and [`src/launch/broker-lock.ts`](src/launch/broker-lock.ts).
60
+
61
+ Back up the database and active service plist before an upgrade if their current state matters. Schema changes are destructive: a database whose schema marker or required columns do not match the running build is recreated, discarding every identity and message it held, and the recreation is reported with the counts it deleted on the standard error of the command that started the service, whether that is `pager serve` or an on-demand start. The marker is part of the service adoption identity, so a build that would recreate the database cannot displace a running service that owns it. Deleting the database and starting the service also creates an empty broker. After `uninstall`, delete the retained `.previous` plist and database only when rollback and message recovery are no longer required.
62
+
63
+ Per-implementation installation and conformance documentation is indexed under [`docs/implementations/acceptance/`](docs/implementations/acceptance/README.md).
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Split a routable address into its alias and optional workspace name.
3
+ * @param value - Caller-supplied address, with an optional leading at-sign and workspace suffix.
4
+ * @returns - Conformed session name, and the workspace name when the address carried one.
5
+ */
6
+ export declare function parseAgentAddress(value: string): {
7
+ sessionName: string;
8
+ workspace: string | null;
9
+ };
10
+ /**
11
+ * Conform a caller-supplied workspace name to the form {@link uniquePathNames} names a workspace by.
12
+ * @param value - Untrusted workspace name, as it appears after the at-sign of an address.
13
+ * @returns - Addressable workspace name.
14
+ */
15
+ export declare function parseWorkspaceName(value: string): string;
16
+ /**
17
+ * Name each path by the shortest trailing segments that no other supplied path shares.
18
+ * @param entries - Every identity paired with its absolute path.
19
+ * @returns - Unique addressable name for each supplied identity.
20
+ */
21
+ export declare function uniquePathNames(entries: readonly {
22
+ id: string;
23
+ path: string;
24
+ }[]): Map<string, string>;
25
+ /**
26
+ * Derive the stable identity of a workspace from its absolute path.
27
+ * @param path - Workspace directory used as the registration boundary.
28
+ * @returns - Stable identity for the resolved workspace path.
29
+ */
30
+ export declare function workspaceIdentity(path: string): string;
31
+ /**
32
+ * Derive the stable team shared by reservationless registrations in one workspace.
33
+ * @param path - Workspace directory used as the registration boundary.
34
+ * @returns - Stable team identity for the resolved workspace path.
35
+ */
36
+ export declare function workspaceTeamId(path: string): string;
37
+ /**
38
+ * Conform a routable alias to its canonical leading-at-sign form.
39
+ * @param alias - Session name with or without its leading at-sign.
40
+ * @returns - Alias with exactly one leading at-sign.
41
+ */
42
+ export declare function normalizeAlias(alias: string): string;
43
+ /**
44
+ * Conform and validate a caller-supplied registration session name or alias.
45
+ * @param value - Untrusted session name or alias candidate.
46
+ * @param label - What the caller supplied, as the rejection names it; the rule is the same for a registration name and a rename.
47
+ * @returns - Conformed session name accepted for registration.
48
+ */
49
+ export declare function validateRegistrationSessionName(value: string, label?: string): string;
@@ -0,0 +1,110 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { ProtocolError, REGISTRATION_SESSION_NAME_MAX_LENGTH, SESSION_IDENTIFIER_PATTERN } from "./protocol.js";
4
+ /**
5
+ * Split a routable address into its alias and optional workspace name.
6
+ * @param value - Caller-supplied address, with an optional leading at-sign and workspace suffix.
7
+ * @returns - Conformed session name, and the workspace name when the address carried one.
8
+ */
9
+ export function parseAgentAddress(value) {
10
+ const withoutPrefix = value.trim().replace(/^@/u, "");
11
+ const separator = withoutPrefix.lastIndexOf("@");
12
+ if (separator === -1)
13
+ return { sessionName: validateRegistrationSessionName(withoutPrefix), workspace: null };
14
+ const workspace = parseWorkspaceName(withoutPrefix.slice(separator + 1));
15
+ return { sessionName: validateRegistrationSessionName(withoutPrefix.slice(0, separator)), workspace };
16
+ }
17
+ /**
18
+ * Conform a caller-supplied workspace name to the form {@link uniquePathNames} names a workspace by.
19
+ * @param value - Untrusted workspace name, as it appears after the at-sign of an address.
20
+ * @returns - Addressable workspace name.
21
+ */
22
+ export function parseWorkspaceName(value) {
23
+ const requested = value.trim();
24
+ const workspace = workspaceName(conformWorkspaceSegments(requested), requested.startsWith("/"));
25
+ if (workspace === "")
26
+ throw new ProtocolError("validation_failed", "Address workspace name is empty");
27
+ return workspace;
28
+ }
29
+ function conformWorkspaceSegments(value) {
30
+ return value
31
+ .toLowerCase()
32
+ .split("/")
33
+ .map((segment) => segment.trim().replace(/[^a-z0-9._-]/gu, "-"))
34
+ .filter((segment) => segment !== "");
35
+ }
36
+ /**
37
+ * Join workspace path segments into one addressable name.
38
+ * @param segments - Trailing path segments this name covers.
39
+ * @param absolute - Whether the segments span the whole path, which marks the name with a leading slash so it cannot read as a suffix of a longer path.
40
+ * @returns - Addressable workspace name.
41
+ */
42
+ function workspaceName(segments, absolute) {
43
+ return `${absolute ? "/" : ""}${segments.join("/")}`;
44
+ }
45
+ /**
46
+ * Name each path by the shortest trailing segments that no other supplied path shares.
47
+ * @param entries - Every identity paired with its absolute path.
48
+ * @returns - Unique addressable name for each supplied identity.
49
+ */
50
+ export function uniquePathNames(entries) {
51
+ const paths = entries.map((entry) => ({
52
+ id: entry.id,
53
+ segments: conformWorkspaceSegments(entry.path),
54
+ }));
55
+ const names = new Map();
56
+ for (const workspace of paths) {
57
+ let depth = 1;
58
+ // Sharing compares bare suffixes, so a path that runs out of segments still extends its neighbour.
59
+ while (depth < workspace.segments.length &&
60
+ paths.some((other) => other.id !== workspace.id &&
61
+ other.segments.slice(-depth).join("/") === workspace.segments.slice(-depth).join("/"))) {
62
+ depth += 1;
63
+ }
64
+ names.set(workspace.id, workspaceName(workspace.segments.slice(-depth), depth === workspace.segments.length));
65
+ }
66
+ return names;
67
+ }
68
+ /**
69
+ * Derive the stable identity of a workspace from its absolute path.
70
+ * @param path - Workspace directory used as the registration boundary.
71
+ * @returns - Stable identity for the resolved workspace path.
72
+ */
73
+ export function workspaceIdentity(path) {
74
+ return createHash("sha256").update(path).digest("hex").slice(0, 16);
75
+ }
76
+ /**
77
+ * Derive the stable team shared by reservationless registrations in one workspace.
78
+ * @param path - Workspace directory used as the registration boundary.
79
+ * @returns - Stable team identity for the resolved workspace path.
80
+ */
81
+ export function workspaceTeamId(path) {
82
+ return `workspace:${workspaceIdentity(resolve(path))}`;
83
+ }
84
+ /**
85
+ * Conform a routable alias to its canonical leading-at-sign form.
86
+ * @param alias - Session name with or without its leading at-sign.
87
+ * @returns - Alias with exactly one leading at-sign.
88
+ */
89
+ export function normalizeAlias(alias) {
90
+ return alias.startsWith("@") ? alias : `@${alias}`;
91
+ }
92
+ function conformSessionName(value) {
93
+ return value.trim().replace(/\s+/gu, "-").toLowerCase();
94
+ }
95
+ function isRegistrationSessionName(value) {
96
+ return (value.length >= 1 && value.length <= REGISTRATION_SESSION_NAME_MAX_LENGTH && SESSION_IDENTIFIER_PATTERN.test(value));
97
+ }
98
+ /**
99
+ * Conform and validate a caller-supplied registration session name or alias.
100
+ * @param value - Untrusted session name or alias candidate.
101
+ * @param label - What the caller supplied, as the rejection names it; the rule is the same for a registration name and a rename.
102
+ * @returns - Conformed session name accepted for registration.
103
+ */
104
+ export function validateRegistrationSessionName(value, label = "registration session name") {
105
+ const conformed = conformSessionName(value);
106
+ if (!isRegistrationSessionName(conformed)) {
107
+ throw new ProtocolError("validation_failed", `${label} must be 1-${String(REGISTRATION_SESSION_NAME_MAX_LENGTH)} letters, digits, dots, colons, underscores, or hyphens`);
108
+ }
109
+ return conformed;
110
+ }