shennian 0.3.37 → 0.3.39

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 (214) hide show
  1. package/README.md +1 -0
  2. package/dist/publish-build-manifest.json +73 -168
  3. package/dist/src/agents/detect.js +1 -1
  4. package/dist/src/agents/manager.d.ts +36 -0
  5. package/dist/src/agents/manager.js +11 -6
  6. package/dist/src/agents/model-registry/service.js +1 -1
  7. package/dist/src/agents/workbuddy.js +2 -2
  8. package/dist/src/commands/agent.d.ts +6 -1
  9. package/dist/src/commands/agent.js +3 -6
  10. package/dist/src/commands/integration.d.ts +1 -0
  11. package/dist/src/commands/integration.js +2 -1
  12. package/dist/src/commands/manager.js +1 -1
  13. package/dist/src/commands/pair-authorization.d.ts +3 -0
  14. package/dist/src/commands/pair-authorization.js +1 -0
  15. package/dist/src/commands/pair-browser.js +1 -1
  16. package/dist/src/commands/room.d.ts +9 -17
  17. package/dist/src/commands/room.js +3 -8
  18. package/dist/src/commands/runtime.d.ts +2 -0
  19. package/dist/src/commands/runtime.js +6 -0
  20. package/dist/src/daemon-ipc/client.d.ts +1 -1
  21. package/dist/src/daemon-ipc/json.d.ts +3 -0
  22. package/dist/src/daemon-ipc/json.js +1 -0
  23. package/dist/src/daemon-ipc/protocol.d.ts +1 -1
  24. package/dist/src/daemon-ipc/server.d.ts +1 -1
  25. package/dist/src/index.js +3 -2
  26. package/dist/src/manager/registry.d.ts +21 -0
  27. package/dist/src/manager/registry.js +2 -2
  28. package/dist/src/manager/runtime.d.ts +5 -1
  29. package/dist/src/manager/runtime.js +14 -13
  30. package/dist/src/room/activation-service.d.ts +6 -0
  31. package/dist/src/room/activation-service.js +5 -8
  32. package/dist/src/room/attachment-cache.d.ts +2 -1
  33. package/dist/src/room/attachment-cache.js +2 -2
  34. package/dist/src/room/device-authorization-client.d.ts +39 -1
  35. package/dist/src/room/managed-context.d.ts +45 -0
  36. package/dist/src/room/managed-context.js +1 -0
  37. package/dist/src/room/managed-prompt-composer.d.ts +32 -0
  38. package/dist/src/room/managed-prompt-composer.js +24 -0
  39. package/dist/src/room/managed-room-ipc.d.ts +16 -0
  40. package/dist/src/room/managed-room-ipc.js +1 -0
  41. package/dist/src/room/mirror-sync-service.d.ts +2 -5
  42. package/dist/src/room/mirror-sync-service.js +1 -1
  43. package/dist/src/room/public-attachment-cache.d.ts +34 -0
  44. package/dist/src/room/public-attachment-cache.js +2 -0
  45. package/dist/src/room/public-room-client.d.ts +36 -0
  46. package/dist/src/room/public-room-client.js +1 -0
  47. package/dist/src/room/room-action-client.d.ts +3 -1
  48. package/dist/src/room/room-action-client.js +1 -1
  49. package/dist/src/room/session-registry.d.ts +7 -0
  50. package/dist/src/room/session-registry.js +2 -2
  51. package/dist/src/session/handlers/chat.d.ts +5 -1
  52. package/dist/src/session/handlers/chat.js +2 -2
  53. package/dist/src/session/manager.d.ts +1 -20
  54. package/dist/src/session/manager.js +1 -1
  55. package/dist/src/session/queue.js +3 -3
  56. package/dist/src/session/store.d.ts +9 -1
  57. package/dist/src/session/store.js +3 -3
  58. package/dist/src/session/types.d.ts +2 -1
  59. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts +0 -1
  60. package/node_modules/@shennian/wire/dist/agent-capabilities.js +0 -1
  61. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +0 -1
  62. package/node_modules/@shennian/wire/dist/agent-workspace.js +0 -1
  63. package/node_modules/@shennian/wire/dist/envelope.d.ts +0 -1
  64. package/node_modules/@shennian/wire/dist/envelope.js +0 -1
  65. package/node_modules/@shennian/wire/dist/events.d.ts +0 -1
  66. package/node_modules/@shennian/wire/dist/events.js +0 -1
  67. package/node_modules/@shennian/wire/dist/external-message-format.d.ts +0 -1
  68. package/node_modules/@shennian/wire/dist/external-message-format.js +0 -1
  69. package/node_modules/@shennian/wire/dist/frames.d.ts +0 -1
  70. package/node_modules/@shennian/wire/dist/frames.js +0 -1
  71. package/node_modules/@shennian/wire/dist/fs.d.ts +0 -1
  72. package/node_modules/@shennian/wire/dist/fs.js +0 -1
  73. package/node_modules/@shennian/wire/dist/index.d.ts +0 -1
  74. package/node_modules/@shennian/wire/dist/index.js +0 -1
  75. package/node_modules/@shennian/wire/dist/machine.d.ts +0 -1
  76. package/node_modules/@shennian/wire/dist/machine.js +0 -1
  77. package/node_modules/@shennian/wire/dist/message-payload.d.ts +0 -1
  78. package/node_modules/@shennian/wire/dist/message-payload.js +0 -1
  79. package/node_modules/@shennian/wire/dist/methods.d.ts +0 -1
  80. package/node_modules/@shennian/wire/dist/methods.js +0 -1
  81. package/node_modules/@shennian/wire/dist/native-session.d.ts +0 -1
  82. package/node_modules/@shennian/wire/dist/native-session.js +0 -1
  83. package/node_modules/@shennian/wire/dist/project.d.ts +0 -1
  84. package/node_modules/@shennian/wire/dist/project.js +0 -1
  85. package/node_modules/@shennian/wire/dist/room.d.ts +44 -1
  86. package/node_modules/@shennian/wire/dist/room.js +0 -1
  87. package/node_modules/@shennian/wire/dist/session.d.ts +18 -3
  88. package/node_modules/@shennian/wire/dist/session.js +0 -2
  89. package/node_modules/@shennian/wire/dist/tool-detail.d.ts +0 -1
  90. package/node_modules/@shennian/wire/dist/tool-detail.js +0 -1
  91. package/node_modules/@shennian/wire/dist/voice-chief.d.ts +0 -1
  92. package/node_modules/@shennian/wire/dist/voice-chief.js +0 -1
  93. package/node_modules/@shennian/wire/package.json +1 -0
  94. package/package.json +2 -2
  95. package/dist/src/integrations/claude-room-plugin-lifecycle.d.ts +0 -47
  96. package/dist/src/integrations/claude-room-plugin-lifecycle.js +0 -1
  97. package/dist/src/integrations/codex-room-plugin-lifecycle.d.ts +0 -49
  98. package/dist/src/integrations/codex-room-plugin-lifecycle.js +0 -1
  99. package/dist/src/integrations/room-plugin-bundles.d.ts +0 -23
  100. package/dist/src/integrations/room-plugin-bundles.js +0 -1
  101. package/dist/src/integrations/room-plugin-lifecycle-common.d.ts +0 -17
  102. package/dist/src/integrations/room-plugin-lifecycle-common.js +0 -1
  103. package/dist/src/integrations/room-plugin-shared.d.ts +0 -24
  104. package/dist/src/integrations/room-plugin-shared.js +0 -38
  105. package/dist/src/integrations/room-session-hook.d.ts +0 -30
  106. package/dist/src/integrations/room-session-hook.js +0 -2
  107. package/dist/src/integrations/workbuddy-plugin-bundle.d.ts +0 -16
  108. package/dist/src/integrations/workbuddy-plugin-bundle.js +0 -1
  109. package/dist/src/integrations/workbuddy-plugin-lifecycle.d.ts +0 -62
  110. package/dist/src/integrations/workbuddy-plugin-lifecycle.js +0 -2
  111. package/dist/src/integrations/workbuddy-session-hook.d.ts +0 -22
  112. package/dist/src/integrations/workbuddy-session-hook.js +0 -1
  113. package/dist/src/room/agent-tool.d.ts +0 -63
  114. package/dist/src/room/agent-tool.js +0 -1
  115. package/dist/src/room/authorized-action-coordinator.d.ts +0 -40
  116. package/dist/src/room/authorized-action-coordinator.js +0 -1
  117. package/dist/src/room/authorized-action-ipc.d.ts +0 -11
  118. package/dist/src/room/authorized-action-ipc.js +0 -1
  119. package/dist/src/room/host-session-observation-ipc.d.ts +0 -9
  120. package/dist/src/room/host-session-observation-ipc.js +0 -1
  121. package/dist/src/room/invocation-token-ipc.d.ts +0 -9
  122. package/dist/src/room/invocation-token-ipc.js +0 -1
  123. package/dist/src/room/invocation-token.d.ts +0 -53
  124. package/dist/src/room/invocation-token.js +0 -1
  125. package/dist/src/room/jsonl-mirror.d.ts +0 -73
  126. package/dist/src/room/jsonl-mirror.js +0 -6
  127. package/dist/src/room/managed-room-session-runtime.d.ts +0 -18
  128. package/dist/src/room/managed-room-session-runtime.js +0 -1
  129. package/dist/src/room/managed-room-session.d.ts +0 -144
  130. package/dist/src/room/managed-room-session.js +0 -2
  131. package/dist/src/room/operator-ipc.d.ts +0 -13
  132. package/dist/src/room/operator-ipc.js +0 -1
  133. package/dist/src/room/pending-authorized-actions.d.ts +0 -69
  134. package/dist/src/room/pending-authorized-actions.js +0 -3
  135. package/dist/src/room/room-action-ipc.d.ts +0 -34
  136. package/dist/src/room/room-action-ipc.js +0 -1
  137. package/dist/src/room/room-action-schema.d.ts +0 -3
  138. package/dist/src/room/room-action-schema.js +0 -1
  139. package/dist/src/room/room-authorized-action-executor.d.ts +0 -23
  140. package/dist/src/room/room-authorized-action-executor.js +0 -1
  141. package/dist/src/room/room-binding-ipc.d.ts +0 -31
  142. package/dist/src/room/room-binding-ipc.js +0 -1
  143. package/dist/src/room/session-registry-ipc.d.ts +0 -14
  144. package/dist/src/room/session-registry-ipc.js +0 -1
  145. package/dist/src/room/tool-schema.d.ts +0 -120
  146. package/dist/src/room/tool-schema.js +0 -1
  147. package/dist/src/room/tool-server.d.ts +0 -11
  148. package/dist/src/room/tool-server.js +0 -3
  149. package/node_modules/@shennian/wire/dist/__tests__/message-payload.test.d.ts +0 -2
  150. package/node_modules/@shennian/wire/dist/__tests__/message-payload.test.d.ts.map +0 -1
  151. package/node_modules/@shennian/wire/dist/__tests__/message-payload.test.js +0 -23
  152. package/node_modules/@shennian/wire/dist/__tests__/message-payload.test.js.map +0 -1
  153. package/node_modules/@shennian/wire/dist/__tests__/mock.test.d.ts +0 -2
  154. package/node_modules/@shennian/wire/dist/__tests__/mock.test.d.ts.map +0 -1
  155. package/node_modules/@shennian/wire/dist/__tests__/mock.test.js +0 -66
  156. package/node_modules/@shennian/wire/dist/__tests__/mock.test.js.map +0 -1
  157. package/node_modules/@shennian/wire/dist/__tests__/types.test.d.ts +0 -2
  158. package/node_modules/@shennian/wire/dist/__tests__/types.test.d.ts.map +0 -1
  159. package/node_modules/@shennian/wire/dist/__tests__/types.test.js +0 -159
  160. package/node_modules/@shennian/wire/dist/__tests__/types.test.js.map +0 -1
  161. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts.map +0 -1
  162. package/node_modules/@shennian/wire/dist/agent-capabilities.js.map +0 -1
  163. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts.map +0 -1
  164. package/node_modules/@shennian/wire/dist/agent-workspace.js.map +0 -1
  165. package/node_modules/@shennian/wire/dist/envelope.d.ts.map +0 -1
  166. package/node_modules/@shennian/wire/dist/envelope.js.map +0 -1
  167. package/node_modules/@shennian/wire/dist/events.d.ts.map +0 -1
  168. package/node_modules/@shennian/wire/dist/events.js.map +0 -1
  169. package/node_modules/@shennian/wire/dist/external-message-format.d.ts.map +0 -1
  170. package/node_modules/@shennian/wire/dist/external-message-format.js.map +0 -1
  171. package/node_modules/@shennian/wire/dist/frames.d.ts.map +0 -1
  172. package/node_modules/@shennian/wire/dist/frames.js.map +0 -1
  173. package/node_modules/@shennian/wire/dist/fs.d.ts.map +0 -1
  174. package/node_modules/@shennian/wire/dist/fs.js.map +0 -1
  175. package/node_modules/@shennian/wire/dist/index.d.ts.map +0 -1
  176. package/node_modules/@shennian/wire/dist/index.js.map +0 -1
  177. package/node_modules/@shennian/wire/dist/machine.d.ts.map +0 -1
  178. package/node_modules/@shennian/wire/dist/machine.js.map +0 -1
  179. package/node_modules/@shennian/wire/dist/message-payload.d.ts.map +0 -1
  180. package/node_modules/@shennian/wire/dist/message-payload.js.map +0 -1
  181. package/node_modules/@shennian/wire/dist/methods.d.ts.map +0 -1
  182. package/node_modules/@shennian/wire/dist/methods.js.map +0 -1
  183. package/node_modules/@shennian/wire/dist/mock/fixtures.d.ts +0 -39
  184. package/node_modules/@shennian/wire/dist/mock/fixtures.d.ts.map +0 -1
  185. package/node_modules/@shennian/wire/dist/mock/fixtures.js +0 -99
  186. package/node_modules/@shennian/wire/dist/mock/fixtures.js.map +0 -1
  187. package/node_modules/@shennian/wire/dist/mock/index.d.ts +0 -3
  188. package/node_modules/@shennian/wire/dist/mock/index.d.ts.map +0 -1
  189. package/node_modules/@shennian/wire/dist/mock/index.js +0 -4
  190. package/node_modules/@shennian/wire/dist/mock/index.js.map +0 -1
  191. package/node_modules/@shennian/wire/dist/mock/routes.d.ts +0 -96
  192. package/node_modules/@shennian/wire/dist/mock/routes.d.ts.map +0 -1
  193. package/node_modules/@shennian/wire/dist/mock/routes.js +0 -40
  194. package/node_modules/@shennian/wire/dist/mock/routes.js.map +0 -1
  195. package/node_modules/@shennian/wire/dist/native-session.d.ts.map +0 -1
  196. package/node_modules/@shennian/wire/dist/native-session.js.map +0 -1
  197. package/node_modules/@shennian/wire/dist/project.d.ts.map +0 -1
  198. package/node_modules/@shennian/wire/dist/project.js.map +0 -1
  199. package/node_modules/@shennian/wire/dist/queue.d.ts +0 -15
  200. package/node_modules/@shennian/wire/dist/queue.d.ts.map +0 -1
  201. package/node_modules/@shennian/wire/dist/queue.js +0 -2
  202. package/node_modules/@shennian/wire/dist/queue.js.map +0 -1
  203. package/node_modules/@shennian/wire/dist/room.d.ts.map +0 -1
  204. package/node_modules/@shennian/wire/dist/room.js.map +0 -1
  205. package/node_modules/@shennian/wire/dist/session.d.ts.map +0 -1
  206. package/node_modules/@shennian/wire/dist/session.js.map +0 -1
  207. package/node_modules/@shennian/wire/dist/skills.d.ts +0 -61
  208. package/node_modules/@shennian/wire/dist/skills.d.ts.map +0 -1
  209. package/node_modules/@shennian/wire/dist/skills.js +0 -2
  210. package/node_modules/@shennian/wire/dist/skills.js.map +0 -1
  211. package/node_modules/@shennian/wire/dist/tool-detail.d.ts.map +0 -1
  212. package/node_modules/@shennian/wire/dist/tool-detail.js.map +0 -1
  213. package/node_modules/@shennian/wire/dist/voice-chief.d.ts.map +0 -1
  214. package/node_modules/@shennian/wire/dist/voice-chief.js.map +0 -1
