shennian 0.3.86 → 0.4.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 (98) hide show
  1. package/README.md +3 -2
  2. package/dist/bin/shennian.js +1 -1
  3. package/dist/publish-build-manifest.json +32 -27
  4. package/dist/src/agents/adapter.js +1 -19
  5. package/dist/src/agents/claude-managed-permissions.js +1 -248
  6. package/dist/src/agents/claude.js +8 -393
  7. package/dist/src/agents/codex-managed-permissions.js +1 -294
  8. package/dist/src/agents/codex-managed-room-tools.js +1 -249
  9. package/dist/src/agents/codex-utils.js +7 -200
  10. package/dist/src/agents/codex.d.ts +7 -1
  11. package/dist/src/agents/codex.js +17 -1352
  12. package/dist/src/agents/config-status.js +1 -226
  13. package/dist/src/agents/cursor-managed-permissions.js +2 -299
  14. package/dist/src/agents/cursor.js +1 -314
  15. package/dist/src/agents/custom.js +4 -418
  16. package/dist/src/agents/external-channel-instructions.js +11 -94
  17. package/dist/src/agents/gemini.js +1 -180
  18. package/dist/src/agents/manager.js +18 -250
  19. package/dist/src/agents/model-registry/cache.js +1 -37
  20. package/dist/src/agents/model-registry/discovery.js +2 -188
  21. package/dist/src/agents/model-registry/parsers.js +4 -521
  22. package/dist/src/agents/model-registry/runner.js +1 -30
  23. package/dist/src/agents/model-registry/service.js +1 -115
  24. package/dist/src/agents/model-registry/types.js +1 -8
  25. package/dist/src/agents/model-registry.js +1 -19
  26. package/dist/src/agents/native-capabilities.js +1 -237
  27. package/dist/src/agents/openclaw.js +2 -293
  28. package/dist/src/agents/opencode-managed-permissions.js +2 -187
  29. package/dist/src/agents/opencode.js +1 -298
  30. package/dist/src/agents/platform-instructions.js +17 -46
  31. package/dist/src/agents/runtime-compatibility.js +1 -44
  32. package/dist/src/agents/workbuddy-managed-permissions.js +2 -329
  33. package/dist/src/agents/workbuddy.js +3 -444
  34. package/dist/src/commands/logout.d.ts +3 -0
  35. package/dist/src/commands/logout.js +1 -0
  36. package/dist/src/daemon-ipc/paths.js +1 -1
  37. package/dist/src/fs/boundary.js +1 -185
  38. package/dist/src/fs/handler.js +1 -130
  39. package/dist/src/fs/security.js +1 -32
  40. package/dist/src/fs/text-decoder.js +1 -110
  41. package/dist/src/index.js +3 -587
  42. package/dist/src/manager/prompt.js +29 -34
  43. package/dist/src/manager/registry.js +1 -317
  44. package/dist/src/manager/retired-external-channel-compat.js +1 -40
  45. package/dist/src/manager/runtime.js +23 -1514
  46. package/dist/src/native-fusion/codex-parser.d.ts +3 -1
  47. package/dist/src/native-fusion/codex-parser.js +4 -660
  48. package/dist/src/native-fusion/codex-user-input.js +5 -102
  49. package/dist/src/native-fusion/config.js +1 -5
  50. package/dist/src/native-fusion/file-watcher.js +1 -191
  51. package/dist/src/native-fusion/local-repository.js +2 -569
  52. package/dist/src/native-fusion/opencode-parser.js +3 -130
  53. package/dist/src/native-fusion/parser-common.d.ts +6 -1
  54. package/dist/src/native-fusion/parser-common.js +4 -4
  55. package/dist/src/native-fusion/parsers.d.ts +4 -2
  56. package/dist/src/native-fusion/parsers.js +3 -140
  57. package/dist/src/native-fusion/service.js +2 -739
  58. package/dist/src/native-fusion/session-header.js +2 -101
  59. package/dist/src/native-fusion/state.js +2 -104
  60. package/dist/src/native-fusion/types.js +1 -1
  61. package/dist/src/personal-sync/machine-authority.js +1 -34
  62. package/dist/src/room/activation-audit.js +2 -45
  63. package/dist/src/room/activation-policy.js +1 -30
  64. package/dist/src/room/activation-service.js +8 -408
  65. package/dist/src/room/invite-bootstrap.js +1 -156
  66. package/dist/src/room/managed-prompt-composer.d.ts +1 -1
  67. package/dist/src/room/managed-prompt-composer.js +17 -16
  68. package/dist/src/room/managed-room-binding.js +1 -167
  69. package/dist/src/room/managed-room-ipc.d.ts +4 -0
  70. package/dist/src/room/managed-room-ipc.js +2 -2
  71. package/dist/src/room/mirror-sync-service.d.ts +1 -0
  72. package/dist/src/room/mirror-sync-service.js +1 -183
  73. package/dist/src/room/room-binding-client.js +1 -151
  74. package/dist/src/session/archive-zip.js +1 -220
  75. package/dist/src/session/deletion-sync-state.js +2 -136
  76. package/dist/src/session/handlers/agent-capabilities.js +1 -14
  77. package/dist/src/session/handlers/agent-config.js +1 -150
  78. package/dist/src/session/handlers/agents.js +1 -55
  79. package/dist/src/session/handlers/chat.js +2 -1030
  80. package/dist/src/session/handlers/control.js +1 -61
  81. package/dist/src/session/handlers/fs.js +1 -594
  82. package/dist/src/session/handlers/message-status.js +1 -15
  83. package/dist/src/session/handlers/remote-access.js +1 -43
  84. package/dist/src/session/handlers/room-managed.js +1 -101
  85. package/dist/src/session/handlers/session-refresh.js +1 -35
  86. package/dist/src/session/handlers/title.js +1 -79
  87. package/dist/src/session/handlers/tool-detail.js +1 -149
  88. package/dist/src/session/handlers/voice-window.js +1 -62
  89. package/dist/src/session/history-subscriptions.js +1 -470
  90. package/dist/src/session/manager.d.ts +1 -0
  91. package/dist/src/session/manager.js +1 -658
  92. package/dist/src/session/native-cleanup-outbox.js +2 -287
  93. package/dist/src/session/native-session-lifecycle.js +6 -327
  94. package/dist/src/session/queue.d.ts +8 -0
  95. package/dist/src/session/queue.js +6 -6
  96. package/dist/src/upgrade/scheduler.js +1 -126
  97. package/node_modules/@shennian/wire/dist/agent-workspace.js +2 -2
  98. package/package.json +1 -1
