@parall/sdk 1.36.0 → 1.37.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.
package/src/client.ts CHANGED
@@ -41,6 +41,7 @@ import type {
41
41
  MachineRuntimeAuthState,
42
42
  AgentWorkspaceState,
43
43
  AgentWorkspaceReportStatus,
44
+ DetectedRuntime,
44
45
  BrowseMachineFilesystemResponse,
45
46
  FilesystemEntry,
46
47
  DaemonAgentConfig,
@@ -74,6 +75,14 @@ import type {
74
75
  CreateExternalConnectionInput,
75
76
  UpdateExternalConnectionInput,
76
77
  ExternalConnectionFilters,
78
+ ChannelConnection,
79
+ ChannelConversation,
80
+ ChannelMessage,
81
+ ChannelCredentialsInput,
82
+ ChannelProvisioningSession,
83
+ CreateChannelConnectionInput,
84
+ InitiateChannelProvisioningInput,
85
+ UpdateChannelConnectionInput,
77
86
  ExternalIngressEvent,
78
87
  ExternalIngressEventFilters,
79
88
  ExternalTrigger,
@@ -97,6 +106,8 @@ import type {
97
106
  PlatformConfigResponse,
98
107
  Wiki,
99
108
  WikiBlob,
109
+ CopyWikiFileRequest,
110
+ CopyWikiFileResponse,
100
111
  WikiChangeset,
101
112
  WikiFileUploadResponse,
102
113
  WikiFilePreviewUrlResponse,
@@ -104,6 +115,7 @@ import type {
104
115
  WikiNodeSectionArtifact,
105
116
  WikiSearchResponse,
106
117
  WikiPageIndex,
118
+ SearchParams,
107
119
  SearchResponse,
108
120
  WikiRefsResponse,
109
121
  WikiRefsCheckResponse,
@@ -132,6 +144,7 @@ import type {
132
144
  DispatchExpireResult,
133
145
  ResolveRefsResponse,
134
146
  BacklinksResponse,
147
+ RefGraphResponse,
135
148
  BrokenRefsResponse,
136
149
  PushSubscribeRequest,
137
150
  NotifPrefs,
@@ -161,6 +174,8 @@ import type {
161
174
  UpdateAutoReloadRequest,
162
175
  ComputePricing,
163
176
  ComputePricingResponse,
177
+ RuntimeCapability,
178
+ PlatformModelsResponse,
164
179
  Clip,
165
180
  AgentClip,
166
181
  CreateClipRequest,
@@ -173,8 +188,10 @@ import type {
173
188
  RegistryClipInfo,
174
189
  MachineClip,
175
190
  BrowserProfile,
191
+ MachineBrowserProfile,
176
192
  BrowserProfileListItem,
177
193
  BrowserProfileStatus,
194
+ BrowserRuntimeStatus,
178
195
  BrowserProfileConsent,
179
196
  CreateBrowserProfileRequest,
180
197
  UpdateBrowserProfileRequest,
@@ -628,6 +645,17 @@ export class ParallClient {
628
645
  return res.data;
629
646
  }
630
647
 
648
+ /** User IDs of soft-removed (former) org members — for marking their avatar /
649
+ * name as "left" where dormant relations still surface them (DMs, task
650
+ * assignees, message history). */
651
+ async getFormerMemberIds(orgId: string): Promise<string[]> {
652
+ const res = await this.request<{ user_ids: string[] }>(
653
+ 'GET',
654
+ ENDPOINTS.ORG_MEMBERS_FORMER(orgId),
655
+ );
656
+ return res.user_ids ?? [];
657
+ }
658
+
631
659
  async getTeams(orgId: string): Promise<Team[]> {
632
660
  const res = await this.request<{ data: Team[] }>('GET', ENDPOINTS.TEAMS(orgId));
633
661
  return res.data;
@@ -950,6 +978,8 @@ export class ParallClient {
950
978
  limit?: number;
951
979
  thread_root_id?: string;
952
980
  top_level?: boolean;
981
+ /** RFC3339 or YYYY-MM-DD; filters messages created at/after this instant. Independent of before/after cursors. */
982
+ since?: string;
953
983
  },
954
984
  ): Promise<PaginatedResponse<Message>> {
955
985
  return this.request(
@@ -1495,9 +1525,10 @@ export class ParallClient {
1495
1525
  return res.data;
1496
1526
  }
1497
1527
 
1498
- /** `GET /machines/me/browser-profiles` — browser profiles hosted by this machine. */
1499
- async listMachineBrowserProfiles(): Promise<BrowserProfile[]> {
1500
- const res = await this.request<{ data: BrowserProfile[] }>(
1528
+ /** `GET /machines/me/browser-profiles` — browser profiles hosted by this machine.
1529
+ * Returns the daemon DTO (carries proxy_password for bb-browser replay). */
1530
+ async listMachineBrowserProfiles(): Promise<MachineBrowserProfile[]> {
1531
+ const res = await this.request<{ data: MachineBrowserProfile[] }>(
1501
1532
  'GET',
1502
1533
  ENDPOINTS.MACHINES_ME_BROWSER_PROFILES,
1503
1534
  );
@@ -1509,29 +1540,40 @@ export class ParallClient {
1509
1540
  profileId: string,
1510
1541
  status: BrowserProfileStatus,
1511
1542
  errorMsg?: string,
1543
+ // The lifecycle generation this report was issued under (captured at op start,
1544
+ // PR #1650). Omitted by pre-generation callers; the server then falls back to
1545
+ // safe-state compatibility. The server no-ops a report older than the row's.
1546
+ generation?: number,
1512
1547
  ): Promise<void> {
1513
1548
  await this.request('POST', ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_STATUS(profileId), {
1514
1549
  status,
1515
1550
  ...(errorMsg ? { error_msg: errorMsg } : {}),
1551
+ ...(generation !== undefined ? { generation } : {}),
1516
1552
  });
1517
1553
  }
1518
1554
 
1519
1555
  /**
1520
1556
  * `POST /machines/me/health` — bump the Machine's `updated_at` to now and
1521
- * report daemon state. The daemon should call this on a fixed cadence (e.g.
1522
- * every 30s) so an external observer can detect a wedged supervisor. Both
1557
+ * report daemon state. The daemon calls this on startup and again whenever
1558
+ * periodic runtime re-detection produces a CHANGED result (there is no
1559
+ * fixed-cadence keepalive loop today — steady state posts nothing). All
1523
1560
  * fields are optional and only persisted when changed:
1524
1561
  * - `daemonVersion` — the running bundle/launcher version.
1525
1562
  * - `selfUpdateCapable` — whether the daemon can act on a machine.update
1526
1563
  * signal (true under a service manager, false for bare `npx` foreground).
1564
+ * - `detectedRuntimes` — runtime CLIs found on the host. Omitted = no
1565
+ * report this beat (server keeps the stored value); [] = detection ran
1566
+ * and found nothing (server clears to empty).
1527
1567
  */
1528
1568
  async postMachineHeartbeat(opts?: {
1529
1569
  daemonVersion?: string;
1530
1570
  selfUpdateCapable?: boolean;
1571
+ detectedRuntimes?: DetectedRuntime[];
1531
1572
  }): Promise<void> {
1532
1573
  const body: Record<string, unknown> = {};
1533
1574
  if (opts?.daemonVersion) body.daemon_version = opts.daemonVersion;
1534
1575
  if (opts?.selfUpdateCapable !== undefined) body.self_update_capable = opts.selfUpdateCapable;
1576
+ if (opts?.detectedRuntimes !== undefined) body.detected_runtimes = opts.detectedRuntimes;
1535
1577
  return this.request(
1536
1578
  'POST',
1537
1579
  ENDPOINTS.MACHINES_ME_HEALTH,
@@ -2159,6 +2201,124 @@ export class ParallClient {
2159
2201
  return this.request('DELETE', ENDPOINTS.EXTERNAL_CONNECTION(orgId, connectionId));
2160
2202
  }
2161
2203
 
2204
+ // ---- External IM channel (org-scoped) ----
2205
+
2206
+ async createChannelConnection(
2207
+ orgId: string,
2208
+ input: CreateChannelConnectionInput,
2209
+ ): Promise<ChannelConnection> {
2210
+ return this.request('POST', ENDPOINTS.CHANNEL_CONNECTIONS(orgId), input);
2211
+ }
2212
+
2213
+ async listChannelConnections(orgId: string): Promise<{ connections: ChannelConnection[] }> {
2214
+ return this.request('GET', ENDPOINTS.CHANNEL_CONNECTIONS(orgId));
2215
+ }
2216
+
2217
+ async getChannelConnection(orgId: string, connectionId: string): Promise<ChannelConnection> {
2218
+ return this.request('GET', ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
2219
+ }
2220
+
2221
+ async updateChannelConnection(
2222
+ orgId: string,
2223
+ connectionId: string,
2224
+ patch: UpdateChannelConnectionInput,
2225
+ ): Promise<ChannelConnection> {
2226
+ return this.request('PATCH', ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId), patch);
2227
+ }
2228
+
2229
+ async archiveChannelConnection(orgId: string, connectionId: string): Promise<ChannelConnection> {
2230
+ return this.request('DELETE', ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
2231
+ }
2232
+
2233
+ async deliverChannelCredentials(
2234
+ orgId: string,
2235
+ connectionId: string,
2236
+ credentials: ChannelCredentialsInput,
2237
+ ): Promise<ChannelConnection> {
2238
+ return this.request(
2239
+ 'POST',
2240
+ ENDPOINTS.CHANNEL_CONNECTION_CREDENTIALS(orgId, connectionId),
2241
+ credentials,
2242
+ );
2243
+ }
2244
+
2245
+ async regenerateChannelIngressToken(
2246
+ orgId: string,
2247
+ connectionId: string,
2248
+ ): Promise<ChannelConnection> {
2249
+ return this.request(
2250
+ 'POST',
2251
+ ENDPOINTS.CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE(orgId, connectionId),
2252
+ );
2253
+ }
2254
+
2255
+ /** Start a Feishu one-click provisioning session (device flow QR). */
2256
+ async initiateChannelProvisioning(
2257
+ orgId: string,
2258
+ input: InitiateChannelProvisioningInput,
2259
+ ): Promise<ChannelProvisioningSession> {
2260
+ return this.request('POST', ENDPOINTS.CHANNEL_PROVISIONING(orgId), input);
2261
+ }
2262
+
2263
+ /**
2264
+ * Lazy status poll — server-side this may forward one provider poll, so
2265
+ * call it at the session's `poll_interval_seconds` cadence, not faster.
2266
+ */
2267
+ async getChannelProvisioningSession(
2268
+ orgId: string,
2269
+ sessionId: string,
2270
+ ): Promise<ChannelProvisioningSession> {
2271
+ return this.request('GET', ENDPOINTS.CHANNEL_PROVISIONING_SESSION(orgId, sessionId));
2272
+ }
2273
+
2274
+ async cancelChannelProvisioning(
2275
+ orgId: string,
2276
+ sessionId: string,
2277
+ ): Promise<ChannelProvisioningSession> {
2278
+ return this.request('POST', ENDPOINTS.CHANNEL_PROVISIONING_CANCEL(orgId, sessionId));
2279
+ }
2280
+
2281
+ async listChannelConversations(
2282
+ orgId: string,
2283
+ connectionId: string,
2284
+ ): Promise<{ conversations: ChannelConversation[] }> {
2285
+ return this.request('GET', ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
2286
+ }
2287
+
2288
+ async getChannelConversation(
2289
+ orgId: string,
2290
+ conversationId: string,
2291
+ ): Promise<ChannelConversation> {
2292
+ return this.request('GET', ENDPOINTS.CHANNEL_CONVERSATION(orgId, conversationId));
2293
+ }
2294
+
2295
+ async listChannelConversationMessages(
2296
+ orgId: string,
2297
+ conversationId: string,
2298
+ limit?: number,
2299
+ ): Promise<{ messages: ChannelMessage[] }> {
2300
+ return this.request(
2301
+ 'GET',
2302
+ ENDPOINTS.CHANNEL_CONVERSATION_MESSAGES(orgId, conversationId),
2303
+ undefined,
2304
+ { limit },
2305
+ );
2306
+ }
2307
+
2308
+ async setChannelConversationSession(
2309
+ orgId: string,
2310
+ conversationId: string,
2311
+ agentSessionId: string,
2312
+ ): Promise<ChannelConversation> {
2313
+ return this.request('PATCH', ENDPOINTS.CHANNEL_CONVERSATION_SESSION(orgId, conversationId), {
2314
+ agent_session_id: agentSessionId,
2315
+ });
2316
+ }
2317
+
2318
+ async getChannelMessage(orgId: string, messageId: string): Promise<ChannelMessage> {
2319
+ return this.request('GET', ENDPOINTS.CHANNEL_MESSAGE(orgId, messageId));
2320
+ }
2321
+
2162
2322
  async getExternalTriggerSchema(
2163
2323
  orgId: string,
2164
2324
  connectionId: string,
@@ -2248,6 +2408,26 @@ export class ParallClient {
2248
2408
  return this.request('GET', ENDPOINTS.WIKI(orgId, wikiId));
2249
2409
  }
2250
2410
 
2411
+ /** Soft-delete an entire wiki (org owner only; the default wiki is
2412
+ * protected → 403 WIKI_PROTECTED). Returns the deleted wiki. Recoverable
2413
+ * via restoreWiki within the retention window. */
2414
+ async deleteWiki(orgId: string, wikiId: string): Promise<Wiki> {
2415
+ return this.request('DELETE', ENDPOINTS.WIKI(orgId, wikiId));
2416
+ }
2417
+
2418
+ /** Restore a soft-deleted wiki (org owner only). 409 WIKI_PURGE_STARTED once
2419
+ * purge has been claimed (no longer restorable), or 404 once the row is gone.
2420
+ * Returns the restored wiki. */
2421
+ async restoreWiki(orgId: string, wikiId: string): Promise<Wiki> {
2422
+ return this.request('POST', ENDPOINTS.WIKI_RESTORE(orgId, wikiId));
2423
+ }
2424
+
2425
+ /** Recycle bin — list soft-deleted wikis for the org (owner only). */
2426
+ async getDeletedWikis(orgId: string): Promise<Wiki[]> {
2427
+ const res = await this.request<{ data: Wiki[] }>('GET', ENDPOINTS.WIKIS_DELETED(orgId));
2428
+ return res.data;
2429
+ }
2430
+
2251
2431
  async getWikiTree(
2252
2432
  orgId: string,
2253
2433
  wikiId: string,
@@ -2295,6 +2475,20 @@ export class ParallClient {
2295
2475
  return blob;
2296
2476
  }
2297
2477
 
2478
+ /**
2479
+ * Copy one wiki file to another path as an independent snapshot (no sync).
2480
+ * Used to "share a private file out": copy a file from the caller's personal
2481
+ * namespace (`users/{userId}/…`) into the public wiki, leaving the original
2482
+ * untouched. 409 `FILE_EXISTS` if `dest_path` already exists.
2483
+ */
2484
+ async copyWikiFile(
2485
+ orgId: string,
2486
+ wikiId: string,
2487
+ req: CopyWikiFileRequest,
2488
+ ): Promise<CopyWikiFileResponse> {
2489
+ return this.request('POST', ENDPOINTS.WIKI_COPY(orgId, wikiId), req);
2490
+ }
2491
+
2298
2492
  async getWikiNodeSections(
2299
2493
  orgId: string,
2300
2494
  wikiId: string,
@@ -2305,9 +2499,7 @@ export class ParallClient {
2305
2499
 
2306
2500
  async search(
2307
2501
  orgId: string,
2308
- // `types` selects entities (message/task/wiki); `wiki_type` is the distinct
2309
- // wiki document-type facet (frontmatter `type`) — the two never overlap.
2310
- params: { q: string; types?: string; chat_id?: string; limit?: number; wiki_type?: string },
2502
+ params: SearchParams,
2311
2503
  opts?: { signal?: AbortSignal },
2312
2504
  ): Promise<SearchResponse> {
2313
2505
  return this.request('GET', ENDPOINTS.SEARCH(orgId), undefined, params, false, opts);
@@ -2680,6 +2872,20 @@ export class ParallClient {
2680
2872
  return this.request('GET', ENDPOINTS.REFS_BACKLINKS(orgId), undefined, params);
2681
2873
  }
2682
2874
 
2875
+ /**
2876
+ * Bounded multi-hop walk of the prll:// reference graph around `uri`. `uri`
2877
+ * must be an entity-level prll:// URI — a refined URI (path/query/fragment) is
2878
+ * rejected with 400 UNSUPPORTED_REFINED_URI. `depth` is clamped server-side to
2879
+ * [1, 4]; breadth (node/edge counts) is capped server-side and surfaced via
2880
+ * `truncated`. ACL is applied per hop (chat membership + wiki path scope).
2881
+ */
2882
+ async getRefsGraph(
2883
+ orgId: string,
2884
+ params: { uri: string; depth?: number },
2885
+ ): Promise<RefGraphResponse> {
2886
+ return this.request('GET', ENDPOINTS.REFS_GRAPH(orgId), undefined, params);
2887
+ }
2888
+
2683
2889
  async checkBrokenRefs(orgId: string): Promise<BrokenRefsResponse> {
2684
2890
  return this.request('GET', ENDPOINTS.REFS_CHECK(orgId));
2685
2891
  }
@@ -2810,6 +3016,21 @@ export class ParallClient {
2810
3016
  return resp.data;
2811
3017
  }
2812
3018
 
3019
+ /** Runtime capability table (public) — SSOT for the create/settings interlock. */
3020
+ async getRuntimes(): Promise<RuntimeCapability[]> {
3021
+ const resp = await this.request<{ data: RuntimeCapability[] }>('GET', ENDPOINTS.RUNTIMES());
3022
+ return resp.data;
3023
+ }
3024
+
3025
+ /**
3026
+ * Platform model catalog (public) — DB-backed, replaces the compile-time
3027
+ * PLATFORM_MODELS constant. Pass includeModel to keep an agent's pinned
3028
+ * hidden legacy model representable (settings picker).
3029
+ */
3030
+ async getModels(includeModel?: string): Promise<PlatformModelsResponse> {
3031
+ return this.request<PlatformModelsResponse>('GET', ENDPOINTS.MODELS(includeModel));
3032
+ }
3033
+
2813
3034
  // ---- Clips ----
2814
3035
 
2815
3036
  async listClips(orgId: string): Promise<Clip[]> {
@@ -2877,6 +3098,12 @@ export class ParallClient {
2877
3098
  return resp.data;
2878
3099
  }
2879
3100
 
3101
+ /** Deployment-wide hosted browser runtime availability — drives whether the
3102
+ * create UI offers the platform-hosted placement. Fail-closed server-side. */
3103
+ async getBrowserRuntimeStatus(orgId: string): Promise<BrowserRuntimeStatus> {
3104
+ return this.request('GET', ENDPOINTS.BROWSER_RUNTIME_STATUS(orgId));
3105
+ }
3106
+
2880
3107
  /** Org-wide browser-profile discovery list. Returns the sanitized
2881
3108
  * {@link BrowserProfileListItem} shape (not the full domain model), each row
2882
3109
  * carrying a per-viewer `can_open` control hint. */
package/src/constants.ts CHANGED
@@ -7,11 +7,17 @@ export const WIKI_BASE = '/wiki/v1';
7
7
  // Clip-service base path (served by clip-service, routed via LB path rules)
8
8
  export const CLIP_BASE = '/clip/v1';
9
9
 
10
- // LLM model catalog — mirrors server/pkg/llmproxy/models.go DefaultModels.
11
- // SSOT is the Go side; keep this list in sync when models change. `provider`
12
- // is used by clients to filter runtime-specific pickers. `runtime_names` is
13
- // included for catalog completeness and agent runtime delivery; product UI
14
- // should continue to display canonical model IDs/names.
10
+ // LLM model catalog — frozen compile-time mirror of the catalog at the time
11
+ // the DB-backed source shipped (docs/engineering-design/model-catalog-design.md).
12
+ // Kept ONLY as the offline/degraded fallback for pickers; it is NOT updated
13
+ // when models are added through the admin catalog.
14
+ /**
15
+ * @deprecated The catalog is DB-backed — fetch it via `client.getModels()`
16
+ * (GET /api/v1/models). This constant is a frozen degraded-mode fallback and
17
+ * will be removed after one release cycle. Still structurally consumed by
18
+ * FALLBACK_CATALOG (`@parall/app` agent-interlock) — do not delete before
19
+ * that fallback strategy changes (PR-3).
20
+ */
15
21
  export const PLATFORM_MODELS = [
16
22
  // Fable 5 rejects an explicit thinking "disabled" param (400) — bridges must
17
23
  // omit the thinking config entirely instead of disabling it.
@@ -211,6 +217,12 @@ export const PLATFORM_MODELS = [
211
217
 
212
218
  // Hidden from new-agent pickers, but available to editors so persisted Beta
213
219
  // agent defaults remain representable.
220
+ /**
221
+ * @deprecated Fetch via `client.getModels(currentModelId)` — the include_model
222
+ * parameter serves the legacy-pin case. Frozen degraded-mode fallback only.
223
+ * Still structurally consumed by FALLBACK_CATALOG (`@parall/app`
224
+ * agent-interlock) — do not delete before that fallback changes (PR-3).
225
+ */
214
226
  export const PLATFORM_LEGACY_MODELS = [
215
227
  {
216
228
  id: 'openai/gpt-5.4-mini',
@@ -284,9 +296,13 @@ export const PLATFORM_LEGACY_MODELS = [
284
296
  },
285
297
  ] as const;
286
298
 
287
- // Mirrors server/pkg/llmproxy/models.go DefaultModelIDForRuntime. Go remains
288
- // the SSOT; keep this client-side fallback in sync until the catalog is codegen
289
- // or fetched before render.
299
+ // Frozen mirror of the per-runtime defaults at ship time of the DB-backed
300
+ // catalog (llm_runtime_defaults). Live values arrive as recommended_model on
301
+ // GET /runtimes.
302
+ /**
303
+ * @deprecated Read `recommended_model` from `client.getRuntimes()` instead.
304
+ * Frozen degraded-mode fallback only.
305
+ */
290
306
  export const PLATFORM_DEFAULT_MODEL_BY_RUNTIME = {
291
307
  openclaw: 'anthropic/claude-sonnet-4.6',
292
308
  'claude-code': 'anthropic/claude-sonnet-4.6',
@@ -312,6 +328,11 @@ export function platformDefaultModelForRuntime(runtime: string | null | undefine
312
328
  case '':
313
329
  case 'openclaw':
314
330
  case 'claude-code':
331
+ // hermes / parel route through the Parall proxy and accept any model; the
332
+ // server (DefaultModelIDForRuntime) recommends the platform default, so
333
+ // mirror it.
334
+ case 'hermes':
335
+ case 'parel':
315
336
  return PLATFORM_DEFAULT_MODEL_BY_RUNTIME.openclaw;
316
337
  case 'codex':
317
338
  return PLATFORM_DEFAULT_MODEL_BY_RUNTIME.codex;
@@ -347,6 +368,7 @@ export const ENDPOINTS = {
347
368
  // Org-scoped
348
369
  ORG: (orgId: string) => `${API_BASE}/orgs/${orgId}`,
349
370
  ORG_MEMBERS: (orgId: string) => `${API_BASE}/orgs/${orgId}/members`,
371
+ ORG_MEMBERS_FORMER: (orgId: string) => `${API_BASE}/orgs/${orgId}/members/former`,
350
372
  TEAMS: (orgId: string) => `${API_BASE}/orgs/${orgId}/teams`,
351
373
  ORG_MEMBERS_ONLINE: (orgId: string) => `${API_BASE}/orgs/${orgId}/members/online`,
352
374
  ORG_MEMBER: (orgId: string, userId: string) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
@@ -584,6 +606,30 @@ export const ENDPOINTS = {
584
606
  EXTERNAL_TRIGGER_RUN: (orgId: string, runId: string) =>
585
607
  `${API_BASE}/orgs/${orgId}/external-trigger-runs/${runId}`,
586
608
 
609
+ // External IM channel (org-scoped, platform-mediated Feishu/Slack)
610
+ CHANNEL_CONNECTIONS: (orgId: string) => `${API_BASE}/orgs/${orgId}/channel-connections`,
611
+ CHANNEL_CONNECTION: (orgId: string, connectionId: string) =>
612
+ `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}`,
613
+ CHANNEL_CONNECTION_CREDENTIALS: (orgId: string, connectionId: string) =>
614
+ `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/credentials`,
615
+ CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE: (orgId: string, connectionId: string) =>
616
+ `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/ingress-token/regenerate`,
617
+ CHANNEL_CONNECTION_CONVERSATIONS: (orgId: string, connectionId: string) =>
618
+ `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/conversations`,
619
+ CHANNEL_CONVERSATION: (orgId: string, conversationId: string) =>
620
+ `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}`,
621
+ CHANNEL_CONVERSATION_MESSAGES: (orgId: string, conversationId: string) =>
622
+ `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/messages`,
623
+ CHANNEL_CONVERSATION_SESSION: (orgId: string, conversationId: string) =>
624
+ `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/session`,
625
+ CHANNEL_MESSAGE: (orgId: string, messageId: string) =>
626
+ `${API_BASE}/orgs/${orgId}/channel-messages/${messageId}`,
627
+ CHANNEL_PROVISIONING: (orgId: string) => `${API_BASE}/orgs/${orgId}/channel-provisioning`,
628
+ CHANNEL_PROVISIONING_SESSION: (orgId: string, sessionId: string) =>
629
+ `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}`,
630
+ CHANNEL_PROVISIONING_CANCEL: (orgId: string, sessionId: string) =>
631
+ `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}/cancel`,
632
+
587
633
  // Invitations (org-scoped, admin)
588
634
  ORG_INVITATIONS: (orgId: string) => `${API_BASE}/orgs/${orgId}/invitations`,
589
635
  ORG_INVITATION: (orgId: string, invId: string) =>
@@ -608,9 +654,17 @@ export const ENDPOINTS = {
608
654
 
609
655
  // Wikis (org-scoped, served by wiki-service)
610
656
  WIKIS: (orgId: string) => `${WIKI_BASE}/orgs/${orgId}/wikis`,
657
+ // Recycle bin — soft-deleted wikis (owner only). Must precede WIKI in the
658
+ // backend router so `deleted` isn't captured as a {wikiId}; the SDK builder
659
+ // is just a string.
660
+ WIKIS_DELETED: (orgId: string) => `${WIKI_BASE}/orgs/${orgId}/wikis/deleted`,
661
+ // Detail URL — also reused for DELETE (soft-delete) and `${WIKI}/restore`.
611
662
  WIKI: (orgId: string, wikiId: string) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}`,
663
+ WIKI_RESTORE: (orgId: string, wikiId: string) =>
664
+ `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/restore`,
612
665
  WIKI_TREE: (orgId: string, wikiId: string) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/tree`,
613
666
  WIKI_BLOB: (orgId: string, wikiId: string) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/blob`,
667
+ WIKI_COPY: (orgId: string, wikiId: string) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/copy`,
614
668
  WIKI_NODE_SECTIONS: (orgId: string, wikiId: string) =>
615
669
  `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/node-sections`,
616
670
  WIKI_SEARCH: (orgId: string, wikiId: string) =>
@@ -706,6 +760,7 @@ export const ENDPOINTS = {
706
760
  // References (org-scoped)
707
761
  REFS_RESOLVE: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/resolve`,
708
762
  REFS_BACKLINKS: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/backlinks`,
763
+ REFS_GRAPH: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/graph`,
709
764
  REFS_CHECK: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/check`,
710
765
 
711
766
  // Platform config (agent-scoped, not org-scoped)
@@ -733,6 +788,19 @@ export const ENDPOINTS = {
733
788
  BILLING_SETUP_INTENT: (orgId: string) => `${API_BASE}/orgs/${orgId}/billing/setup-intent`,
734
789
  COMPUTE_PRICING: () => `${API_BASE}/billing/compute-pricing`,
735
790
 
791
+ // Runtime capability table (public, no auth) — SSOT for the create/settings
792
+ // interlock: per-runtime compute modes, native model family, cross-family
793
+ // availability, and recommended model.
794
+ RUNTIMES: () => `${API_BASE}/runtimes`,
795
+
796
+ // Model catalog (public, no auth) — served from the server's DB-backed
797
+ // snapshot. Optional include_model keeps an agent's pinned hidden legacy
798
+ // model representable in settings pickers.
799
+ MODELS: (includeModel?: string) =>
800
+ includeModel
801
+ ? `${API_BASE}/models?include_model=${encodeURIComponent(includeModel)}`
802
+ : `${API_BASE}/models`,
803
+
736
804
  // ADMIN_GRANTS intentionally NOT exported here — it sits under the
737
805
  // unauthenticated `/internal/admin/*` surface and must not bleed into the
738
806
  // public SDK. Admin dashboard hits the URL directly from its own client.
@@ -749,6 +817,7 @@ export const ENDPOINTS = {
749
817
  `${CLIP_BASE}/orgs/${orgId}/agents/${agentId}/clips/${clipId}`,
750
818
  CLIP_INVOKE: (orgId: string) => `${CLIP_BASE}/orgs/${orgId}/invoke`,
751
819
  CLIP_ONLINE: (orgId: string) => `${CLIP_BASE}/orgs/${orgId}/online`,
820
+ BROWSER_RUNTIME_STATUS: (orgId: string) => `${CLIP_BASE}/orgs/${orgId}/browser-runtime/status`,
752
821
  BROWSER_PROFILES: (orgId: string) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles`,
753
822
  BROWSER_PROFILE: (orgId: string, profileId: string) =>
754
823
  `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}`,
@@ -771,6 +840,42 @@ export const ENDPOINTS = {
771
840
  CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`,
772
841
  } as const;
773
842
 
843
+ /**
844
+ * Wiki personal-namespace prefix — the path subtree a member owns for
845
+ * self-service private spaces: `users/{userId}/`. SSOT mirror of the
846
+ * wiki-service `personalNamespacePrefix` (server/internal/wiki/handler/acl.go):
847
+ * a member may create/manage path restrictions anywhere under this prefix
848
+ * without org/path admin. The trailing slash is load-bearing — it keeps a user
849
+ * ID that is a string prefix of another from matching across namespaces.
850
+ */
851
+ export function wikiPersonalNamespacePrefix(userId: string): string {
852
+ return `users/${userId}/`;
853
+ }
854
+
855
+ /**
856
+ * Whether `path` falls inside `userId`'s personal namespace (the namespace root
857
+ * itself or any descendant). Mirrors the server's `ownsPersonalNamespacePath`;
858
+ * use it for UI affordance gating only — the wiki-service stays the security
859
+ * SSOT and re-checks on every mutation.
860
+ */
861
+ export function isInWikiPersonalNamespace(userId: string, path: string): boolean {
862
+ if (!userId) return false;
863
+ const prefix = wikiPersonalNamespacePrefix(userId);
864
+ return path === prefix || path.startsWith(prefix);
865
+ }
866
+
867
+ /**
868
+ * Whether `path` falls inside ANY user's personal namespace (`users/{id}/…`),
869
+ * regardless of whose. UI affordance helper — e.g. badging member personal-space
870
+ * restriction rows in the admin Access tab, or keeping "copy to shared wiki"
871
+ * destinations out of personal namespaces. Matches by shape (`users/<segment>/`
872
+ * or the bare `users/<segment>`), deliberately broader than
873
+ * `isInWikiPersonalNamespace`.
874
+ */
875
+ export function isWikiPersonalNamespacePath(path: string): boolean {
876
+ return /^users\/[^/]+(\/|$)/.test(path);
877
+ }
878
+
774
879
  // WebSocket event types
775
880
  export const WS_EVENTS = {
776
881
  // Client -> Server
@@ -812,10 +917,13 @@ export const WS_EVENTS = {
812
917
  INVITATION_REVOKED: 'invitation.revoked',
813
918
  ORG_JOIN_REQUEST_NEW: 'org.join_request.new',
814
919
  ORG_INVITE_LINK_JOINED: 'org.invite_link.joined',
920
+ ORG_MEMBER_REMOVED: 'org.member.removed',
815
921
  AGENT_CONFIG_UPDATE: 'agent_config.update',
816
922
  PRESENCE_UPDATE: 'presence.update',
817
923
  WIKI_CHANGESET_CREATED: 'wiki.changeset.created',
818
924
  WIKI_CHANGESET_UPDATED: 'wiki.changeset.updated',
925
+ WIKI_DELETED: 'wiki.deleted',
926
+ WIKI_RESTORED: 'wiki.restored',
819
927
  COMMENT_CREATED: 'comment.created',
820
928
  COMMENT_UPDATED: 'comment.updated',
821
929
  COMMENT_DELETED: 'comment.deleted',
@@ -843,6 +951,7 @@ export const WS_EVENTS = {
843
951
  MACHINE_FILESYSTEM_BROWSE: 'machine.filesystem.browse',
844
952
  MACHINE_UPDATE: 'machine.update',
845
953
  MACHINE_CONFIG_UPDATED: 'machine.config.updated',
954
+ MACHINE_AGENT_CONFIG_UPDATED: 'machine.agent_config.updated',
846
955
  MACHINE_CLIP_SYNC: 'machine.clip.sync',
847
956
  MACHINE_BROWSER_PROFILE_LIFECYCLE: 'machine.browser_profile.lifecycle',
848
957
  MACHINE_BROWSER_PROFILE_VIEWER: 'machine.browser_profile.viewer',