@@ -1,144 +0,0 @@
1
- import type { RoomActivationMode, RoomBindingStatus, RoomRuntimeAdapter } from '@shennian/wire';
2
- export type ManagedRoomSessionAgent = 'codex' | 'claude' | 'workbuddy';
3
- export type ManagedRoomSessionInput = {
4
- inviteUrl: string;
5
- agent: ManagedRoomSessionAgent;
6
- workDir: string;
7
- alias: string;
8
- mode?: RoomActivationMode;
9
- modelId?: string;
10
- };
11
- export type ManagedRoomSessionPhase = 'initialized' | 'session_created' | 'native_session_ready' | 'session_registered' | 'joined';
12
- export type ManagedRoomSessionFailureStage = 'validate_input' | 'create_session' | 'wait_for_native_session' | 'register_session' | 'join_room';
13
- type NormalizedInput = Required<Pick<ManagedRoomSessionInput, 'inviteUrl' | 'agent' | 'workDir' | 'alias' | 'mode'>> & {
14
- modelId: string | null;
15
- };
16
- export type ManagedRoomSessionRegistration = {
17
- registryKey: string;
18
- };
19
- export type ManagedRoomJoinResult = {
20
- outcome: 'joined' | 'pending_approval';
21
- room: {
22
- id: string;
23
- name: string;
24
- webUrl: string;
25
- };
26
- binding: {
27
- id: string;
28
- agentIdentityId: string;
29
- agentMembershipId: string | null;
30
- status: RoomBindingStatus;
31
- activationMode: RoomActivationMode;
32
- };
33
- idempotent: boolean;
34
- };
35
- export type ManagedRoomSessionOperation = {
36
- schemaVersion: 1;
37
- operationId: string;
38
- inputFingerprint: string;
39
- input: NormalizedInput;
40
- runtimeAdapter: RoomRuntimeAdapter;
41
- phase: ManagedRoomSessionPhase;
42
- nianSessionId: string | null;
43
- sourceSessionKey: string | null;
44
- hostProcessIdentity: string | null;
45
- registryKey: string | null;
46
- result: ManagedRoomJoinResult | null;
47
- createdAt: string;
48
- updatedAt: string;
49
- };
50
- export interface ManagedRoomSessionOperationStore {
51
- load(operationId: string): ManagedRoomSessionOperation | null;
52
- save(operation: ManagedRoomSessionOperation): void;
53
- }
54
- export type ManagedRoomSessionDependencies = {
55
- operations: ManagedRoomSessionOperationStore;
56
- /**
57
- * Must be idempotent for creationKey. A successful call followed by a daemon
58
- * crash may be repeated before the operation record reaches disk.
59
- */
60
- createManagedSession(input: {
61
- creationKey: string;
62
- agent: ManagedRoomSessionAgent;
63
- workDir: string;
64
- modelId: string | null;
65
- initialMessage: string;
66
- }): Promise<{
67
- nianSessionId: string;
68
- }>;
69
- /** Resolves only the native Session created for nianSessionId; never a recent Session. */
70
- waitForNativeSession(input: {
71
- nianSessionId: string;
72
- runtimeAdapter: RoomRuntimeAdapter;
73
- timeoutMs: number;
74
- }): Promise<{
75
- sourceSessionKey: string;
76
- hostProcessIdentity: string;
77
- }>;
78
- /** Persists the trusted observation and exact managed Session Registry record. */
79
- registerManagedSession(input: {
80
- nianSessionId: string;
81
- runtimeAdapter: RoomRuntimeAdapter;
82
- sourceSessionKey: string;
83
- hostProcessIdentity: string;
84
- workDir: string;
85
- capabilities: {
86
- canIdentifySession: true;
87
- canBindAutomatically: true;
88
- canOpenEmbeddedRoomPage: false;
89
- canResumeIdleSession: true;
90
- canActivateWhileHostOpen: true;
91
- };
92
- }): Promise<ManagedRoomSessionRegistration>;
93
- /**
94
- * Uses daemon-owned machine credentials internally. Public callers never
95
- * supply Room, Binding, Identity, native Session, or machine credential IDs.
96
- */
97
- joinRegisteredSession(input: {
98
- bindingIntentId: string;
99
- registryKey: string;
100
- inviteUrl: string;
101
- agentAlias: string;
102
- agentDescription: string;
103
- activationMode: RoomActivationMode;
104
- activationCapability: 'managed';
105
- }): Promise<ManagedRoomJoinResult>;
106
- allowedInviteOrigins?: string[];
107
- nativeSessionTimeoutMs?: number;
108
- now?: () => Date;
109
- };
110
- export declare class ManagedRoomSessionError extends Error {
111
- readonly code: string;
112
- readonly stage: ManagedRoomSessionFailureStage;
113
- readonly operationId: string | null;
114
- readonly phase: ManagedRoomSessionPhase;
115
- readonly nianSessionId: string | null;
116
- constructor(code: string, message: string, stage: ManagedRoomSessionFailureStage, operationId: string | null, phase: ManagedRoomSessionPhase, nianSessionId: string | null);
117
- }
118
- export declare class ManagedRoomSessionOrchestrator {
119
- private readonly deps;
120
- private readonly mutexTails;
121
- constructor(deps: ManagedRoomSessionDependencies);
122
- execute(operationId: string, rawInput: ManagedRoomSessionInput): Promise<{
123
- operationId: string;
124
- phase: 'joined';
125
- nianSessionId: string;
126
- result: ManagedRoomJoinResult;
127
- }>;
128
- private executeExclusive;
129
- private save;
130
- }
131
- export declare class InMemoryManagedRoomSessionOperationStore implements ManagedRoomSessionOperationStore {
132
- private readonly records;
133
- load(operationId: string): ManagedRoomSessionOperation | null;
134
- save(operation: ManagedRoomSessionOperation): void;
135
- }
136
- /** Private per-operation files allow daemon restart recovery without one operation overwriting another. */
137
- export declare class FileManagedRoomSessionOperationStore implements ManagedRoomSessionOperationStore {
138
- readonly directory: string;
139
- constructor(directory: string);
140
- load(operationId: string): ManagedRoomSessionOperation | null;
141
- save(operation: ManagedRoomSessionOperation): void;
142
- private filePath;
143
- }
144
- export {};
@@ -1,2 +0,0 @@
1
- import S from"node:crypto";import a from"node:fs";import y from"node:path";class h extends Error{code;stage;operationId;phase;nianSessionId;constructor(i,o,t,r,n,s){super(o),this.code=i,this.stage=t,this.operationId=r,this.phase=n,this.nianSessionId=s,this.name="ManagedRoomSessionError"}}const j=/^[A-Za-z0-9_-]{8,96}$/,R=/^[A-Za-z0-9_.:@/-]{1,512}$/,w=/^[a-f0-9]{64}$/,D=/^[A-Za-z0-9_-]{43}$/,k=new Set(["inviteUrl","agent","workDir","alias","mode","modelId"]),K=new Set(["codex","claude","workbuddy"]),$=new Set(["auto_follow","mention_only","paused"]),N=["https://shennian.net"],P=3e4;class B{deps;mutexTails=new Map;constructor(i){this.deps=i}async execute(i,o){_(i);let t;const r=this.mutexTails.get(i)??Promise.resolve(),n=new Promise(l=>{t=l}),s=r.then(()=>n);this.mutexTails.set(i,s),await r;try{return await this.executeExclusive(i,o)}finally{t(),this.mutexTails.get(i)===s&&this.mutexTails.delete(i)}}async executeExclusive(i,o){let t;try{t=I(o,this.deps.allowedInviteOrigins??N)}catch(s){throw g(s,"validate_input",i,null)}const r=b(t);let n=this.deps.operations.load(i);if(n&&n.inputFingerprint!==r)throw new h("managed_room_operation_conflict","This operation ID was already used with different inputs.","validate_input",i,n.phase,n.nianSessionId);if(!n){const s=(this.deps.now?.()??new Date).toISOString();n={schemaVersion:1,operationId:i,inputFingerprint:r,input:t,runtimeAdapter:v(t.agent),phase:"initialized",nianSessionId:null,sourceSessionKey:null,hostProcessIdentity:null,registryKey:null,result:null,createdAt:s,updatedAt:s},this.save(n)}if(n.phase==="joined")return E(n);if(n.phase==="initialized")try{const s=await this.deps.createManagedSession({creationKey:`room-managed-session:${i}`,agent:t.agent,workDir:t.workDir,modelId:t.modelId,initialMessage:U()});c(s.nianSessionId,"nian_session_id"),n.nianSessionId=s.nianSessionId,n.phase="session_created",this.save(n)}catch(s){throw g(s,"create_session",i,n)}if(n.phase==="session_created")try{const s=await this.deps.waitForNativeSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,timeoutMs:this.deps.nativeSessionTimeoutMs??P});A(s.sourceSessionKey),c(s.hostProcessIdentity,"host_process_identity"),n.sourceSessionKey=s.sourceSessionKey,n.hostProcessIdentity=s.hostProcessIdentity,n.phase="native_session_ready",this.save(n)}catch(s){throw g(s,"wait_for_native_session",i,n)}if(n.phase==="native_session_ready")try{const s=await this.deps.registerManagedSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostProcessIdentity:n.hostProcessIdentity,workDir:t.workDir,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:!0,canActivateWhileHostOpen:!0}});if(!w.test(s.registryKey))throw u("registry_key");n.registryKey=s.registryKey,n.phase="session_registered",this.save(n)}catch(s){throw g(s,"register_session",i,n)}if(n.phase==="session_registered")try{n.result=await this.deps.joinRegisteredSession({bindingIntentId:`room-managed-session-join:${i}`,registryKey:n.registryKey,inviteUrl:t.inviteUrl,agentAlias:t.alias,agentDescription:`${t.alias}\uFF08\u53D7\u7BA1 ${t.agent} Agent\uFF09`,activationMode:t.mode,activationCapability:"managed"}),O(n.result),n.phase="joined",this.save(n)}catch(s){throw g(s,"join_room",i,n)}return E(n)}save(i){i.updatedAt=(this.deps.now?.()??new Date).toISOString(),this.deps.operations.save(m(i))}}class G{records=new Map;load(i){const o=this.records.get(i);return o?m(o):null}save(i){this.records.set(i.operationId,m(i))}}class Z{directory;constructor(i){this.directory=i}load(i){_(i);const o=this.filePath(i);try{const t=a.lstatSync(o);if(!t.isFile()||t.isSymbolicLink())throw p();process.platform!=="win32"&&(t.mode&63)!==0&&a.chmodSync(o,384);const r=JSON.parse(a.readFileSync(o,"utf8"));return M(r,i),m(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}save(i){_(i.operationId),M(i,i.operationId),a.mkdirSync(this.directory,{recursive:!0,mode:448});const o=a.lstatSync(this.directory);if(!o.isDirectory()||o.isSymbolicLink())throw p();process.platform!=="win32"&&a.chmodSync(this.directory,448);const t=this.filePath(i.operationId),r=`${t}.tmp-${process.pid}-${S.randomBytes(6).toString("hex")}`;try{if(a.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
- `,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&a.chmodSync(r,384),process.platform==="win32"&&a.existsSync(t)){const n=a.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw p();a.unlinkSync(t)}a.renameSync(r,t)}finally{try{a.unlinkSync(r)}catch{}}}filePath(i){return y.join(this.directory,`${i}.json`)}}function I(e,i){(!e||typeof e!="object"||Array.isArray(e))&&d("input");const o=e;Object.keys(o).find(T=>!k.has(T))&&d("unsupported_field");const r=o.agent;(typeof r!="string"||!K.has(r))&&d("agent");const n=f(o.workDir,"work_dir",4096);(!y.isAbsolute(n)||y.normalize(n)!==n)&&d("work_dir");const s=f(o.alias,"alias",80),l=o.mode??"auto_follow";(typeof l!="string"||!$.has(l))&&d("mode");const x=o.modelId==null?null:f(o.modelId,"model_id",191);return{inviteUrl:F(o.inviteUrl,i),agent:r,workDir:n,alias:s,mode:l,modelId:x}}function F(e,i){const o=f(e,"invite_url",2048);let t;try{t=new URL(o)}catch{d("invite_url")}(!new Set(i.map(l=>new URL(l).origin)).has(t.origin)||t.username||t.password||t.search||t.hash)&&d("invite_url");const n=t.pathname.split("/").filter(Boolean),s=n.at(-1);return(!s||!D.test(s)||n.at(-2)!=="join")&&d("invite_url"),t.toString()}function f(e,i,o){typeof e!="string"&&d(i);const t=e.trim();return(!t||t.length>o||/[\0\r\n]/.test(t))&&d(i),t}function _(e){if(!j.test(e))throw new h("invalid_managed_room_operation_id","Managed Room operation ID is invalid.","validate_input",null,"initialized",null)}function v(e){return e==="claude"?"claude_code":e}function b(e){return S.createHash("sha256").update(JSON.stringify(e),"utf8").digest("hex")}function c(e,i){if(!R.test(e))throw u(i)}function A(e){if(!e||e.length>1024||/[\0\r\n]/.test(e))throw u("source_session_key")}function O(e){if(c(e.room?.id,"room_id"),c(e.binding?.id,"binding_id"),c(e.binding?.agentIdentityId,"agent_identity_id"),e.binding.agentMembershipId!==null&&c(e.binding.agentMembershipId,"agent_membership_id"),e.outcome!=="joined"&&e.outcome!=="pending_approval")throw u("join_outcome");if(e.outcome==="joined"&&e.binding.status!=="active"&&e.binding.status!=="paused"||e.outcome==="pending_approval"&&e.binding.status!=="pending_approval"||e.binding.status==="pending_approval"&&e.binding.agentMembershipId!==null||(e.binding.status==="active"||e.binding.status==="paused")&&e.binding.agentMembershipId===null)throw u("join_binding_state")}function U(){return["\u8FD9\u662F\u4E00\u4E2A\u7531\u795E\u5FF5\u521B\u5EFA\u7684\u53D7\u7BA1 Agent \u5BF9\u8BDD\u3002","\u8BF7\u5148\u4FDD\u6301\u7A7A\u95F2\uFF1B\u795E\u5FF5\u6B63\u5728\u4E3A\u8BE5\u7CBE\u786E\u5BF9\u8BDD\u5EFA\u7ACB\u7FA4\u804A\u8FDE\u63A5\uFF0C\u5B8C\u6210\u540E\u4F1A\u53D1\u9001\u540E\u7EED\u6D88\u606F\u3002"].join(" ")}function E(e){if(!e.nianSessionId||!e.result||e.phase!=="joined")throw u("operation_state");return{operationId:e.operationId,phase:"joined",nianSessionId:e.nianSessionId,result:m(e.result)}}function g(e,i,o,t){if(e instanceof h)return e;const r=e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"?e.code:`managed_room_${i}_failed`;return new h(r,`Managed Room Session failed during ${i}. The created conversation was preserved for retry.`,i,o,t?.phase??"initialized",t?.nianSessionId??null)}function d(e){throw new h(`invalid_managed_room_${e}`,`Managed Room Session ${e} is invalid.`,"validate_input",null,"initialized",null)}function u(e){return Object.assign(new Error(`Managed Room dependency returned an invalid ${e}.`),{code:`invalid_managed_room_${e}`})}function M(e,i){if(!e||e.schemaVersion!==1||e.operationId!==i||!/^[a-f0-9]{64}$/.test(e.inputFingerprint)||!["initialized","session_created","native_session_ready","session_registered","joined"].includes(e.phase)||!e.input||v(e.input.agent)!==e.runtimeAdapter)throw p();const o=I(e.input,[new URL(e.input.inviteUrl).origin]);if(b(o)!==e.inputFingerprint||(e.nianSessionId!==null&&c(e.nianSessionId,"nian_session_id"),e.sourceSessionKey!==null&&A(e.sourceSessionKey),e.hostProcessIdentity!==null&&c(e.hostProcessIdentity,"host_process_identity"),e.registryKey!==null&&!w.test(e.registryKey)))throw p();e.result!==null&&O(e.result)}function p(){return Object.assign(new Error("Managed Room Session operation store is unsafe or corrupt."),{code:"unsafe_managed_room_operation_store"})}function m(e){return JSON.parse(JSON.stringify(e))}export{Z as FileManagedRoomSessionOperationStore,G as InMemoryManagedRoomSessionOperationStore,h as ManagedRoomSessionError,B as ManagedRoomSessionOrchestrator};
@@ -1,13 +0,0 @@
1
- import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
2
- import type { RoomSessionRegistry } from './session-registry.js';
3
- import type { HttpRoomActionClient } from './room-action-client.js';
4
- import type { ManagedRoomSessionOrchestrator } from './managed-room-session.js';
5
- export declare function createRoomOperatorIpcHandlers(input: {
6
- registry: Pick<RoomSessionRegistry, 'list'>;
7
- actionClient: Pick<HttpRoomActionClient, 'createOperatorRoom'>;
8
- getMachineCredentials: () => {
9
- machineId?: string;
10
- machineToken?: string;
11
- };
12
- managedRoomSessions?: Pick<ManagedRoomSessionOrchestrator, 'execute'>;
13
- }): Record<string, DaemonIpcHandler>;
@@ -1 +0,0 @@
1
- import{DaemonIpcError as r}from"../daemon-ipc/protocol.js";import h from"node:crypto";import f from"node:fs";import p from"node:path";function R(e){return{"room-operator.inspect":t=>{const o=y(t),n=e.getMachineCredentials(),i=n.machineId?.trim(),l=!!(i&&n.machineToken?.trim()),a=i?e.registry.list().filter(c=>c.machineId===i):[],d=a.filter(c=>c.binding!==null),s=b(d);return g(o==="status"?{source:"local_registry",signedIn:l,registeredSessions:a.length,boundSessions:d.length,rooms:s.length}:{source:"local_registry",complete:!1,rooms:s})},"room-operator.create":async t=>{const o=_(t),n=e.getMachineCredentials();if(!n.machineId?.trim()||!n.machineToken?.trim())throw new r("cli_not_signed_in","Sign in to the Shennian CLI first.");try{return g(await e.actionClient.createOperatorRoom({machineToken:n.machineToken,name:o}))}catch(i){throw u(i)}},"room-operator.agent-create":async(t,o)=>{if(!e.managedRoomSessions)throw new r("managed_room_unavailable","Managed Room Agent creation is unavailable.");const n=e.getMachineCredentials(),i=n.machineId?.trim();if(!i||!n.machineToken?.trim())throw new r("cli_not_signed_in","Sign in to the Shennian CLI first.");const l=v(t),d=I(t)??`room_agent_${h.createHash("sha256").update(`${i}\0${o.requestId}`,"utf8").digest("hex").slice(0,48)}`;try{return g(await e.managedRoomSessions.execute(d,l))}catch(s){throw u(s)}}}}function g(e){return JSON.parse(JSON.stringify(e))}function y(e){if(!e||Array.isArray(e)||typeof e!="object")throw new r("invalid_params","Room operator parameters are required.");const t=e;if(Object.keys(t).length!==1||t.action!=="status"&&t.action!=="list")throw new r("invalid_params","Room operator action is invalid.");return t.action}function _(e){if(!e||Array.isArray(e)||typeof e!="object")throw new r("invalid_params","Room operator parameters are required.");const t=e;if(Object.keys(t).length!==1||typeof t.name!="string")throw new r("invalid_params","Room operator create parameters are invalid.");const o=t.name.trim();if(!o||o.length>80)throw new r("invalid_params","Room name must contain 1 to 80 characters.");return o}function v(e){if(!e||Array.isArray(e)||typeof e!="object")throw new r("invalid_params","Managed Agent parameters are required.");const t=e,o=new Set(["room","agent","workDir","modelId","alias","activationMode","operationId"]);if(Object.keys(t).some(w=>!o.has(w)))throw new r("invalid_params","Managed Agent parameters are invalid.");const n=m(t.room,"room",2048),i=m(t.agent,"agent",32),l=m(t.alias,"alias",80),a=m(t.activationMode,"activationMode",32),d=m(t.workDir,"workDir",4096);if(!p.isAbsolute(d))throw new r("invalid_workdir","Managed Agent workDir must be absolute.");let s;try{if(s=f.realpathSync(d),!f.statSync(s).isDirectory())throw new Error("not-directory")}catch{throw new r("invalid_workdir","Managed Agent workDir must be an existing directory.")}if(i!=="codex"&&i!=="claude"&&i!=="workbuddy")throw new r("invalid_params","Managed Agent runtime is invalid.");if(a!=="auto_follow"&&a!=="mention_only"&&a!=="paused")throw new r("invalid_params","Managed Agent activation mode is invalid.");const c=t.modelId===void 0?void 0:m(t.modelId,"modelId",191);return{inviteUrl:n,agent:i,workDir:s,alias:l,mode:a,...c?{modelId:c}:{}}}function I(e){if(!e||Array.isArray(e)||typeof e!="object")return null;const t=e.operationId;if(t===void 0)return null;if(typeof t!="string"||!/^room_agent_[a-f0-9]{48}$/.test(t))throw new r("invalid_params","Managed Agent operationId is invalid.");return t}function m(e,t,o){if(typeof e!="string"||!e.trim()||e.length>o||/[\0\r\n]/.test(e))throw new r("invalid_params",`${t} is invalid.`);return e.trim()}function u(e){return e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"&&"message"in e&&typeof e.message=="string"?new r(e.code,e.message):new r("room_operator_failed","Shennian Room operator action failed.")}function b(e){const t=new Map;for(const o of e){const n=o.binding;if(!n)continue;const i=t.get(n.roomId)??{sessions:0,runtimes:new Set,statuses:new Set,modes:new Set};i.sessions+=1,i.runtimes.add(o.runtimeAdapter),i.statuses.add(n.status),i.modes.add(n.activationMode),t.set(n.roomId,i)}return[...t.entries()].sort(([o],[n])=>o.localeCompare(n)).map(([o,n])=>({roomId:o,sessions:n.sessions,runtimes:[...n.runtimes].sort(),bindingStatuses:[...n.statuses].sort(),activationModes:[...n.modes].sort()}))}export{R as createRoomOperatorIpcHandlers};
@@ -1,69 +0,0 @@
1
- export type AuthorizedActionStatus = 'waiting_user' | 'approved' | 'executing' | 'completed' | 'failed' | 'expired';
2
- export type JsonValue = null | boolean | number | string | JsonValue[] | {
3
- [key: string]: JsonValue;
4
- };
5
- export type PendingAuthorizedAction = {
6
- schemaVersion: 1;
7
- id: string;
8
- loginIntentId: string;
9
- publicLoginUrl: string;
10
- actionType: string;
11
- canonicalActionPayload: JsonValue;
12
- actionPayloadHash: string;
13
- idempotencyKey: string;
14
- deviceSecretHash: string;
15
- status: AuthorizedActionStatus;
16
- expiresAt: string;
17
- createdAt: string;
18
- updatedAt: string;
19
- browserOpenedAt?: string;
20
- approvedAt?: string;
21
- executingAt?: string;
22
- completedAt?: string;
23
- failedAt?: string;
24
- errorCode?: string;
25
- result?: JsonValue;
26
- };
27
- type CreatePendingActionInput = {
28
- id?: string;
29
- loginIntentId: string;
30
- publicLoginUrl: string;
31
- actionType: string;
32
- actionPayload: JsonValue;
33
- idempotencyKey: string;
34
- deviceSecret: string;
35
- expiresAt: string;
36
- now?: Date;
37
- };
38
- export declare class PendingAuthorizedActionError extends Error {
39
- readonly code: string;
40
- constructor(code: string, message: string);
41
- }
42
- export declare function canonicalizeActionPayload(value: JsonValue): string;
43
- export declare function hashActionPayload(value: JsonValue): string;
44
- export declare function hashPendingDeviceSecret(secret: string): string;
45
- export declare class PendingAuthorizedActionStore {
46
- readonly directory: string;
47
- constructor(rootDir?: string);
48
- create(input: CreatePendingActionInput): PendingAuthorizedAction;
49
- get(id: string): PendingAuthorizedAction | null;
50
- list(): PendingAuthorizedAction[];
51
- findByIdempotencyKey(idempotencyKey: string): PendingAuthorizedAction | null;
52
- listRecoverable(now?: Date): PendingAuthorizedAction[];
53
- readDeviceSecret(id: string): string;
54
- markApproved(id: string, now?: Date): PendingAuthorizedAction;
55
- markBrowserOpened(id: string, now?: Date): PendingAuthorizedAction;
56
- markExecuting(id: string, now?: Date): PendingAuthorizedAction;
57
- markCompleted(id: string, result: JsonValue, now?: Date): PendingAuthorizedAction;
58
- markFailed(id: string, errorCode: string, now?: Date): PendingAuthorizedAction;
59
- private transition;
60
- private update;
61
- private ensureDirectory;
62
- private recordPath;
63
- private secretPath;
64
- private writeRecord;
65
- private writeSecret;
66
- private atomicWrite;
67
- private removeSecret;
68
- }
69
- export {};
@@ -1,3 +0,0 @@
1
- import d from"node:crypto";import c from"node:fs";import m from"node:path";import{resolveShennianPath as b}from"../config/index.js";const u=/^[A-Za-z0-9_-]{8,128}$/,w=/^[a-z][a-z0-9_.-]{0,63}$/,S=/^[a-f0-9]{64}$/,f=new Set(["completed","failed","expired"]);class r extends Error{code;constructor(e,t){super(t),this.code=e,this.name="PendingAuthorizedActionError"}}function I(i){return l(i)}function _(i){return d.createHash("sha256").update(I(i),"utf8").digest("hex")}function v(i){return d.createHash("sha256").update(i,"utf8").digest("hex")}class k{directory;constructor(e=b("runtime","pending-authorized-actions")){this.directory=e}create(e){if(y(e.loginIntentId,"loginIntentId"),O(e.publicLoginUrl,e.loginIntentId),!w.test(e.actionType))throw new r("invalid_action_type","Invalid authorized action type.");if(!e.idempotencyKey.trim()||e.idempotencyKey.length>191)throw new r("invalid_idempotency_key","Invalid idempotency key.");if(e.deviceSecret.length<32||e.deviceSecret.length>256)throw new r("invalid_device_secret","Invalid device secret.");const t=new Date(e.expiresAt);if(!Number.isFinite(t.getTime()))throw new r("invalid_expiry","Invalid authorization expiry.");this.ensureDirectory();const n=x(e.actionPayload),o=_(n),a=this.list().find(p=>p.idempotencyKey===e.idempotencyKey);if(a){if(a.actionType!==e.actionType||a.actionPayloadHash!==o)throw new r("idempotency_payload_conflict","The idempotency key is already bound to a different action.");return a}const s=(e.now??new Date).toISOString(),h=e.id??d.randomUUID();y(h,"id");const g={schemaVersion:1,id:h,loginIntentId:e.loginIntentId,publicLoginUrl:e.publicLoginUrl,actionType:e.actionType,canonicalActionPayload:n,actionPayloadHash:o,idempotencyKey:e.idempotencyKey,deviceSecretHash:v(e.deviceSecret),status:"waiting_user",expiresAt:t.toISOString(),createdAt:s,updatedAt:s};this.writeSecret(h,e.deviceSecret);try{this.writeRecord(g)}catch(p){throw this.removeSecret(h),p}return g}get(e){y(e,"id");try{const t=this.recordPath(e),n=c.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw new r("unsafe_pending_action","Pending action is not a regular file.");return A(JSON.parse(c.readFileSync(t,"utf8")))}catch(t){if(N(t))return null;throw t instanceof r?t:new r("corrupt_pending_action","Pending authorized action is corrupt.")}}list(){return this.ensureDirectory(),c.readdirSync(this.directory).filter(e=>e.endsWith(".json")&&!e.includes(".tmp-")).map(e=>this.get(e.slice(0,-5))).filter(e=>e!==null).sort((e,t)=>e.createdAt.localeCompare(t.createdAt))}findByIdempotencyKey(e){return this.list().find(t=>t.idempotencyKey===e)??null}listRecoverable(e=new Date){const t=[];for(const n of this.list())if(!f.has(n.status)){if(new Date(n.expiresAt)<=e){this.update(n.id,{status:"expired",errorCode:"authorization_expired"},e),this.removeSecret(n.id);continue}t.push(n)}return t}readDeviceSecret(e){const t=this.get(e);if(!t||f.has(t.status))throw new r("pending_action_unavailable","Pending action is unavailable.");let n;try{const o=this.secretPath(e),a=c.lstatSync(o);if(!a.isFile()||a.isSymbolicLink())throw new r("unsafe_device_secret","Device authorization secret is not a regular file.");n=c.readFileSync(o,"utf8").trim()}catch(o){throw o instanceof r?o:new r("device_secret_missing","Device authorization secret is missing.")}if(!$(n,t.deviceSecretHash))throw new r("device_secret_mismatch","Device authorization secret is invalid.");return n}markApproved(e,t=new Date){return this.transition(e,["waiting_user","approved"],"approved",{approvedAt:t.toISOString()},t)}markBrowserOpened(e,t=new Date){const n=this.get(e);if(!n)throw new r("pending_action_not_found","Pending action not found.");return n.browserOpenedAt||n.status!=="waiting_user"?n:this.update(e,{browserOpenedAt:t.toISOString()},t)}markExecuting(e,t=new Date){const n=this.transition(e,["approved"],"executing",{executingAt:t.toISOString()},t);return this.removeSecret(e),n}markCompleted(e,t,n=new Date){const o=this.transition(e,["executing","completed"],"completed",{completedAt:n.toISOString(),result:t,errorCode:void 0},n);return this.removeSecret(e),o}markFailed(e,t,n=new Date){const o=this.transition(e,["waiting_user","approved","executing","failed"],"failed",{failedAt:n.toISOString(),errorCode:t.slice(0,120)},n);return this.removeSecret(e),o}transition(e,t,n,o,a){const s=this.get(e);if(!s)throw new r("pending_action_not_found","Pending action not found.");if(!t.includes(s.status))throw new r("invalid_pending_action_transition",`Cannot transition ${s.status} to ${n}.`);return s.status===n&&f.has(n)?s:this.update(e,{...o,status:n},a)}update(e,t,n){const o=this.get(e);if(!o)throw new r("pending_action_not_found","Pending action not found.");const a=A({...o,...t,id:o.id,updatedAt:n.toISOString()});return this.writeRecord(a),a}ensureDirectory(){c.mkdirSync(this.directory,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(this.directory,448)}recordPath(e){return m.join(this.directory,`${e}.json`)}secretPath(e){return m.join(this.directory,`${e}.secret`)}writeRecord(e){this.atomicWrite(this.recordPath(e.id),`${JSON.stringify(e,null,2)}
2
- `)}writeSecret(e,t){this.atomicWrite(this.secretPath(e),`${t}
3
- `)}atomicWrite(e,t){this.ensureDirectory();const n=`${e}.tmp-${process.pid}-${d.randomBytes(6).toString("hex")}`;try{c.writeFileSync(n,t,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&c.chmodSync(n,384),c.renameSync(n,e),process.platform!=="win32"&&c.chmodSync(e,384)}finally{try{c.unlinkSync(n)}catch{}}}removeSecret(e){try{c.unlinkSync(this.secretPath(e))}catch{}}}function l(i){if(i===null||typeof i=="boolean"||typeof i=="string")return JSON.stringify(i);if(typeof i=="number"){if(!Number.isFinite(i))throw new r("invalid_action_payload","Numbers must be finite.");return JSON.stringify(i)}if(Array.isArray(i))return`[${i.map(l).join(",")}]`;if(typeof i!="object")throw new r("invalid_action_payload","Action payload must be JSON.");const e=Object.getPrototypeOf(i);if(e!==Object.prototype&&e!==null)throw new r("invalid_action_payload","Action payload must be plain JSON.");return`{${Object.keys(i).sort().map(t=>`${JSON.stringify(t)}:${l(i[t])}`).join(",")}}`}function x(i){return JSON.parse(l(i))}function y(i,e){if(!u.test(i))throw new r(`invalid_${e}`,`Invalid ${e}.`)}function A(i){if(!i||typeof i!="object")throw new r("corrupt_pending_action","Pending action must be an object.");const e=i;if(e.schemaVersion!==1||typeof e.id!="string"||!u.test(e.id)||typeof e.loginIntentId!="string"||!u.test(e.loginIntentId)||typeof e.publicLoginUrl!="string"||!P(e.publicLoginUrl,e.loginIntentId)||typeof e.actionType!="string"||!w.test(e.actionType)||typeof e.idempotencyKey!="string"||!e.idempotencyKey||typeof e.actionPayloadHash!="string"||!S.test(e.actionPayloadHash)||typeof e.deviceSecretHash!="string"||!S.test(e.deviceSecretHash)||!["waiting_user","approved","executing","completed","failed","expired"].includes(e.status??"")||typeof e.expiresAt!="string"||!Number.isFinite(new Date(e.expiresAt).getTime())||typeof e.createdAt!="string"||typeof e.updatedAt!="string")throw new r("corrupt_pending_action","Pending action has invalid fields.");if(e.browserOpenedAt!==void 0&&(typeof e.browserOpenedAt!="string"||!Number.isFinite(new Date(e.browserOpenedAt).getTime())))throw new r("corrupt_pending_action","Pending action has invalid browser state.");if(_(e.canonicalActionPayload)!==e.actionPayloadHash)throw new r("action_payload_tampered","Pending action payload hash mismatch.");return e}function O(i,e){if(!P(i,e))throw new r("invalid_public_login_url","Invalid Shennian authorization URL.")}function P(i,e){try{const t=new URL(i);return t.pathname!==`/authorize/${e}`||t.search||t.hash?!1:t.protocol==="https:"&&(t.hostname==="shennian.net"||t.hostname.endsWith(".shennian.net"))?!0:t.protocol==="http:"&&D(t.hostname)}catch{return!1}}function D(i){return i==="127.0.0.1"||i==="localhost"||i==="::1"||/^10\./.test(i)||/^192\.168\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i)}function $(i,e){const t=Buffer.from(v(i),"hex"),n=Buffer.from(e,"hex");return t.length===n.length&&d.timingSafeEqual(t,n)}function N(i){return typeof i=="object"&&i!==null&&"code"in i&&i.code==="ENOENT"}export{r as PendingAuthorizedActionError,k as PendingAuthorizedActionStore,I as canonicalizeActionPayload,_ as hashActionPayload,v as hashPendingDeviceSecret};
@@ -1,34 +0,0 @@
1
- import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
2
- import { RoomInvocationTokenAuthority } from './invocation-token.js';
3
- import { type RoomAttachmentCache } from './attachment-cache.js';
4
- import { HttpRoomActionClient } from './room-action-client.js';
5
- import { RoomSessionRegistry } from './session-registry.js';
6
- import { type RoomAttachmentUploader } from './attachment-upload.js';
7
- import type { RoomHostSessionObservationStore } from './host-session-observations.js';
8
- type RoomMirrorWriter = {
9
- readonly messagesPath?: string;
10
- append(input: {
11
- messages: Awaited<ReturnType<HttpRoomActionClient['read']>>['messages'];
12
- historyFromSequence: string;
13
- roomName?: string | null;
14
- }): unknown;
15
- markAccessRevoked(): void;
16
- search(input: {
17
- query: string;
18
- limit?: number;
19
- }): unknown;
20
- };
21
- export declare function createRoomActionIpcHandlers(input: {
22
- authority: RoomInvocationTokenAuthority;
23
- registry: RoomSessionRegistry;
24
- actionClient: Pick<HttpRoomActionClient, 'createRoom' | 'joinRoom' | 'read' | 'send' | 'mode' | 'status'>;
25
- getMachineCredentials: () => {
26
- machineId?: string;
27
- machineToken?: string;
28
- };
29
- mirrorFactory?: (roomId: string) => RoomMirrorWriter;
30
- attachmentCache?: Pick<RoomAttachmentCache, 'materialize'>;
31
- attachmentUploader?: Pick<RoomAttachmentUploader, 'upload'>;
32
- observations?: RoomHostSessionObservationStore;
33
- }): Record<string, DaemonIpcHandler>;
34
- export {};
@@ -1 +0,0 @@
1
- import{DaemonIpcError as a}from"../daemon-ipc/protocol.js";import{RoomInvocationTokenError as S}from"./invocation-token.js";import{RoomAttachmentCacheError as k}from"./attachment-cache.js";import{RoomActionHttpError as C}from"./room-action-client.js";import{normalizeRoomActionParameters as q}from"./room-action-schema.js";import{isInterruptedRoomActivation as w}from"./session-registry.js";import{RoomAttachmentUploadError as B}from"./attachment-upload.js";function O(e){return{"room-action.execute":async o=>{const I=x(o),l=e.getMachineCredentials();if(!l.machineId?.trim()||!l.machineToken?.trim())throw new a("machine_not_authorized","This machine is not authorized yet.");const r=_(I.registryKey,"registryKey",64),R=_(I.invocationToken,"invocationToken",128),m=E(I.action),b=q(m,I.actionParameters);return e.registry.runExclusive(r,async()=>{let g;try{g=e.authority.consume({invocationToken:R,machineId:l.machineId,machineCredential:l.machineToken,registryKey:r,action:m,targetTool:"shennian_room",actionParameters:b})}catch(t){throw M(t)}const c=e.registry.getByRegistryKey(r),i=c?.binding;if(!c)throw new a("room_session_unavailable","The exact Agent session is unavailable.");if(m==="room.create"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!g.bindingIntentId)throw new a("binding_intent_missing","Room create intent is missing.");const t=b,n=await e.actionClient.createRoom({machineToken:l.machineToken,bindingIntentId:g.bindingIntentId,nianSessionId:c.nianSessionId,runtimeAdapter:c.runtimeAdapter,sessionRegistryKey:r,name:t.name,agentAlias:t.agentAlias,agentDescription:t.agentDescription,activationCapability:A(c.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:r,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),u(n)}if(m==="room.join"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!g.bindingIntentId)throw new a("binding_intent_missing","Room join intent is missing.");const t=b,n=await e.actionClient.joinRoom({machineToken:l.machineToken,bindingIntentId:g.bindingIntentId,nianSessionId:c.nianSessionId,runtimeAdapter:c.runtimeAdapter,sessionRegistryKey:r,inviteToken:j(t.inviteUrl),message:t.message,agentAlias:t.agentAlias,agentDescription:t.agentDescription,activationCapability:A(c.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:r,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),u(n)}if(!i)throw new a("room_not_bound","This Agent session is not bound to a Room.");if(m!=="room.status"&&(i.status==="detached"||i.status==="pending_approval"))throw new a(i.status==="pending_approval"?"room_binding_pending_approval":"room_binding_detached",i.status==="pending_approval"?"This Agent is waiting for Room approval.":"This Agent Room binding must be repaired before use.");const v={machineToken:l.machineToken,roomId:i.roomId,bindingId:i.bindingId};if(m==="room.status"){const t=await e.actionClient.status(v),n=e.mirrorFactory?.(t.roomId);e.registry.setBinding({registryKey:r,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:t.status,activationMode:t.activationMode}),t.status==="detached"&&n?.markAccessRevoked();const h=e.registry.getByRegistryKey(r),d=h?.binding,s=e.observations?.getBySource(c);return u({...t,localHistoryPath:n?.messagesPath??null,hostSession:{state:h?.lifecycleState??"closed",lastReportedEvent:s?.lastEventName??null,lastActiveAt:s?.lastActiveAt??null,lastIdleAt:s?.lastIdleAt??null,closedAt:s?.closedAt??null,closedConfirmed:s?.lifecycleState==="closed",activationCapability:A(c.capabilities)},localActivation:{queued:d?.activationQueue.length??0,inFlight:!!(d?.activeActivation&&!w(d.activeActivation)),interrupted:!!(d?.activeActivation&&w(d.activeActivation)),pausedUntil:d?.loopBreaker.pausedUntil??null}})}if(m==="room.read"){const t=b;if(t.query){const s=e.mirrorFactory?.(i.roomId);if(!s)throw new a("room_mirror_unavailable","Room mirror is unavailable.");return u({source:"local_mirror",matches:s.search({query:t.query,limit:t.limit})})}const n=await e.actionClient.read({...v,afterSequence:t.afterSequence,limit:t.limit});e.mirrorFactory?.(i.roomId).append({messages:n.messages,historyFromSequence:n.historyFromSequence,roomName:n.roomName});const h=BigInt(n.nextSequence)>BigInt(i.lastDeliveredSequence)?n.nextSequence:i.lastDeliveredSequence;e.registry.advanceCursor({registryKey:r,cursor:"delivered",sequence:h});const d=[];for(const s of t.attachmentIds??[]){const y=n.messages.flatMap(p=>p.attachments).find(p=>p.attachmentId===s);if(!y||y.status!=="ready")throw new a("attachment_not_in_read_result","Requested attachment is not available in this read result.");if(!e.attachmentCache)throw new a("attachment_cache_unavailable","Room attachment cache is unavailable.");d.push({attachmentId:s,...await e.attachmentCache.materialize({...v,attachmentId:s,expected:{downloadRef:y.downloadRef,byteSize:y.byteSize,sha256:y.sha256}})})}return u({...n,materializedAttachments:d})}if(m==="room.send"){const t=b;let n;if(t.files?.length){const h=e.observations?.getBySource(c);if(!h?.workspacePath||!e.attachmentUploader)throw new a("room_attachment_upload_unavailable","The exact Agent workspace is unavailable for attachment upload.");n=await e.attachmentUploader.upload({...v,workspacePath:h.workspacePath,clientMessageId:t.clientMessageId,files:t.files})}return u(await e.actionClient.send({...v,clientMessageId:t.clientMessageId,text:t.text,mentionMembershipIds:t.mentionMembershipIds,replyToMessageId:t.replyToMessageId,attachmentIds:n,activationCauseMessageId:i.activeActivation&&!w(i.activeActivation)?i.activeActivation.causeMessageId:void 0}))}const T=b,f=await e.actionClient.mode({...v,...T});return e.registry.setBinding({registryKey:r,bindingId:i.bindingId,roomId:i.roomId,agentIdentityId:i.agentIdentityId,agentMembershipId:i.agentMembershipId,status:f.status,activationMode:f.activationMode}),u(f)}).catch(g=>{throw M(g)})}}}function x(e){if(!e||Array.isArray(e)||typeof e!="object")throw new a("invalid_params","Room action parameters are required.");return e}function _(e,o,I){if(typeof e!="string"||!e.trim()||e.length>I)throw new a("invalid_params",`${o} is invalid.`);return e.trim()}function E(e){const o=_(e,"action",64);if(o!=="room.read"&&o!=="room.create"&&o!=="room.join"&&o!=="room.send"&&o!=="room.mode"&&o!=="room.status")throw new a("room_action_not_implemented","Room action is not available yet.");return o}function j(e){const o=new URL(e).pathname.split("/").at(-1);if(!o||!/^[A-Za-z0-9_-]{43}$/.test(o))throw new a("invalid_params","inviteUrl is invalid.");return o}function A(e){return e.canResumeIdleSession||e.canActivateWhileHostOpen?"idle_resume":"notify_only"}function M(e){return e instanceof a?e:e instanceof S||e instanceof C||e instanceof k||e instanceof B?new a(e.code,e.message):new a("room_invocation_failed","Room invocation failed.")}function u(e){return JSON.parse(JSON.stringify(e))}export{O as createRoomActionIpcHandlers};
@@ -1,3 +0,0 @@
1
- import type { RoomInvocationAction } from './invocation-token.js';
2
- import type { JsonValue } from './pending-authorized-actions.js';
3
- export declare function normalizeRoomActionParameters(action: Exclude<RoomInvocationAction, 'room.bind'>, value: JsonValue | undefined): JsonValue;
@@ -1 +0,0 @@
1
- import{randomUUID as g}from"node:crypto";import{DaemonIpcError as u}from"../daemon-ipc/protocol.js";function w(t,n){const e=y(n);switch(t){case"room.status":return m(e,[]),{};case"room.read":{m(e,["afterSequence","limit","attachmentIds","query"]);const o=d(e.query,"query",200),i=e.afterSequence??"0",s=e.limit??50;(typeof i!="string"||!/^(0|[1-9]\d{0,19})$/.test(i))&&r("afterSequence"),(typeof s!="number"||!Number.isInteger(s)||s<1||s>100)&&r("limit");const c=p(e.attachmentIds,"attachmentIds",10);return o&&(e.afterSequence!=null||c)&&r("parameters"),o?{query:o,limit:s}:{afterSequence:i,limit:s,...c?{attachmentIds:c}:{}}}case"room.send":{m(e,["clientMessageId","text","files","mentionMembershipIds","replyToMessageId"]);const o=e.clientMessageId==null?`room-tool-${g()}`:a(e.clientMessageId,"clientMessageId",191),i=d(e.text,"text",2e4),s=h(e.files,"files",10);!i&&!s&&r("parameters");const c=p(e.mentionMembershipIds,"mentionMembershipIds",20),l=d(e.replyToMessageId,"replyToMessageId",191);return{clientMessageId:o,text:i??"",...s?{files:s}:{},...c?{mentionMembershipIds:c}:{},...l?{replyToMessageId:l}:{}}}case"room.mode":return m(e,["activationMode"]),{activationMode:f(e.activationMode)};case"room.create":return m(e,["name","agentAlias","agentDescription","activationMode"]),{name:a(e.name,"name",80),agentAlias:a(e.agentAlias,"agentAlias",80),agentDescription:a(e.agentDescription,"agentDescription",1e3),activationMode:f(e.activationMode??"auto_follow")};case"room.join":return m(e,["inviteUrl","agentAlias","agentDescription","message","activationMode"]),{inviteUrl:M(e.inviteUrl),agentAlias:a(e.agentAlias,"agentAlias",80),agentDescription:a(e.agentDescription,"agentDescription",1e3),activationMode:f(e.activationMode??"auto_follow"),...d(e.message,"message",500)?{message:d(e.message,"message",500)}:{}}}}function h(t,n,e){return t==null?void 0:((!Array.isArray(t)||t.length<1||t.length>e)&&r(n),t.map(i=>((typeof i!="string"||!i.trim()||i.length>4096||/[\0\r\n]/.test(i))&&r(n),i.trim())))}function y(t){return t==null?{}:((Array.isArray(t)||typeof t!="object")&&r("parameters"),t)}function m(t,n){const e=new Set(n);Object.keys(t).some(o=>!e.has(o))&&r("parameters")}function a(t,n,e){return(typeof t!="string"||!t.trim()||t.length>e||/[\0\r]/.test(t))&&r(n),t.trim()}function d(t,n,e){if(!(t==null||t===""))return a(t,n,e)}function p(t,n,e){if(t==null)return;(!Array.isArray(t)||t.length>e)&&r(n);const o=t.map(i=>a(i,n,191));return new Set(o).size!==o.length&&r(n),o.length>0?o:void 0}function f(t){if(t==="auto"||t==="auto_follow")return"auto_follow";if(t==="mention"||t==="mention_only")return"mention_only";if(t==="paused")return"paused";r("activationMode")}function M(t){const n=a(t,"inviteUrl",2048);let e;try{e=new URL(n)}catch{r("inviteUrl")}return(e.protocol!=="https:"||e.hostname!=="shennian.net"||!/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(e.pathname)||e.username||e.password||e.search||e.hash)&&r("inviteUrl"),e.toString()}function r(t){throw new u("invalid_params",`${t} is invalid.`)}export{w as normalizeRoomActionParameters};
@@ -1,23 +0,0 @@
1
- import type { AuthorizedActionExecutor } from './authorized-action-coordinator.js';
2
- import type { RoomHostSessionObservationStore } from './host-session-observations.js';
3
- import type { HttpRoomBindingClient } from './room-binding-client.js';
4
- import type { JsonValue, PendingAuthorizedAction } from './pending-authorized-actions.js';
5
- import type { RoomSessionRegistry } from './session-registry.js';
6
- type Credentials = {
7
- machineId: string;
8
- machineToken: string;
9
- };
10
- export declare class RoomAuthorizedActionExecutor implements AuthorizedActionExecutor {
11
- private readonly deps;
12
- constructor(deps: {
13
- registry: RoomSessionRegistry;
14
- observations: RoomHostSessionObservationStore;
15
- actionClient: Pick<HttpRoomBindingClient, 'resolveNativeSession' | 'createRoom' | 'joinRoom'>;
16
- getMachineCredentials: () => {
17
- machineId?: string;
18
- machineToken?: string;
19
- };
20
- });
21
- execute(action: PendingAuthorizedAction, claimed?: Credentials): Promise<JsonValue>;
22
- }
23
- export {};
@@ -1 +0,0 @@
1
- import g from"node:path";import{normalizeRoomActionParameters as f}from"./room-action-schema.js";class H{deps;constructor(e){this.deps=e}async execute(e,m){const s=I(m??this.deps.getMachineCredentials()),n=A(e),r=this.deps.observations.observe({machineId:s.machineId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostContextHash:n.hostContextHash,workspaceHash:n.workspaceHash,workspacePath:n.workspacePath,lifecycleState:"active",eventName:"PreToolUse"}),c=await this.deps.actionClient.resolveNativeSession({machineToken:s.machineToken,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey}),i=!!r.workspacePath,p=this.deps.registry.register({machineId:s.machineId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,nianSessionId:c.nianSessionId,hostProcessIdentity:`${n.runtimeAdapter}:${r.hostContextHash}:${r.registrationEpoch}`,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:i,canActivateWhileHostOpen:!1}}),y=i?"idle_resume":"notify_only",l=`${n.action.replace(".","-")}:${e.id}`,h={machineToken:s.machineToken,bindingIntentId:l,nianSessionId:c.nianSessionId,runtimeAdapter:n.runtimeAdapter,sessionRegistryKey:p.registryKey,agentAlias:n.parameters.agentAlias,agentDescription:n.parameters.agentDescription,activationCapability:y,activationMode:n.parameters.activationMode},o=n.action==="room.create"?await this.deps.actionClient.createRoom({...h,name:n.parameters.name}):await this.deps.actionClient.joinRoom({...h,inviteToken:w(n.parameters.inviteUrl),message:n.parameters.message});return this.deps.registry.setBinding({registryKey:p.registryKey,bindingId:o.binding.id,roomId:o.room.id,agentIdentityId:o.binding.agentIdentityId,agentMembershipId:o.binding.agentMembershipId,status:o.binding.status,activationMode:o.binding.activationMode}),JSON.parse(JSON.stringify(o))}}function A(t){const e=t.canonicalActionPayload;(!e||Array.isArray(e)||typeof e!="object"||e.schemaVersion!==1)&&a();const m=k(e.runtimeAdapter),s=d(e.sourceSessionKey,1024),n=u(e.hostContextHash),r=e.workspaceHash==null?null:u(e.workspaceHash),c=S(e.workspacePath);e.lifecycleState!=="active"&&a(),(e.action!==t.actionType||e.action!=="room.create"&&e.action!=="room.join")&&a();const i=f(e.action,e.actionParameters);return(!i||Array.isArray(i)||typeof i!="object")&&a(),{runtimeAdapter:m,sourceSessionKey:s,hostContextHash:n,workspaceHash:r,workspacePath:c,action:e.action,parameters:{name:typeof i.name=="string"?i.name:"",inviteUrl:typeof i.inviteUrl=="string"?i.inviteUrl:"",agentAlias:d(i.agentAlias,80),...typeof i.agentDescription=="string"?{agentDescription:i.agentDescription}:{},activationMode:b(i.activationMode),...typeof i.message=="string"?{message:i.message}:{}}}}function I(t){if(!t.machineId?.trim()||!t.machineToken?.trim())throw new Error("machine_credentials_unavailable");return{machineId:t.machineId.trim(),machineToken:t.machineToken.trim()}}function k(t){return t!=="codex"&&t!=="claude_code"&&t!=="workbuddy"&&a(),t}function b(t){return t!=="auto_follow"&&t!=="mention_only"&&t!=="paused"&&a(),t}function d(t,e){return(typeof t!="string"||!t.trim()||t.length>e||/[\0\r\n]/.test(t))&&a(),t.trim()}function u(t){const e=d(t,64);return/^[a-f0-9]{64}$/.test(e)||a(),e}function S(t){if(t==null||t==="")return null;const e=d(t,4096);return g.isAbsolute(e)||a(),e}function w(t){const e=/^https:\/\/shennian\.net\/spaces\/join\/([A-Za-z0-9_-]{43})$/.exec(t);return e||a(),e[1]}function a(){throw new Error("invalid_room_authorized_action")}export{H as RoomAuthorizedActionExecutor};
@@ -1,31 +0,0 @@
1
- import type { RoomActivationCapability, RoomActivationMode } from '@shennian/wire';
2
- import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
3
- import type { JsonValue } from './pending-authorized-actions.js';
4
- import { RoomInvocationTokenAuthority } from './invocation-token.js';
5
- import { HttpRoomBindingClient } from './room-binding-client.js';
6
- import { RoomSessionRegistry } from './session-registry.js';
7
- import type { RoomJsonlMirror } from './jsonl-mirror.js';
8
- import type { RoomAttachmentCache } from './attachment-cache.js';
9
- import type { RoomAttachmentUploader } from './attachment-upload.js';
10
- import type { RoomHostSessionObservationStore } from './host-session-observations.js';
11
- export declare function createRoomBindingIpcHandlers(input: {
12
- authority: RoomInvocationTokenAuthority;
13
- registry: RoomSessionRegistry;
14
- bindingClient: HttpRoomBindingClient;
15
- getMachineCredentials: () => {
16
- machineId?: string;
17
- machineToken?: string;
18
- };
19
- mirrorFactory?: (roomId: string) => Pick<RoomJsonlMirror, 'append' | 'markAccessRevoked' | 'search'> & {
20
- readonly messagesPath?: string;
21
- };
22
- attachmentCache?: Pick<RoomAttachmentCache, 'materialize'>;
23
- attachmentUploader?: Pick<RoomAttachmentUploader, 'upload'>;
24
- observations?: RoomHostSessionObservationStore;
25
- }): Record<string, DaemonIpcHandler>;
26
- export declare function buildRoomBindingActionParameters(input: {
27
- roomId: string;
28
- agentIdentityId: string;
29
- activationCapability?: RoomActivationCapability;
30
- activationMode?: RoomActivationMode;
31
- }): JsonValue;
@@ -1 +0,0 @@
1
- import{DaemonIpcError as a}from"../daemon-ipc/protocol.js";import{createRoomActionIpcHandlers as b}from"./room-action-ipc.js";function _(n){return{...b({authority:n.authority,registry:n.registry,actionClient:n.bindingClient,getMachineCredentials:n.getMachineCredentials,mirrorFactory:n.mirrorFactory,attachmentCache:n.attachmentCache,attachmentUploader:n.attachmentUploader,observations:n.observations}),"room-binding.execute":async i=>{const e=v(i),r=n.getMachineCredentials();if(!r.machineId?.trim()||!r.machineToken?.trim())throw new a("machine_not_authorized","This machine is not authorized yet.");const l=c(e.invocationToken,"invocationToken",128),m=c(e.registryKey,"registryKey",64),d=c(e.roomId,"roomId",191),s=c(e.agentIdentityId,"agentIdentityId",191),g=p(e.activationCapability),y=u(e.activationMode),h=f({roomId:d,agentIdentityId:s,activationCapability:g,activationMode:y});return n.registry.runExclusive(m,async()=>{const o=n.authority.consume({invocationToken:l,machineId:r.machineId,machineCredential:r.machineToken,registryKey:m,action:"room.bind",targetTool:"shennian_room",actionParameters:h});if(!o.bindingIntentId)throw new a("binding_intent_missing","Room Binding intent is missing.");const t=await n.bindingClient.create({machineToken:r.machineToken,bindingIntentId:o.bindingIntentId,roomId:d,agentIdentityId:s,nianSessionId:o.nianSessionId,runtimeAdapter:o.runtimeAdapter,sessionRegistryKey:o.registryKey,activationCapability:g,activationMode:y}),I=n.registry.setBinding({registryKey:m,bindingId:t.binding.id,roomId:d,agentIdentityId:s,agentMembershipId:t.binding.agentMembershipId,status:t.binding.status,activationMode:t.binding.activationMode});return{binding:{id:t.binding.id,roomId:d,agentIdentityId:s,agentMembershipId:t.binding.agentMembershipId,nianSessionId:o.nianSessionId,runtimeAdapter:o.runtimeAdapter,activationCapability:g,activationMode:t.binding.activationMode,status:t.binding.status},idempotent:t.idempotent,panelContextKey:I.panelContextKey}})}}}function f(n){return{roomId:n.roomId,agentIdentityId:n.agentIdentityId,activationCapability:n.activationCapability??"notify_only",activationMode:n.activationMode??"auto_follow"}}function v(n){if(!n||Array.isArray(n)||typeof n!="object")throw new a("invalid_params","Room Binding parameters are required.");return n}function c(n,i,e){if(typeof n!="string"||!n.trim()||n.length>e)throw new a("invalid_params",`${i} is invalid.`);return n.trim()}function p(n){const i=n??"notify_only";if(i!=="managed"&&i!=="idle_resume"&&i!=="notify_only")throw new a("invalid_params","activationCapability is invalid.");return i}function u(n){const i=n??"auto_follow";if(i!=="auto_follow"&&i!=="mention_only"&&i!=="paused")throw new a("invalid_params","activationMode is invalid.");return i}export{f as buildRoomBindingActionParameters,_ as createRoomBindingIpcHandlers};
@@ -1,14 +0,0 @@
1
- import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
2
- import type { RoomHostSessionObservationStore } from './host-session-observations.js';
3
- import type { HttpRoomActionClient } from './room-action-client.js';
4
- import { RoomSessionRegistry } from './session-registry.js';
5
- export declare function createRoomSessionRegistryIpcHandlers(input: {
6
- registry: RoomSessionRegistry;
7
- observations?: RoomHostSessionObservationStore;
8
- sessionResolver?: Pick<HttpRoomActionClient, 'resolveNativeSession'>;
9
- getMachineCredentials?: () => {
10
- machineId?: string;
11
- machineToken?: string;
12
- };
13
- getMachineId?: () => string | undefined;
14
- }): Record<string, DaemonIpcHandler>;
@@ -1 +0,0 @@
1
- import p from"node:path";import{DaemonIpcError as o}from"../daemon-ipc/protocol.js";import{isInterruptedRoomActivation as b}from"./session-registry.js";function _(e){return{"room-session.register":t=>{const i=u(t),s=g(e).machineId?.trim();if(!s)throw new o("machine_not_authorized","This machine is not authorized yet.");const n=e.registry.register({machineId:s,runtimeAdapter:w(i.runtimeAdapter),sourceSessionKey:l(i.sourceSessionKey,"sourceSessionKey",1024),nianSessionId:l(i.nianSessionId,"nianSessionId",512),hostProcessIdentity:l(i.hostProcessIdentity,"hostProcessIdentity",512),capabilities:S(i.capabilities)});return y(n)},"room-session.resolve":async t=>{const i=u(t),s=g(e),n=s.machineId?.trim();if(!n)throw new o("machine_not_authorized","This machine is not authorized yet.");const a=w(i.runtimeAdapter),c=l(i.sourceSessionKey,"sourceSessionKey",1024),d=e.registry.getBySource({machineId:n,runtimeAdapter:a,sourceSessionKey:c});if(d)return y(d);if(!s.machineToken?.trim())throw new o("machine_not_authorized","This machine is not authorized yet.");const r=e.observations?.getBySource({machineId:n,runtimeAdapter:a,sourceSessionKey:c});if(!r||r.lifecycleState==="closed")throw new o("room_session_not_observed","The exact native Agent session has not been observed on this machine.");if(!e.sessionResolver)throw new o("room_session_not_found","Native Agent session is not registered.");const h=await e.sessionResolver.resolveNativeSession({machineToken:s.machineToken,runtimeAdapter:a,sourceSessionKey:c}),f=!!r.workspacePath,m=e.registry.register({machineId:n,runtimeAdapter:a,sourceSessionKey:c,nianSessionId:h.nianSessionId,hostProcessIdentity:`${a}:${r.hostContextHash}:${r.registrationEpoch}`,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:f,canActivateWhileHostOpen:!1}});return r.lifecycleState==="idle"&&e.registry.updateLifecycle(m.registryKey,"inactive"),y(e.registry.getByRegistryKey(m.registryKey))},"room-session.resolve-active-workspace":t=>{const i=u(t),s=g(e).machineId?.trim();if(!s)throw new o("machine_not_authorized","This machine is not authorized yet.");const n=p.resolve(l(i.workspacePath,"workspacePath",4096)),a=Date.now(),c=e.registry.list().filter(d=>{const r=d.binding,h=r?.activeActivation;if(d.machineId!==s||!r||!h||b(h)||r.status!=="active")return!1;const f=h.dispatchedAt??h.enqueuedAt;if(a-new Date(f).getTime()>10*6e4)return!1;const m=e.observations?.getBySource(d);return m?.lifecycleState!=="closed"&&!!m?.workspacePath&&p.resolve(m.workspacePath)===n});if(c.length!==1)throw new o("room_session_context_ambiguous","No single active Room session matches this Agent workspace.");return y(c[0])}}}function g(e){return e.getMachineCredentials?.()??{machineId:e.getMachineId?.()}}function y(e){return{registryKey:e.registryKey,nianSessionId:e.nianSessionId,panelContextKey:e.panelContextKey,runtimeAdapter:e.runtimeAdapter,registrationEpoch:e.registrationEpoch,lifecycleState:e.lifecycleState,capabilities:e.capabilities,binding:e.binding,lastSeenAt:e.lastSeenAt}}function u(e){if(!e||Array.isArray(e)||typeof e!="object")throw new o("invalid_params","Room session parameters are required.");return e}function l(e,t,i){if(typeof e!="string"||!e.trim()||e.length>i)throw new o("invalid_params",`${t} is invalid.`);return e.trim()}function w(e){const t=l(e,"runtimeAdapter",32);if(t!=="codex"&&t!=="claude_code"&&t!=="workbuddy")throw new o("invalid_params","runtimeAdapter is unsupported.");return t}function S(e){const t=u(e),i=["canIdentifySession","canBindAutomatically","canOpenEmbeddedRoomPage","canResumeIdleSession","canActivateWhileHostOpen"],s={};for(const n of i){if(typeof t[n]!="boolean")throw new o("invalid_params",`${n} must be boolean.`);s[n]=t[n]}return s}export{_ as createRoomSessionRegistryIpcHandlers};
@@ -1,120 +0,0 @@
1
- import type { JsonValue } from './pending-authorized-actions.js';
2
- import type { RoomInvocationAction } from './invocation-token.js';
3
- export declare const SHENNIAN_ROOM_TOOL_NAME = "shennian_room";
4
- export declare const SHENNIAN_ROOM_TOOL: {
5
- readonly name: "shennian_room";
6
- readonly title: "Shennian Room";
7
- readonly description: "Create, join, read, search, send to, configure, or inspect the single Shennian Room bound to this Agent session. Creating or joining requires a non-empty Agent description. Status includes member and Agent descriptions plus the canonical local JSONL mirror path.";
8
- readonly inputSchema: {
9
- readonly type: "object";
10
- readonly additionalProperties: false;
11
- readonly required: readonly ["action"];
12
- readonly properties: {
13
- readonly action: {
14
- readonly type: "string";
15
- readonly enum: readonly ["create", "join", "read", "send", "mode", "status"];
16
- readonly description: "The Room action to perform.";
17
- };
18
- readonly name: {
19
- readonly type: "string";
20
- readonly minLength: 1;
21
- readonly maxLength: 80;
22
- };
23
- readonly agentAlias: {
24
- readonly type: "string";
25
- readonly minLength: 1;
26
- readonly maxLength: 80;
27
- };
28
- readonly agentDescription: {
29
- readonly type: "string";
30
- readonly minLength: 1;
31
- readonly maxLength: 1000;
32
- };
33
- readonly inviteUrl: {
34
- readonly type: "string";
35
- readonly format: "uri";
36
- readonly maxLength: 2048;
37
- };
38
- readonly message: {
39
- readonly type: "string";
40
- readonly maxLength: 500;
41
- };
42
- readonly activationMode: {
43
- readonly type: "string";
44
- readonly enum: readonly ["auto", "auto_follow", "mention", "mention_only", "paused"];
45
- };
46
- readonly afterSequence: {
47
- readonly type: "string";
48
- readonly pattern: "^(0|[1-9]\\d{0,19})$";
49
- };
50
- readonly limit: {
51
- readonly type: "integer";
52
- readonly minimum: 1;
53
- readonly maximum: 100;
54
- };
55
- readonly query: {
56
- readonly type: "string";
57
- readonly minLength: 1;
58
- readonly maxLength: 200;
59
- };
60
- readonly attachmentIds: {
61
- readonly type: "array";
62
- readonly maxItems: 10;
63
- readonly uniqueItems: true;
64
- readonly items: {
65
- readonly type: "string";
66
- readonly minLength: 1;
67
- readonly maxLength: 191;
68
- };
69
- };
70
- readonly clientMessageId: {
71
- readonly type: "string";
72
- readonly minLength: 1;
73
- readonly maxLength: 191;
74
- readonly description: "Optional idempotency key. Shennian generates one when omitted.";
75
- };
76
- readonly text: {
77
- readonly type: "string";
78
- readonly minLength: 1;
79
- readonly maxLength: 20000;
80
- };
81
- readonly files: {
82
- readonly type: "array";
83
- readonly description: "Up to 10 files inside the current Agent workspace to upload privately with this message.";
84
- readonly minItems: 1;
85
- readonly maxItems: 10;
86
- readonly items: {
87
- readonly type: "string";
88
- readonly minLength: 1;
89
- readonly maxLength: 4096;
90
- };
91
- };
92
- readonly mentionMembershipIds: {
93
- readonly type: "array";
94
- readonly maxItems: 20;
95
- readonly uniqueItems: true;
96
- readonly items: {
97
- readonly type: "string";
98
- readonly minLength: 1;
99
- readonly maxLength: 191;
100
- };
101
- };
102
- readonly replyToMessageId: {
103
- readonly type: "string";
104
- readonly minLength: 1;
105
- readonly maxLength: 191;
106
- };
107
- };
108
- };
109
- };
110
- export declare function normalizeRoomToolInput(value: unknown): {
111
- action: Exclude<RoomInvocationAction, 'room.bind'>;
112
- actionParameters: JsonValue;
113
- registryKey?: string;
114
- invocationToken?: string;
115
- authorizedActionId?: string;
116
- };
117
- export declare class RoomToolInputError extends Error {
118
- readonly code: string;
119
- constructor(code: string, message: string);
120
- }
@@ -1 +0,0 @@
1
- import{normalizeRoomActionParameters as c}from"./room-action-schema.js";const d="shennian_room",l={name:d,title:"Shennian Room",description:"Create, join, read, search, send to, configure, or inspect the single Shennian Room bound to this Agent session. Creating or joining requires a non-empty Agent description. Status includes member and Agent descriptions plus the canonical local JSONL mirror path.",inputSchema:{type:"object",additionalProperties:!1,required:["action"],properties:{action:{type:"string",enum:["create","join","read","send","mode","status"],description:"The Room action to perform."},name:{type:"string",minLength:1,maxLength:80},agentAlias:{type:"string",minLength:1,maxLength:80},agentDescription:{type:"string",minLength:1,maxLength:1e3},inviteUrl:{type:"string",format:"uri",maxLength:2048},message:{type:"string",maxLength:500},activationMode:{type:"string",enum:["auto","auto_follow","mention","mention_only","paused"]},afterSequence:{type:"string",pattern:"^(0|[1-9]\\d{0,19})$"},limit:{type:"integer",minimum:1,maximum:100},query:{type:"string",minLength:1,maxLength:200},attachmentIds:{type:"array",maxItems:10,uniqueItems:!0,items:{type:"string",minLength:1,maxLength:191}},clientMessageId:{type:"string",minLength:1,maxLength:191,description:"Optional idempotency key. Shennian generates one when omitted."},text:{type:"string",minLength:1,maxLength:2e4},files:{type:"array",description:"Up to 10 files inside the current Agent workspace to upload privately with this message.",minItems:1,maxItems:10,items:{type:"string",minLength:1,maxLength:4096}},mentionMembershipIds:{type:"array",maxItems:20,uniqueItems:!0,items:{type:"string",minLength:1,maxLength:191}},replyToMessageId:{type:"string",minLength:1,maxLength:191}}}},u=new Map([["create","room.create"],["join","room.join"],["read","room.read"],["send","room.send"],["mode","room.mode"],["status","room.status"]]);function f(i){(!i||typeof i!="object"||Array.isArray(i))&&g();const e=i,n=typeof e.action=="string"?u.get(e.action):void 0;n||g();const o=e._shennianRegistryKey,r=e._shennianInvocationToken,s=e._shennianAuthorizedActionId,a=Object.keys(e).filter(t=>t.startsWith("_shennian"));if(typeof s=="string"&&/^[A-Za-z0-9_-]{8,128}$/.test(s)&&a.length===1){const t=Object.fromEntries(Object.entries(e).filter(([m])=>m!=="action"&&m!=="_shennianAuthorizedActionId"));return{action:n,actionParameters:c(n,t),authorizedActionId:s}}if(typeof o!="string"||!/^[a-f0-9]{64}$/.test(o)||typeof r!="string"||!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(r)||a.some(t=>t!=="_shennianRegistryKey"&&t!=="_shennianInvocationToken"))throw new p("session_context_required","The current Agent session is not connected to Shennian Room yet.");const h=Object.fromEntries(Object.entries(e).filter(([t])=>t!=="action"&&t!=="_shennianRegistryKey"&&t!=="_shennianInvocationToken"));return{action:n,actionParameters:c(n,h),registryKey:o,invocationToken:r}}class p extends Error{code;constructor(e,n){super(n),this.code=e,this.name="RoomToolInputError"}}function g(){throw new p("invalid_tool_input","Invalid Shennian Room tool input.")}export{p as RoomToolInputError,l as SHENNIAN_ROOM_TOOL,d as SHENNIAN_ROOM_TOOL_NAME,f as normalizeRoomToolInput};
@@ -1,11 +0,0 @@
1
- import { callDaemonIpc } from '../daemon-ipc/client.js';
2
- import type { JsonValue } from './pending-authorized-actions.js';
3
- export declare function executeRoomTool(rawInput: unknown, invoke?: typeof callDaemonIpc, env?: NodeJS.ProcessEnv): Promise<JsonValue>;
4
- export declare function handleRoomToolRpc(value: unknown, invoke?: typeof callDaemonIpc, env?: NodeJS.ProcessEnv): Promise<Record<string, unknown> | null>;
5
- /**
6
- * The daemon keeps a canonical Room URL for validation and persistence. At the
7
- * MCP host boundary, expose that same Room route in the focused workspace mode
8
- * so Codex, Claude Code, and WorkBuddy never land on the generic app shell.
9
- */
10
- export declare function focusRoomWebUrlForHost(result: JsonValue): JsonValue;
11
- export declare function runRoomToolServer(input?: NodeJS.ReadableStream, output?: NodeJS.WritableStream): Promise<void>;
@@ -1,3 +0,0 @@
1
- import g from"node:readline";import{callDaemonIpc as p}from"../daemon-ipc/client.js";import{DaemonIpcError as _}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as A}from"./room-action-schema.js";import{normalizeRoomToolInput as N,RoomToolInputError as y,SHENNIAN_ROOM_TOOL as O,SHENNIAN_ROOM_TOOL_NAME as l}from"./tool-schema.js";import{AgentToolError as T,executeAgentTool as w,SHENNIAN_AGENT_TOOL as E,SHENNIAN_AGENT_TOOL_NAME as R}from"./agent-tool.js";async function b(t,o=p,i=process.env){const r=await x(t,o,i);return r.authorizedActionId?o({method:"authorized-action.wait",params:{id:r.authorizedActionId,timeoutMs:12e4},timeoutMs:135e3}):o({method:"room-action.execute",params:{registryKey:r.registryKey,invocationToken:r.invocationToken,action:r.action,actionParameters:r.actionParameters},timeoutMs:r.action==="room.read"?65e3:15e3})}async function j(t,o=p,i=process.env){if(!t||typeof t!="object"||Array.isArray(t))return m(null,-32600,"Invalid Request");const r=t,e=M(r.id);if(r.jsonrpc!=="2.0"||typeof r.method!="string")return m(e,-32600,"Invalid Request");if(r.method.startsWith("notifications/"))return null;if(r.method==="initialize")return f(e,{protocolVersion:z(r.params),capabilities:{tools:{listChanged:!1}},serverInfo:{name:"shennian-room",version:"1.0.0"}});if(r.method==="ping")return f(e,{});if(r.method==="tools/list")return f(e,{tools:[O,E]});if(r.method!=="tools/call")return m(e,-32601,"Method not found");const n=P(r.params);if(!n||n.name!==l&&n.name!==R)return m(e,-32602,"Invalid params");try{const c=n.name===l?I(await b(n.arguments,o,i)):await w(n.arguments);return f(e,{content:[{type:"text",text:JSON.stringify(c)}],structuredContent:c,isError:!1})}catch(c){const s=v(c);return f(e,{content:[{type:"text",text:JSON.stringify(s)}],structuredContent:s,isError:!0})}}function I(t){if(!t||typeof t!="object"||Array.isArray(t))return t;const o=t.room;if(!o||typeof o!="object"||Array.isArray(o))return t;const i=S(o.webUrl);return i?{...t,room:{...o,webUrl:i}}:t}function S(t){if(typeof t!="string")return null;let o;try{o=new URL(t)}catch{return null}return!["http:","https:"].includes(o.protocol)||o.username||o.password||o.search||o.hash||!/^\/spaces\/[A-Za-z0-9_-]+$/.test(o.pathname)?null:(o.searchParams.set("focus","room"),o.toString())}async function x(t,o,i){try{return N(t)}catch(r){let e=i.SHENNIAN_ROOM_REGISTRY_KEY?.trim();if(!(r instanceof y)||r.code!=="session_context_required"||e&&!/^[a-f0-9]{64}$/.test(e)||!t||typeof t!="object"||Array.isArray(t))throw r;const n=t;if(Object.keys(n).some(a=>a.startsWith("_shennian")))throw r;const c={create:"room.create",join:"room.join",read:"room.read",send:"room.send",mode:"room.mode",status:"room.status"},s=typeof n.action=="string"?c[n.action]:void 0;if(!s)throw r;const h=Object.fromEntries(Object.entries(n).filter(([a])=>a!=="action")),d=A(s,h);if(!e){const a=await o({method:"room-session.resolve-active-workspace",params:{workspacePath:process.cwd()},timeoutMs:2e3});if(typeof a?.registryKey!="string"||!/^[a-f0-9]{64}$/.test(a.registryKey))throw r;e=a.registryKey}const u=(await o({method:"room-invocation.issue",params:{registryKey:e,action:s,targetTool:l,actionParameters:d},timeoutMs:2e3}))?.invocationToken;if(typeof u!="string"||!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(u))throw r;return{action:s,actionParameters:d,registryKey:e,invocationToken:u}}}async function J(t=process.stdin,o=process.stdout){const i=g.createInterface({input:t,crlfDelay:1/0});for await(const r of i){if(!r.trim())continue;let e;try{e=JSON.parse(r)}catch{o.write(`${JSON.stringify(m(null,-32700,"Parse error"))}
2
- `);continue}const n=await j(e);n&&o.write(`${JSON.stringify(n)}
3
- `)}}function M(t){return typeof t=="string"||typeof t=="number"||t===null?t:null}function z(t){if(t&&typeof t=="object"){const o=t.protocolVersion;if(typeof o=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(o))return o}return"2025-06-18"}function P(t){if(!t||typeof t!="object"||Array.isArray(t))return null;const o=t;return typeof o.name=="string"?{name:o.name,arguments:o.arguments??{}}:null}function v(t){return t instanceof y||t instanceof T||t instanceof _?{ok:!1,error:{code:t.code,message:t.message}}:{ok:!1,error:{code:"room_tool_failed",message:"Shennian Room action failed."}}}function f(t,o){return{jsonrpc:"2.0",id:t,result:o}}function m(t,o,i){return{jsonrpc:"2.0",id:t,error:{code:o,message:i}}}export{b as executeRoomTool,I as focusRoomWebUrlForHost,j as handleRoomToolRpc,J as runRoomToolServer};
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=message-payload.test.d.ts.map