@@ -1,408 +1,8 @@
1
- // @arch docs/features/network-rooms/core-data-and-security-decisions.md#五触发语义
2
- // @test src/__tests__/room-activation-service.test.ts
3
- import { isAvailableBuiltinAgentType, } from '@shennian/wire';
4
- import { getBuiltinAgentVersion, resolveBuiltinCommand } from '../agents/command-spec.js';
5
- import { registerCustomRuntimeSecurity, resolveCustomRuntimeExecutableFingerprint, } from '../agents/custom-managed-runtime.js';
6
- import { loadConfig } from '../config/index.js';
7
- import { resolveManagedAgentExecutableIdentity } from '../security/managed-agent-executable.js';
8
- import { compileManagedAgentPolicy, } from '../security/managed-agent-policy.js';
9
- import { resolveManagedAgentRuntimeProtection } from '../security/managed-agent-runtime.js';
10
- import { selectRoomActivationTargets } from './activation-policy.js';
11
- import { composeManagedRoomPrompt } from './managed-prompt-composer.js';
12
- const MAX_HOPS = 4;
13
- const ACTIVATION_WINDOW_MS = 60_000;
14
- const MAX_ACTIVATIONS_PER_WINDOW = 6;
15
- const RATE_PAUSE_MS = 5 * 60_000;
16
- export class RoomActivationService {
17
- deps;
18
- latestContexts = new Map();
19
- constructor(deps) {
20
- this.deps = deps;
21
- }
22
- async processMessages(roomId, messages, registrations, context = { roomName: '当前群聊' }) {
23
- const queued = [];
24
- const cursorAdvances = [];
25
- const mentionContexts = new Map();
26
- const legacyStateToRetire = new Set();
27
- const ordered = [...messages].sort((a, b) => compareSequence(a.sequence, b.sequence));
28
- for (const snapshot of registrations) {
29
- const current = this.deps.registry.getByRegistryKey(snapshot.registryKey);
30
- if (!current?.binding || current.binding.roomId !== roomId)
31
- continue;
32
- if (current.binding.status !== 'active' &&
33
- current.binding.status !== 'paused' &&
34
- current.binding.activationQueue.length === 0)
35
- continue;
36
- }
37
- // One-way upgrade bridge: old daemons persisted Room-specific pending items.
38
- // Move only items that were never dispatched into the Session queue, and
39
- // clear every legacy active marker after the durable batch commit.
40
- for (const snapshot of registrations) {
41
- const current = this.deps.registry.getByRegistryKey(snapshot.registryKey);
42
- if (!current?.binding || current.binding.roomId !== roomId)
43
- continue;
44
- if (current.binding.activationQueue.length === 0 && !current.binding.activeActivation)
45
- continue;
46
- legacyStateToRetire.add(current.registryKey);
47
- if (current.binding.activationQueue.length === 0)
48
- continue;
49
- const observation = this.deps.observations.getBySource(current);
50
- if (!observation?.workspacePath || observation.lifecycleState === 'closed') {
51
- throw new Error('managed_room_workspace_unavailable');
52
- }
53
- this.latestContexts.set(current.registryKey, context);
54
- for (const item of current.binding.activationQueue) {
55
- queued.push({
56
- registration: current,
57
- input: {
58
- params: {
59
- sessionId: current.nianSessionId,
60
- text: item.deliveryText ?? '群聊中有一条待处理消息,请结合当前群聊上下文处理。',
61
- agentType: toAgentType(current.runtimeAdapter),
62
- sessionMode: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.sessionMode ?? 'standard',
63
- managerConfig: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.managerConfig ?? null,
64
- managerDefaultWorkerAgentType: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType ?? null,
65
- managerDefaultWorkerModelId: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId ?? null,
66
- workDir: observation.workspacePath,
67
- agentSessionId: current.sourceSessionKey,
68
- queueMessageId: `room-activation-${current.binding.bindingId}-${item.causeMessageId}`,
69
- clientMessageId: `room-activation-${current.binding.bindingId}-${item.causeMessageId}`,
70
- },
71
- context: {
72
- registryKey: current.registryKey,
73
- causeMessageId: item.causeMessageId,
74
- rootCauseMessageId: item.rootCauseMessageId,
75
- sequence: item.sequence,
76
- hopCount: item.hopCount,
77
- },
78
- },
79
- });
80
- }
81
- }
82
- for (const message of ordered) {
83
- const targets = new Set(selectRoomActivationTargets(message, registrations).map((target) => target.registration.registryKey));
84
- for (const snapshot of registrations) {
85
- const current = this.deps.registry.getByRegistryKey(snapshot.registryKey);
86
- if (!current?.binding || current.binding.roomId !== roomId)
87
- continue;
88
- if (current.binding.status !== 'active' && current.binding.status !== 'paused')
89
- continue;
90
- if (BigInt(message.sequence) <= BigInt(current.binding.lastActivatedSequence))
91
- continue;
92
- const mentionContext = mentionContexts.get(current.registryKey)
93
- ?? [...(current.binding.mentionContext ?? [])];
94
- mentionContexts.set(current.registryKey, mentionContext);
95
- if (targets.has(current.registryKey)) {
96
- const hopCount = (message.activationCause?.hopCount ?? 0) +
97
- (message.author.principalType === 'agent' ? 1 : 0);
98
- const rootCauseMessageId = message.activationCause?.rootCauseMessageId ?? message.id;
99
- if (hopCount > MAX_HOPS) {
100
- this.audit(current, message.id, rootCauseMessageId, hopCount, 'suppressed', 'max_hops');
101
- }
102
- else {
103
- const claim = this.deps.registry.claimActivation({
104
- registryKey: current.registryKey,
105
- windowMs: ACTIVATION_WINDOW_MS,
106
- maxActivations: MAX_ACTIVATIONS_PER_WINDOW,
107
- pauseMs: RATE_PAUSE_MS,
108
- now: this.deps.now?.(),
109
- });
110
- if (!claim.allowed) {
111
- if (claim.newlyPaused) {
112
- this.audit(current, message.id, rootCauseMessageId, hopCount, 'suppressed', 'rate_limited');
113
- }
114
- cursorAdvances.push({ registryKey: current.registryKey, sequence: message.sequence });
115
- continue;
116
- }
117
- const observation = this.deps.observations.getBySource(current);
118
- if (!observation?.workspacePath || observation.lifecycleState === 'closed') {
119
- throw new Error('managed_room_workspace_unavailable');
120
- }
121
- const currentDelivery = buildDeliveryText([message]);
122
- const deliveryText = current.binding.activationMode === 'mention_only'
123
- ? mergeDeliveryTexts([
124
- ...mentionContext.map((item) => item.deliveryText),
125
- currentDelivery,
126
- ])
127
- : currentDelivery;
128
- this.latestContexts.set(current.registryKey, context);
129
- queued.push({
130
- registration: current,
131
- input: {
132
- params: {
133
- sessionId: current.nianSessionId,
134
- text: deliveryText,
135
- agentType: toAgentType(current.runtimeAdapter),
136
- sessionMode: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.sessionMode ?? 'standard',
137
- managerConfig: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.managerConfig ?? null,
138
- managerDefaultWorkerAgentType: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType ?? null,
139
- managerDefaultWorkerModelId: context.agentsByIdentityId?.[current.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId ?? null,
140
- workDir: observation.workspacePath,
141
- agentSessionId: current.sourceSessionKey,
142
- queueMessageId: `room-activation-${current.binding.bindingId}-${message.id}`,
143
- clientMessageId: `room-activation-${current.binding.bindingId}-${message.id}`,
144
- },
145
- context: {
146
- registryKey: current.registryKey,
147
- causeMessageId: message.id,
148
- rootCauseMessageId,
149
- sequence: message.sequence,
150
- hopCount,
151
- },
152
- },
153
- });
154
- if (current.binding.activationMode === 'mention_only') {
155
- mentionContexts.set(current.registryKey, []);
156
- }
157
- }
158
- }
159
- else if (current.binding.activationMode === 'mention_only' &&
160
- message.kind !== 'system' &&
161
- message.author.membershipId !== current.binding.agentMembershipId) {
162
- if (!mentionContext.some((item) => item.sequence === message.sequence)) {
163
- mentionContext.push({ sequence: message.sequence, deliveryText: buildDeliveryText([message]) });
164
- }
165
- }
166
- cursorAdvances.push({ registryKey: current.registryKey, sequence: message.sequence });
167
- }
168
- }
169
- await this.deps.enqueueConversationBatch(queued.map((entry) => entry.input));
170
- for (const registryKey of legacyStateToRetire) {
171
- this.deps.registry.retireLegacyActivationState(registryKey, this.deps.now?.() ?? new Date());
172
- }
173
- for (const [registryKey, items] of mentionContexts) {
174
- this.deps.registry.replaceMentionContext(registryKey, items, this.deps.now?.() ?? new Date());
175
- }
176
- for (const entry of queued) {
177
- this.audit(entry.registration, entry.input.context.causeMessageId, entry.input.context.rootCauseMessageId, entry.input.context.hopCount, 'queued', 'conversation_queue');
178
- }
179
- for (const advance of cursorAdvances) {
180
- this.deps.registry.advanceCursor({
181
- registryKey: advance.registryKey,
182
- cursor: 'activated',
183
- sequence: advance.sequence,
184
- });
185
- }
186
- }
187
- async prepareConversationTurn(contexts) {
188
- const currentContext = contexts.at(-1);
189
- if (!currentContext)
190
- throw new Error('managed_room_queue_context_missing');
191
- const registration = this.deps.registry.getByRegistryKey(currentContext.registryKey);
192
- if (!registration?.binding || registration.binding.status !== 'active') {
193
- throw new Error('managed_room_binding_unavailable');
194
- }
195
- const roomContext = this.deps.loadContext
196
- ? await this.deps.loadContext(registration)
197
- : this.latestContexts.get(currentContext.registryKey);
198
- if (!roomContext)
199
- throw new Error('managed_room_prompt_context_unavailable');
200
- const identity = roomContext.agentsByIdentityId?.[registration.binding.agentIdentityId];
201
- if (!identity?.permissionPolicy)
202
- throw new Error('managed_agent_policy_missing');
203
- const observation = this.deps.observations.getBySource(registration);
204
- if (!observation?.workspacePath || observation.lifecycleState === 'closed') {
205
- throw new Error('managed_room_workspace_unavailable');
206
- }
207
- const managedAgentPolicy = this.deps.resolveManagedAgentPolicy
208
- ? await this.deps.resolveManagedAgentPolicy({
209
- registration,
210
- permissionPolicy: identity.permissionPolicy,
211
- permissionConfirmation: identity.permissionConfirmation,
212
- workDir: observation.workspacePath,
213
- })
214
- : await resolveManagedAgentPolicy({
215
- registration,
216
- permissionPolicy: identity.permissionPolicy,
217
- permissionConfirmation: identity.permissionConfirmation,
218
- workDir: observation.workspacePath,
219
- });
220
- const prompt = buildRoomSystemPrompt(roomContext, registration, managedAgentPolicy);
221
- const token = this.deps.issueManagedRoomContext(registration.registryKey, currentContext.causeMessageId);
222
- if (!/^mrc_[A-Za-z0-9_-]{43}$/.test(token)) {
223
- throw new Error('managed_room_context_unavailable');
224
- }
225
- this.audit(registration, currentContext.causeMessageId, currentContext.rootCauseMessageId, currentContext.hopCount, 'activated', 'conversation_queue_dispatch');
226
- return {
227
- systemPrompt: prompt.systemPrompt,
228
- roomContextRegistryKey: registration.registryKey,
229
- managedRoomContextToken: token,
230
- managedAgentPolicy,
231
- managedRoomPromptSnapshot: prompt.snapshot,
232
- };
233
- }
234
- audit(registration, causeMessageId, rootCauseMessageId, hopCount, outcome, reason) {
235
- if (!registration.binding)
236
- return;
237
- this.deps.audit?.record({
238
- roomId: registration.binding.roomId,
239
- bindingId: registration.binding.bindingId,
240
- causeMessageId,
241
- rootCauseMessageId,
242
- hopCount,
243
- outcome,
244
- reason,
245
- at: this.deps.now?.(),
246
- });
247
- }
248
- }
249
- function buildDeliveryText(messages) {
250
- const blocks = messages.map((message) => {
251
- const text = message.text?.trim() || '发送了一条消息';
252
- const attachments = message.attachments.length > 0
253
- ? `\n${message.attachments.map(formatDeliveryAttachment).join('\n')}`
254
- : '';
255
- return { speaker: message.author.displayName, body: `${text}${attachments}` };
256
- });
257
- return formatDeliveryBlocks(blocks);
258
- }
259
- function formatDeliveryAttachment(attachment) {
260
- const label = `${escapeMarkdownLabel(attachment.name)} · ${formatByteSize(attachment.byteSize)}`;
261
- return `附件:${label}|${attachment.mimeType}|attachmentId=${attachment.attachmentId}`;
262
- }
263
- function escapeMarkdownLabel(value) {
264
- return String(value || '附件').replace(/[[\]\\]/g, '\\$&').replace(/[\r\n]+/g, ' ');
265
- }
266
- function formatByteSize(value) {
267
- const bytes = Number(value);
268
- if (!Number.isFinite(bytes) || bytes < 0)
269
- return `${value} B`;
270
- if (bytes < 1024)
271
- return `${bytes} B`;
272
- const units = ['KB', 'MB', 'GB', 'TB'];
273
- let size = bytes;
274
- let unit = -1;
275
- do {
276
- size /= 1024;
277
- unit += 1;
278
- } while (size >= 1024 && unit < units.length - 1);
279
- const digits = size >= 100 ? 0 : size >= 10 ? 1 : 2;
280
- return `${size.toFixed(digits).replace(/\.0+$|(?<=\.[0-9])0+$/, '')} ${units[unit]}`;
281
- }
282
- function mergeDeliveryTexts(deliveryTexts) {
283
- return formatDeliveryBlocks(deliveryTexts.map(parseDeliveryText));
284
- }
285
- function parseDeliveryText(deliveryText) {
286
- const compact = deliveryText.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);
287
- if (compact)
288
- return { speaker: compact[1], body: compact[2] };
289
- // Registries written by earlier local daemons used a three-line header.
290
- const legacy = deliveryText.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);
291
- if (legacy)
292
- return { speaker: legacy[1], body: legacy[2] };
293
- // A short-lived format used an unbracketed source label before the speaker.
294
- const labeledLegacy = deliveryText.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);
295
- if (labeledLegacy)
296
- return { speaker: labeledLegacy[1], body: labeledLegacy[2] };
297
- // The first natural Room format used an unadorned speaker on the first line.
298
- const natural = deliveryText.match(/^([^\n]+)\n([\s\S]*)$/);
299
- if (natural)
300
- return { speaker: natural[1], body: natural[2] };
301
- return { speaker: '群成员', body: deliveryText.trim() };
302
- }
303
- function formatDeliveryBlocks(blocks) {
304
- if (blocks.length === 1) {
305
- const [block] = blocks;
306
- return `[群消息] <${block.speaker}>\n${block.body}`;
307
- }
308
- return `[群消息]\n${blocks.map((block) => `<${block.speaker}>\n${block.body}`).join('\n\n')}`;
309
- }
310
- function buildRoomSystemPrompt(context, registration, managedAgentPolicy) {
311
- const identity = registration.binding
312
- ? context.agentsByIdentityId?.[registration.binding.agentIdentityId]
313
- : undefined;
314
- if (!registration.binding || !identity)
315
- throw new Error('managed_room_prompt_incomplete');
316
- return composeManagedRoomPrompt({
317
- runtimeAdapter: toAgentType(registration.runtimeAdapter),
318
- agent: {
319
- displayName: identity.displayName,
320
- description: identity.description,
321
- systemPrompt: context.agentSystemPromptsByIdentityId?.[registration.binding.agentIdentityId],
322
- },
323
- room: {
324
- name: context.roomName,
325
- activationMode: registration.binding.activationMode,
326
- members: context.members,
327
- },
328
- runtimePermissions: {
329
- requestedPolicy: managedAgentPolicy.requestedPolicy,
330
- enforcement: managedAgentPolicy.enforcement,
331
- resolvedWorkDir: managedAgentPolicy.adapterPolicy.workDir,
332
- },
333
- });
334
- }
335
- export async function resolveManagedAgentPolicy(input) {
336
- const agentType = toAgentType(input.registration.runtimeAdapter);
337
- const command = isAvailableBuiltinAgentType(agentType)
338
- ? resolveBuiltinCommand(agentType)
339
- : null;
340
- const customEntry = agentType.startsWith('custom:')
341
- ? loadConfig().customAgents?.[agentType.slice('custom:'.length)]
342
- : undefined;
343
- if (customEntry)
344
- registerCustomRuntimeSecurity(agentType, customEntry);
345
- const runtimeVersion = command
346
- ? getBuiltinAgentVersion(agentType, command)
347
- : customEntry?.caps.version;
348
- const executableIdentity = command
349
- ? resolveManagedAgentExecutableIdentity(command)
350
- : {
351
- platform: process.platform,
352
- isWsl: process.platform === 'linux' &&
353
- Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP),
354
- executableFingerprint: customEntry
355
- ? resolveCustomRuntimeExecutableFingerprint(customEntry)
356
- : undefined,
357
- };
358
- const runtimeInput = {
359
- agentType,
360
- runtimeVersion,
361
- platform: executableIdentity.platform,
362
- isWsl: executableIdentity.isWsl,
363
- executableFingerprint: executableIdentity.executableFingerprint,
364
- requireExecutableFingerprint: true,
365
- };
366
- const protection = resolveManagedAgentRuntimeProtection(runtimeInput);
367
- const confirmationMatches = managedAgentPermissionConfirmationMatches({
368
- confirmation: input.permissionConfirmation,
369
- policy: input.permissionPolicy,
370
- runtimeAdapter: agentType,
371
- protection,
372
- });
373
- const result = await compileManagedAgentPolicy({
374
- ...runtimeInput,
375
- policy: input.permissionPolicy,
376
- workDir: input.workDir,
377
- hostFullAccessConfirmed: confirmationMatches && input.permissionConfirmation?.hostFullAccess,
378
- limitedProtectionConfirmed: confirmationMatches && input.permissionConfirmation?.limitedProtection,
379
- });
380
- if (!result.ok) {
381
- const unsupported = result.unsupportedCapabilities?.length
382
- ? `:${result.unsupportedCapabilities.join(',')}`
383
- : '';
384
- throw new Error(`${result.errorCode}${unsupported}`);
385
- }
386
- return result.value;
387
- }
388
- export function managedAgentPermissionConfirmationMatches(input) {
389
- const { confirmation, policy, runtimeAdapter, protection } = input;
390
- return Boolean(confirmation &&
391
- confirmation.policyVersion === policy.version &&
392
- confirmation.runtimeAdapter === runtimeAdapter &&
393
- confirmation.runtimeVersion === protection.runtimeVersion &&
394
- confirmation.platform === protection.platform &&
395
- confirmation.executableFingerprint === protection.executableFingerprint &&
396
- confirmation.hostFullAccess === (policy.fileAccess === 'host-full') &&
397
- confirmation.limitedProtection === (protection.level !== 'hard'));
398
- }
399
- function toAgentType(runtime) {
400
- if (runtime === 'claude_code')
401
- return 'claude';
402
- return runtime;
403
- }
404
- function compareSequence(left, right) {
405
- const a = BigInt(left);
406
- const b = BigInt(right);
407
- return a < b ? -1 : a > b ? 1 : 0;
408
- }
1
+ import{isAvailableBuiltinAgentType as A}from"@shennian/wire";import{getBuiltinAgentVersion as k,resolveBuiltinCommand as P}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as S,resolveCustomRuntimeExecutableFingerprint as K}from"../agents/custom-managed-runtime.js";import{loadConfig as B}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as $}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as x}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as q}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as T}from"./activation-policy.js";import{composeManagedRoomPrompt as D}from"./managed-prompt-composer.js";const R=4,E=6e4,W=6,N=5*6e4;class oe{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,s,t,a={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],c=[],g=new Map,m=new Set,u=[...s].sort((i,r)=>G(i.sequence,r.sequence));for(const i of t){const r=this.deps.registry.getByRegistryKey(i.registryKey);!r?.binding||r.binding.roomId!==e||r.binding.status!=="active"&&r.binding.status!=="paused"&&r.binding.activationQueue.length}for(const i of t){const r=this.deps.registry.getByRegistryKey(i.registryKey);if(!r?.binding||r.binding.roomId!==e||r.binding.activationQueue.length===0&&!r.binding.activeActivation||(m.add(r.registryKey),r.binding.status!=="active"||r.binding.activationMode==="paused")||r.binding.activationQueue.length===0)continue;const p=this.deps.observations.getBySource(r);if(!p?.workspacePath||p.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(r.registryKey,a);for(const o of r.binding.activationQueue)d.push({registration:r,input:{params:{sessionId:r.nianSessionId,text:o.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:f(r.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:p.workspacePath,agentSessionId:r.sourceSessionKey,queueMessageId:`room-activation-${r.binding.bindingId}-${o.causeMessageId}`,clientMessageId:`room-activation-${r.binding.bindingId}-${o.causeMessageId}`},context:{registryKey:r.registryKey,causeMessageId:o.causeMessageId,rootCauseMessageId:o.rootCauseMessageId,sequence:o.sequence,hopCount:o.hopCount}}})}for(const i of u){const r=new Set(T(i,t).map(p=>p.registration.registryKey));for(const p of t){const o=this.deps.registry.getByRegistryKey(p.registryKey);if(!o?.binding||o.binding.roomId!==e||o.binding.status!=="active"&&o.binding.status!=="paused"||BigInt(i.sequence)<=BigInt(o.binding.lastActivatedSequence))continue;const l=g.get(o.registryKey)??[...o.binding.mentionContext??[]];if(g.set(o.registryKey,l),r.has(o.registryKey)){const y=(i.activationCause?.hopCount??0)+(i.author.principalType==="agent"?1:0),I=i.activationCause?.rootCauseMessageId??i.id;if(y>R)this.audit(o,i.id,I,y,"suppressed","max_hops");else{const h=this.deps.registry.claimActivation({registryKey:o.registryKey,windowMs:E,maxActivations:W,pauseMs:N,now:this.deps.now?.()});if(!h.allowed){h.newlyPaused&&this.audit(o,i.id,I,y,"suppressed","rate_limited"),c.push({registryKey:o.registryKey,sequence:i.sequence});continue}const b=this.deps.observations.getBySource(o);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const v=w([i]),M=o.binding.activationMode==="mention_only"?L([...l.map(_=>_.deliveryText),v]):v;this.latestContexts.set(o.registryKey,a),d.push({registration:o,input:{params:{sessionId:o.nianSessionId,text:M,agentType:f(o.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:o.sourceSessionKey,queueMessageId:`room-activation-${o.binding.bindingId}-${i.id}`,clientMessageId:`room-activation-${o.binding.bindingId}-${i.id}`},context:{registryKey:o.registryKey,causeMessageId:i.id,rootCauseMessageId:I,sequence:i.sequence,hopCount:y}}}),o.binding.activationMode==="mention_only"&&g.set(o.registryKey,[])}}else o.binding.activationMode==="mention_only"&&i.kind!=="system"&&i.author.membershipId!==o.binding.agentMembershipId&&(l.some(y=>y.sequence===i.sequence)||l.push({sequence:i.sequence,deliveryText:w([i])}));c.push({registryKey:o.registryKey,sequence:i.sequence})}}await this.deps.enqueueConversationBatch(d.map(i=>i.input));for(const i of m)this.deps.registry.retireLegacyActivationState(i,this.deps.now?.()??new Date);for(const[i,r]of g)this.deps.registry.replaceMentionContext(i,r,this.deps.now?.()??new Date);for(const i of d)this.audit(i.registration,i.input.context.causeMessageId,i.input.context.rootCauseMessageId,i.input.context.hopCount,"queued","conversation_queue");for(const i of c)this.deps.registry.advanceCursor({registryKey:i.registryKey,cursor:"activated",sequence:i.sequence})}async prepareConversationTurn(e){const s=e.at(-1);if(!s)throw new Error("managed_room_queue_context_missing");const t=this.deps.registry.getByRegistryKey(s.registryKey);if(!t?.binding||t.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const a=this.deps.loadContext?await this.deps.loadContext(t):this.latestContexts.get(s.registryKey);if(!a)throw new Error("managed_room_prompt_context_unavailable");const d=a.agentsByIdentityId?.[t.binding.agentIdentityId];if(!d?.permissionPolicy)throw new Error("managed_agent_policy_missing");const c=this.deps.observations.getBySource(t);if(!c?.workspacePath||c.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const g=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:t,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:c.workspacePath}):await j({registration:t,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:c.workspacePath}),m=Q(a,t,g),u=this.deps.issueManagedRoomContext(t.registryKey,s.causeMessageId);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(u))throw new Error("managed_room_context_unavailable");return this.audit(t,s.causeMessageId,s.rootCauseMessageId,s.hopCount,"activated","conversation_queue_dispatch"),{systemPrompt:m.systemPrompt,roomContextRegistryKey:t.registryKey,managedRoomContextToken:u,managedAgentPolicy:g,managedRoomPromptSnapshot:m.snapshot}}audit(e,s,t,a,d,c){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:s,rootCauseMessageId:t,hopCount:a,outcome:d,reason:c,at:this.deps.now?.()})}}function w(n){const e=n.map(s=>{const t=s.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",a=s.attachments.length>0?`
2
+ ${s.attachments.map(F).join(`
3
+ `)}`:"";return{speaker:s.author.displayName,body:`${t}${a}`}});return C(e)}function F(n){return`\u9644\u4EF6\uFF1A${`${O(n.name)} \xB7 ${V(n.byteSize)}`}\uFF5C${n.mimeType}\uFF5CattachmentId=${n.attachmentId}`}function O(n){return String(n||"\u9644\u4EF6").replace(/[[\]\\]/g,"\\$&").replace(/[\r\n]+/g," ")}function V(n){const e=Number(n);if(!Number.isFinite(e)||e<0)return`${n} B`;if(e<1024)return`${e} B`;const s=["KB","MB","GB","TB"];let t=e,a=-1;do t/=1024,a+=1;while(t>=1024&&a<s.length-1);const d=t>=100?0:t>=10?1:2;return`${t.toFixed(d).replace(/\.0+$|(?<=\.[0-9])0+$/,"")} ${s[a]}`}function L(n){return C(n.map(z))}function z(n){const e=n.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);if(e)return{speaker:e[1],body:e[2]};const s=n.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);if(s)return{speaker:s[1],body:s[2]};const t=n.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);if(t)return{speaker:t[1],body:t[2]};const a=n.match(/^([^\n]+)\n([\s\S]*)$/);return a?{speaker:a[1],body:a[2]}:{speaker:"\u7FA4\u6210\u5458",body:n.trim()}}function C(n){if(n.length===1){const[e]=n;return`[\u7FA4\u6D88\u606F] <${e.speaker}>
4
+ ${e.body}`}return`[\u7FA4\u6D88\u606F]
5
+ ${n.map(e=>`<${e.speaker}>
6
+ ${e.body}`).join(`
7
+
8
+ `)}`}function Q(n,e,s){const t=e.binding?n.agentsByIdentityId?.[e.binding.agentIdentityId]:void 0;if(!e.binding||!t)throw new Error("managed_room_prompt_incomplete");return D({runtimeAdapter:f(e.runtimeAdapter),agent:{displayName:t.displayName,description:t.description,systemPrompt:n.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId]},room:{name:n.roomName,activationMode:e.binding.activationMode,members:n.members},runtimePermissions:{requestedPolicy:s.requestedPolicy,enforcement:s.enforcement,resolvedWorkDir:s.adapterPolicy.workDir}})}async function j(n){const e=f(n.registration.runtimeAdapter),s=A(e)?P(e):null,t=e.startsWith("custom:")?B().customAgents?.[e.slice(7)]:void 0;t&&S(e,t);const a=s?k(e,s):t?.caps.version,d=s?$(s):{platform:process.platform,isWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:t?K(t):void 0},c={agentType:e,runtimeVersion:a,platform:d.platform,isWsl:d.isWsl,executableFingerprint:d.executableFingerprint,requireExecutableFingerprint:!0},g=q(c),m=X({confirmation:n.permissionConfirmation,policy:n.permissionPolicy,runtimeAdapter:e,protection:g}),u=await x({...c,policy:n.permissionPolicy,workDir:n.workDir,hostFullAccessConfirmed:m&&n.permissionConfirmation?.hostFullAccess,limitedProtectionConfirmed:m&&n.permissionConfirmation?.limitedProtection});if(!u.ok){const i=u.unsupportedCapabilities?.length?`:${u.unsupportedCapabilities.join(",")}`:"";throw new Error(`${u.errorCode}${i}`)}return u.value}function X(n){const{confirmation:e,policy:s,runtimeAdapter:t,protection:a}=n;return!!(e&&e.policyVersion===s.version&&e.runtimeAdapter===t&&e.runtimeVersion===a.runtimeVersion&&e.platform===a.platform&&e.executableFingerprint===a.executableFingerprint&&e.hostFullAccess===(s.fileAccess==="host-full")&&e.limitedProtection===(a.level!=="hard"))}function f(n){return n==="claude_code"?"claude":n}function G(n,e){const s=BigInt(n),t=BigInt(e);return s<t?-1:s>t?1:0}export{oe as RoomActivationService,X as managedAgentPermissionConfirmationMatches,j as resolveManagedAgentPolicy};
@@ -1,156 +1 @@
1
- // @arch docs/features/network-rooms/implementation-plan.md#八、邀请链接作为人和-agent-共同入口
2
- // @test src/__tests__/room-invite-bootstrap.test.ts
3
- const OFFICIAL_BOOTSTRAP_URL = 'https://shennian.net/api/room-invites/agent-bootstrap';
4
- const EXPECTED_ACTIONS = [
5
- 'room.create',
6
- 'room.join',
7
- 'room.read',
8
- 'room.send',
9
- 'room.mode',
10
- 'room.status',
11
- ];
12
- const EXPECTED_RUNTIMES = ['codex', 'claude_code', 'workbuddy'];
13
- const EXPECTED_INTEGRATION_COMMANDS = {
14
- codex: {
15
- install: 'shennian integration codex install --confirm-user-action',
16
- update: 'shennian integration codex update --confirm-user-action',
17
- },
18
- claude_code: {
19
- install: 'shennian integration claude-code install --confirm-user-action',
20
- update: 'shennian integration claude-code update --confirm-user-action',
21
- },
22
- workbuddy: {
23
- install: 'shennian integration workbuddy install --confirm-user-action',
24
- update: 'shennian integration workbuddy update --confirm-user-action',
25
- },
26
- };
27
- export class RoomInviteBootstrapError extends Error {
28
- code;
29
- constructor(code, message) {
30
- super(message);
31
- this.code = code;
32
- this.name = 'RoomInviteBootstrapError';
33
- }
34
- }
35
- export async function fetchTrustedRoomAgentBootstrap(fetchImpl = fetch) {
36
- let response;
37
- try {
38
- response = await fetchImpl(OFFICIAL_BOOTSTRAP_URL, {
39
- headers: { accept: 'application/json' },
40
- redirect: 'error',
41
- signal: AbortSignal.timeout(10_000),
42
- });
43
- }
44
- catch {
45
- throw new RoomInviteBootstrapError('room_bootstrap_unavailable', 'Official Shennian Room setup information is unavailable.');
46
- }
47
- if (!response.ok) {
48
- throw new RoomInviteBootstrapError('room_bootstrap_unavailable', 'Official Shennian Room setup information is unavailable.');
49
- }
50
- let value;
51
- try {
52
- value = await response.json();
53
- }
54
- catch {
55
- throw invalidBootstrap();
56
- }
57
- return validateTrustedRoomAgentBootstrap(value);
58
- }
59
- export function validateTrustedRoomAgentBootstrap(value) {
60
- if (!value || typeof value !== 'object' || Array.isArray(value))
61
- throw invalidBootstrap();
62
- const root = value;
63
- const install = readObject(root.install);
64
- const roomTool = readObject(root.roomTool);
65
- const integrationCommands = roomTool.integrationCommands === undefined
66
- ? EXPECTED_INTEGRATION_COMMANDS
67
- : readObject(roomTool.integrationCommands);
68
- if (root.schemaVersion !== 1 ||
69
- root.product !== 'shennian' ||
70
- root.inviteKind !== 'room' ||
71
- install.documentationUrl !== 'https://shennian.net/agent-install.md' ||
72
- install.npmPackage !== 'shennian' ||
73
- install.distTag !== 'latest' ||
74
- install.installCommand !== 'npm install -g shennian@latest' ||
75
- install.upgradeCommand !== 'shennian upgrade' ||
76
- typeof install.minimumCliVersion !== 'string' ||
77
- !/^\d+\.\d+\.\d+$/.test(install.minimumCliVersion) ||
78
- roomTool.schemaVersion !== 1 ||
79
- !sameStrings(roomTool.actions, EXPECTED_ACTIONS) ||
80
- !sameStrings(roomTool.supportedRuntimes, EXPECTED_RUNTIMES) ||
81
- !sameIntegrationCommands(integrationCommands)) {
82
- throw invalidBootstrap();
83
- }
84
- return {
85
- documentationUrl: 'https://shennian.net/agent-install.md',
86
- installCommand: 'npm install -g shennian@latest',
87
- upgradeCommand: 'shennian upgrade',
88
- minimumCliVersion: install.minimumCliVersion,
89
- actions: EXPECTED_ACTIONS,
90
- supportedRuntimes: EXPECTED_RUNTIMES,
91
- integrationCommands: EXPECTED_INTEGRATION_COMMANDS,
92
- };
93
- }
94
- function sameIntegrationCommands(value) {
95
- if (Object.keys(value).sort().join(',') !== [...EXPECTED_RUNTIMES].sort().join(','))
96
- return false;
97
- return EXPECTED_RUNTIMES.every((runtime) => {
98
- const commands = value[runtime];
99
- if (!commands || typeof commands !== 'object' || Array.isArray(commands))
100
- return false;
101
- const object = commands;
102
- const expected = EXPECTED_INTEGRATION_COMMANDS[runtime];
103
- return (Object.keys(object).sort().join(',') === 'install,update' &&
104
- object.install === expected.install &&
105
- object.update === expected.update);
106
- });
107
- }
108
- export function resolveRoomClientPreparation(currentVersion, bootstrap) {
109
- if (currentVersion === null) {
110
- return {
111
- status: 'install_required',
112
- documentationUrl: bootstrap.documentationUrl,
113
- command: bootstrap.installCommand,
114
- };
115
- }
116
- const current = parseVersion(currentVersion);
117
- const minimum = parseVersion(bootstrap.minimumCliVersion);
118
- if (!current || !minimum) {
119
- throw new RoomInviteBootstrapError('invalid_local_cli_version', 'The installed Shennian CLI version could not be verified.');
120
- }
121
- if (compareVersion(current, minimum) < 0) {
122
- return {
123
- status: 'upgrade_required',
124
- documentationUrl: bootstrap.documentationUrl,
125
- command: bootstrap.upgradeCommand,
126
- };
127
- }
128
- return { status: 'ready' };
129
- }
130
- function readObject(value) {
131
- if (!value || typeof value !== 'object' || Array.isArray(value))
132
- throw invalidBootstrap();
133
- return value;
134
- }
135
- function sameStrings(value, expected) {
136
- return (Array.isArray(value) &&
137
- value.length === expected.length &&
138
- value.every((entry, index) => entry === expected[index]));
139
- }
140
- function parseVersion(value) {
141
- const match = /^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(value.trim());
142
- if (!match)
143
- return null;
144
- const parts = [Number(match[1]), Number(match[2]), Number(match[3])];
145
- return parts.every(Number.isSafeInteger) ? parts : null;
146
- }
147
- function compareVersion(left, right) {
148
- for (let index = 0; index < 3; index += 1) {
149
- if (left[index] !== right[index])
150
- return left[index] - right[index];
151
- }
152
- return 0;
153
- }
154
- function invalidBootstrap() {
155
- return new RoomInviteBootstrapError('invalid_room_bootstrap', 'Official Shennian Room setup information failed validation.');
156
- }
1
+ const p="https://shennian.net/api/room-invites/agent-bootstrap",d=["room.create","room.join","room.read","room.send","room.mode","room.status"],r=["codex","claude_code","workbuddy"],m={codex:{install:"shennian integration codex install --confirm-user-action",update:"shennian integration codex update --confirm-user-action"},claude_code:{install:"shennian integration claude-code install --confirm-user-action",update:"shennian integration claude-code update --confirm-user-action"},workbuddy:{install:"shennian integration workbuddy install --confirm-user-action",update:"shennian integration workbuddy update --confirm-user-action"}};class a extends Error{code;constructor(t,n){super(n),this.code=t,this.name="RoomInviteBootstrapError"}}async function y(o=fetch){let t;try{t=await o(p,{headers:{accept:"application/json"},redirect:"error",signal:AbortSignal.timeout(1e4)})}catch{throw new a("room_bootstrap_unavailable","Official Shennian Room setup information is unavailable.")}if(!t.ok)throw new a("room_bootstrap_unavailable","Official Shennian Room setup information is unavailable.");let n;try{n=await t.json()}catch{throw s()}return f(n)}function f(o){if(!o||typeof o!="object"||Array.isArray(o))throw s();const t=o,n=c(t.install),e=c(t.roomTool),i=e.integrationCommands===void 0?m:c(e.integrationCommands);if(t.schemaVersion!==1||t.product!=="shennian"||t.inviteKind!=="room"||n.documentationUrl!=="https://shennian.net/agent-install.md"||n.npmPackage!=="shennian"||n.distTag!=="latest"||n.installCommand!=="npm install -g shennian@latest"||n.upgradeCommand!=="shennian upgrade"||typeof n.minimumCliVersion!="string"||!/^\d+\.\d+\.\d+$/.test(n.minimumCliVersion)||e.schemaVersion!==1||!l(e.actions,d)||!l(e.supportedRuntimes,r)||!h(i))throw s();return{documentationUrl:"https://shennian.net/agent-install.md",installCommand:"npm install -g shennian@latest",upgradeCommand:"shennian upgrade",minimumCliVersion:n.minimumCliVersion,actions:d,supportedRuntimes:r,integrationCommands:m}}function h(o){return Object.keys(o).sort().join(",")!==[...r].sort().join(",")?!1:r.every(t=>{const n=o[t];if(!n||typeof n!="object"||Array.isArray(n))return!1;const e=n,i=m[t];return Object.keys(e).sort().join(",")==="install,update"&&e.install===i.install&&e.update===i.update})}function C(o,t){if(o===null)return{status:"install_required",documentationUrl:t.documentationUrl,command:t.installCommand};const n=u(o),e=u(t.minimumCliVersion);if(!n||!e)throw new a("invalid_local_cli_version","The installed Shennian CLI version could not be verified.");return g(n,e)<0?{status:"upgrade_required",documentationUrl:t.documentationUrl,command:t.upgradeCommand}:{status:"ready"}}function c(o){if(!o||typeof o!="object"||Array.isArray(o))throw s();return o}function l(o,t){return Array.isArray(o)&&o.length===t.length&&o.every((n,e)=>n===t[e])}function u(o){const t=/^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(o.trim());if(!t)return null;const n=[Number(t[1]),Number(t[2]),Number(t[3])];return n.every(Number.isSafeInteger)?n:null}function g(o,t){for(let n=0;n<3;n+=1)if(o[n]!==t[n])return o[n]-t[n];return 0}function s(){return new a("invalid_room_bootstrap","Official Shennian Room setup information failed validation.")}export{a as RoomInviteBootstrapError,y as fetchTrustedRoomAgentBootstrap,C as resolveRoomClientPreparation,f as validateTrustedRoomAgentBootstrap};
@@ -1,5 +1,5 @@
1
1
  import type { ManagedAgentEnforcement, ManagedAgentPermissionPolicy, AgentType, RoomActivationMode } from '@shennian/wire';
2
- export declare const MANAGED_ROOM_POLICY_VERSION = "managed-room-surface-v4";
2
+ export declare const MANAGED_ROOM_POLICY_VERSION = "managed-room-surface-v5";
3
3
  export type ManagedRoomPromptInput = {
4
4
  agent: {
5
5
  displayName: string;