@productbrain/mcp 0.0.1-beta.997 → 0.0.1-beta.9989

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.
@@ -0,0 +1,1690 @@
1
+ // src/analytics.ts
2
+ import { userInfo } from "os";
3
+ import { PostHog } from "posthog-node";
4
+ var client = null;
5
+ var distinctId = "anonymous";
6
+ var POSTHOG_HOST = "https://eu.i.posthog.com";
7
+ function log(msg) {
8
+ if (process.env.MCP_DEBUG === "1") {
9
+ process.stderr.write(msg);
10
+ }
11
+ }
12
+ function getBuildTimeKey() {
13
+ try {
14
+ return "";
15
+ } catch {
16
+ return "";
17
+ }
18
+ }
19
+ function initAnalytics() {
20
+ const apiKey = process.env.POSTHOG_MCP_KEY || getBuildTimeKey();
21
+ if (!apiKey) {
22
+ log("[MCP-ANALYTICS] No PostHog key \u2014 tracking disabled (set SYNERGYOS_POSTHOG_KEY at build time for publish)\n");
23
+ return;
24
+ }
25
+ client = new PostHog(apiKey, {
26
+ host: POSTHOG_HOST,
27
+ flushAt: 1,
28
+ flushInterval: 5e3,
29
+ featureFlagsPollingInterval: 3e4
30
+ });
31
+ distinctId = process.env.MCP_USER_ID || fallbackDistinctId();
32
+ log(`[MCP-ANALYTICS] Initialized \u2014 host=${POSTHOG_HOST} distinctId=${distinctId}
33
+ `);
34
+ }
35
+ function fallbackDistinctId() {
36
+ try {
37
+ return userInfo().username;
38
+ } catch {
39
+ return `os-${process.pid}`;
40
+ }
41
+ }
42
+ function trackSessionStarted(workspaceId, serverVersion) {
43
+ if (!client) return;
44
+ client.capture({
45
+ distinctId,
46
+ event: "mcp_session_started",
47
+ properties: {
48
+ workspace_id: workspaceId,
49
+ server_version: serverVersion,
50
+ source: "mcp-server",
51
+ $groups: { workspace: workspaceId }
52
+ }
53
+ });
54
+ }
55
+ function trackToolCall(fn, status, durationMs, workspaceId, errorMsg) {
56
+ const properties = {
57
+ tool: fn,
58
+ status,
59
+ duration_ms: durationMs,
60
+ workspace_id: workspaceId,
61
+ source: "mcp-server",
62
+ $groups: { workspace: workspaceId }
63
+ };
64
+ if (errorMsg) properties.error = errorMsg;
65
+ if (!client) return;
66
+ client.capture({
67
+ distinctId,
68
+ event: "mcp_tool_called",
69
+ properties
70
+ });
71
+ }
72
+ function trackCompoundToolAction(tool, action, workspaceId) {
73
+ if (!client) return;
74
+ client.capture({
75
+ distinctId,
76
+ event: "mcp_compound_tool_action",
77
+ properties: {
78
+ tool,
79
+ action: action ?? null,
80
+ workspace_id: workspaceId,
81
+ source: "mcp-server",
82
+ $groups: { workspace: workspaceId }
83
+ }
84
+ });
85
+ }
86
+ function trackSetupStarted() {
87
+ if (!client) return;
88
+ client.capture({
89
+ distinctId,
90
+ event: "mcp_setup_started",
91
+ properties: {
92
+ source: "mcp-server",
93
+ platform: process.platform
94
+ }
95
+ });
96
+ }
97
+ function trackSetupCompleted(chosenClient, outcome) {
98
+ if (!client) return;
99
+ client.capture({
100
+ distinctId,
101
+ event: "mcp_setup_completed",
102
+ properties: {
103
+ client: chosenClient,
104
+ outcome,
105
+ source: "mcp-server",
106
+ platform: process.platform
107
+ }
108
+ });
109
+ }
110
+ function trackQualityVerdict(workspaceId, props) {
111
+ if (!client) return;
112
+ client.capture({
113
+ distinctId,
114
+ event: "quality_verdict_generated",
115
+ properties: {
116
+ ...props,
117
+ workspace_id: workspaceId,
118
+ source_system: "mcp-server",
119
+ $groups: { workspace: workspaceId }
120
+ }
121
+ });
122
+ }
123
+ function trackQualityCheck(workspaceId, props) {
124
+ if (!client) return;
125
+ client.capture({
126
+ distinctId,
127
+ event: "quality_verdict_checked",
128
+ properties: {
129
+ ...props,
130
+ workspace_id: workspaceId,
131
+ source_system: "mcp-server",
132
+ $groups: { workspace: workspaceId }
133
+ }
134
+ });
135
+ }
136
+ function trackCaptureClassifierEvent(event, workspaceId, props) {
137
+ if (!client) return;
138
+ try {
139
+ client.capture({
140
+ distinctId,
141
+ event,
142
+ properties: {
143
+ ...props,
144
+ workspace_id: workspaceId,
145
+ source_system: "mcp-server",
146
+ $groups: { workspace: workspaceId }
147
+ }
148
+ });
149
+ } catch {
150
+ }
151
+ }
152
+ function trackCaptureClassifierEvaluated(workspaceId, props) {
153
+ trackCaptureClassifierEvent("mcp_capture_classifier_evaluated", workspaceId, props);
154
+ }
155
+ function trackCaptureClassifierAutoRouted(workspaceId, props) {
156
+ trackCaptureClassifierEvent("mcp_capture_classifier_auto_routed", workspaceId, props);
157
+ }
158
+ function trackCaptureClassifierFallback(workspaceId, props) {
159
+ trackCaptureClassifierEvent("mcp_capture_classifier_fallback", workspaceId, props);
160
+ }
161
+ function trackChainEntryCommitted(workspaceId, props) {
162
+ if (!client) return;
163
+ try {
164
+ client.capture({
165
+ distinctId,
166
+ event: "chain_entry_committed",
167
+ properties: {
168
+ workspace_id: workspaceId,
169
+ source_system: "mcp-server",
170
+ $groups: { workspace: workspaceId },
171
+ ...props
172
+ }
173
+ });
174
+ } catch {
175
+ }
176
+ }
177
+ function trackKnowledgeGap(workspaceId, props) {
178
+ if (!client) return;
179
+ try {
180
+ client.capture({
181
+ distinctId,
182
+ event: "knowledge_gap_detected",
183
+ properties: {
184
+ ...props,
185
+ query: props.query.slice(0, 200),
186
+ workspace_id: workspaceId,
187
+ source_system: "mcp-server",
188
+ $groups: { workspace: workspaceId }
189
+ }
190
+ });
191
+ } catch {
192
+ }
193
+ }
194
+ function trackCaptureQualityHints(workspaceId, props) {
195
+ if (!client) return;
196
+ try {
197
+ client.capture({
198
+ distinctId,
199
+ event: "mcp_capture_quality_hints",
200
+ properties: {
201
+ ...props,
202
+ workspace_id: workspaceId,
203
+ source_system: "mcp-server",
204
+ $groups: { workspace: workspaceId }
205
+ }
206
+ });
207
+ } catch {
208
+ }
209
+ }
210
+ function trackCollectionClassified(workspaceId, props) {
211
+ if (!client) return;
212
+ try {
213
+ client.capture({
214
+ distinctId,
215
+ event: "collection_classified",
216
+ properties: {
217
+ ...props,
218
+ workspace_id: workspaceId,
219
+ source_system: "mcp-server",
220
+ $groups: { workspace: workspaceId }
221
+ }
222
+ });
223
+ } catch {
224
+ }
225
+ }
226
+ function trackSessionCaptureRate(workspaceId, props) {
227
+ if (!client) return;
228
+ try {
229
+ client.capture({
230
+ distinctId,
231
+ event: "session_capture_rate",
232
+ properties: {
233
+ ...props,
234
+ workspace_id: workspaceId,
235
+ source_system: "mcp-server",
236
+ $groups: { workspace: workspaceId }
237
+ }
238
+ });
239
+ } catch {
240
+ }
241
+ }
242
+ function trackZeroCaptureAuditFired(workspaceId) {
243
+ if (!client) return;
244
+ try {
245
+ client.capture({
246
+ distinctId,
247
+ event: "zero_capture_audit_fired",
248
+ properties: {
249
+ workspace_id: workspaceId,
250
+ source_system: "mcp-server",
251
+ $groups: { workspace: workspaceId }
252
+ }
253
+ });
254
+ } catch {
255
+ }
256
+ }
257
+ function trackCaptureContractMiss(workspaceId, props) {
258
+ if (!client) return;
259
+ try {
260
+ client.capture({
261
+ distinctId,
262
+ event: "capture_contract_miss",
263
+ properties: {
264
+ ...props,
265
+ workspace_id: workspaceId,
266
+ source_system: "mcp-server",
267
+ $groups: { workspace: workspaceId }
268
+ }
269
+ });
270
+ } catch {
271
+ }
272
+ }
273
+ function trackCommitErrorByCode(workspaceId, props) {
274
+ if (!client) return;
275
+ try {
276
+ client.capture({
277
+ distinctId,
278
+ event: "commit_error_by_code",
279
+ properties: {
280
+ ...props,
281
+ workspace_id: workspaceId,
282
+ source_system: "mcp-server",
283
+ $groups: { workspace: workspaceId }
284
+ }
285
+ });
286
+ } catch {
287
+ }
288
+ }
289
+ function trackClassifierDivergence(workspaceId, props) {
290
+ if (!client) return;
291
+ try {
292
+ client.capture({
293
+ distinctId,
294
+ event: "classifier_divergence",
295
+ properties: {
296
+ ...props,
297
+ workspace_id: workspaceId,
298
+ source_system: "mcp-server",
299
+ $groups: { workspace: workspaceId }
300
+ }
301
+ });
302
+ } catch {
303
+ }
304
+ }
305
+ function getPostHogClient() {
306
+ return client;
307
+ }
308
+ async function shutdownAnalytics() {
309
+ await client?.shutdown();
310
+ }
311
+
312
+ // src/auth.ts
313
+ import { AsyncLocalStorage } from "async_hooks";
314
+ import { createHash } from "crypto";
315
+ function hashKey(key) {
316
+ return createHash("sha256").update(key).digest("hex").slice(0, 16);
317
+ }
318
+ var requestStore = new AsyncLocalStorage();
319
+ function runWithAuth(auth, fn) {
320
+ return requestStore.run(auth, fn);
321
+ }
322
+ function getRequestApiKey() {
323
+ return requestStore.getStore()?.apiKey;
324
+ }
325
+ function getRequestMcpSessionId() {
326
+ return requestStore.getStore()?.mcpSessionId;
327
+ }
328
+ var SESSION_TTL_MS = 30 * 60 * 1e3;
329
+ var MAX_KEYS = 100;
330
+ var keyStateMap = /* @__PURE__ */ new Map();
331
+ function newKeyState() {
332
+ return {
333
+ workspaceId: null,
334
+ workspaceSlug: null,
335
+ workspaceName: null,
336
+ workspaceCreatedAt: null,
337
+ agentSessionId: null,
338
+ apiKeyId: null,
339
+ apiKeyScope: "readwrite",
340
+ sessionOriented: false,
341
+ sessionClosed: false,
342
+ lastAccess: Date.now(),
343
+ deploymentUrl: null
344
+ };
345
+ }
346
+ function getKeyState(apiKey) {
347
+ let s = keyStateMap.get(apiKey);
348
+ if (!s) {
349
+ s = newKeyState();
350
+ keyStateMap.set(apiKey, s);
351
+ evictStale();
352
+ }
353
+ s.lastAccess = Date.now();
354
+ return s;
355
+ }
356
+ function evictStale() {
357
+ if (keyStateMap.size <= MAX_KEYS) return;
358
+ const now = Date.now();
359
+ for (const [key, s] of keyStateMap) {
360
+ if (now - s.lastAccess > SESSION_TTL_MS) keyStateMap.delete(key);
361
+ }
362
+ if (keyStateMap.size > MAX_KEYS) {
363
+ const sorted = [...keyStateMap.entries()].sort((a, b) => a[1].lastAccess - b[1].lastAccess);
364
+ for (let i = 0; i < sorted.length - MAX_KEYS; i++) {
365
+ keyStateMap.delete(sorted[i][0]);
366
+ }
367
+ }
368
+ }
369
+
370
+ // src/cli/config-writer.ts
371
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
372
+ import { join, dirname } from "path";
373
+ import { homedir, platform } from "os";
374
+ var SERVER_ENTRY_KEY = "Product Brain";
375
+ var LEGACY_ENTRY_KEY = "productbrain";
376
+ var MCP_NPX_PACKAGE = "@productbrain/mcp@beta";
377
+ function buildServerEntry(apiKey) {
378
+ return {
379
+ command: "npx",
380
+ args: ["-y", MCP_NPX_PACKAGE],
381
+ env: { PRODUCTBRAIN_API_KEY: apiKey }
382
+ };
383
+ }
384
+ function getCursorConfigPath() {
385
+ return join(process.cwd(), ".cursor", "mcp.json");
386
+ }
387
+ function getClaudeDesktopConfigPath() {
388
+ const os = platform();
389
+ if (os === "darwin") {
390
+ return join(
391
+ homedir(),
392
+ "Library",
393
+ "Application Support",
394
+ "Claude",
395
+ "claude_desktop_config.json"
396
+ );
397
+ }
398
+ if (os === "win32") {
399
+ const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
400
+ return join(appData, "Claude", "claude_desktop_config.json");
401
+ }
402
+ return null;
403
+ }
404
+ function resolveClient(name) {
405
+ if (name === "Cursor") {
406
+ return { name, configPath: getCursorConfigPath() };
407
+ }
408
+ const configPath = getClaudeDesktopConfigPath();
409
+ return configPath ? { name, configPath } : null;
410
+ }
411
+ function readJsonSafe(path) {
412
+ if (!existsSync(path)) return {};
413
+ try {
414
+ return JSON.parse(readFileSync(path, "utf-8"));
415
+ } catch {
416
+ return {};
417
+ }
418
+ }
419
+ async function writeClientConfig(client2, apiKey) {
420
+ const config = readJsonSafe(client2.configPath);
421
+ const serversKey = "mcpServers";
422
+ if (!config[serversKey]) config[serversKey] = {};
423
+ if (config[serversKey][LEGACY_ENTRY_KEY]) {
424
+ const legacy = config[serversKey][LEGACY_ENTRY_KEY];
425
+ config[serversKey][SERVER_ENTRY_KEY] = {
426
+ ...buildServerEntry(apiKey),
427
+ env: { ...legacy.env, PRODUCTBRAIN_API_KEY: legacy.env?.PRODUCTBRAIN_API_KEY ?? apiKey }
428
+ };
429
+ delete config[serversKey][LEGACY_ENTRY_KEY];
430
+ } else {
431
+ const existing = config[serversKey][SERVER_ENTRY_KEY];
432
+ config[serversKey][SERVER_ENTRY_KEY] = existing ? { ...existing, env: { ...existing.env, PRODUCTBRAIN_API_KEY: apiKey } } : buildServerEntry(apiKey);
433
+ }
434
+ const dir = dirname(client2.configPath);
435
+ if (!existsSync(dir)) {
436
+ mkdirSync(dir, { recursive: true });
437
+ }
438
+ writeFileSync(client2.configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
439
+ return true;
440
+ }
441
+
442
+ // src/generated/routeLatencyBudget.generated.ts
443
+ var ROUTE_LATENCY_BUDGET_MS = {
444
+ query: 1e4,
445
+ mutation: 1e4,
446
+ action: 3e4
447
+ };
448
+ var ROUTE_LATENCY_BUDGET_OVERRIDE_MS = {
449
+ // convex/intelligence/onboardingChat.ts:333 declares `timeoutMs: 45_000` for the extraction
450
+ // LLM call — 15s BEYOND the flat action budget. 60s clears it with room for the surrounding
451
+ // request/response work the 45s covers none of.
452
+ "onboarding.chat": 6e4,
453
+ // convex/intelligence/spineCheck.ts:210 sets SPINE_CHECK_WALL_CLOCK_BUDGET_MS = 4 * 60_000
454
+ // for its SEQUENTIAL strategy probes (:225-239, deliberately not Promise.all), and :215
455
+ // reserves a tail of one 25s probe + an optional 25s story pass + 10s. The server's own
456
+ // design ceiling is scheduleSpineCheck's 5-min MAX_RUN_IN_FLIGHT_MS lease (:206-209).
457
+ // 5.5 min sits above that ceiling and far under Convex's 10-min action cap.
458
+ "quality.spineCheck": 33e4,
459
+ // convex/digest/compose.ts declares `timeoutMs: 120_000` (LLM_TIMEOUT_MS) for the digest
460
+ // composer's callOpenRouter call — but callOpenRouter's own MAX_RETRIES (=1,
461
+ // convex/lib/llm/openrouter.ts) means a transient-status (408/409/429/5xx) failure gets ONE
462
+ // retry of the FULL call, so the wrapper's genuine worst-case wall-clock is 2 attempts ×
463
+ // 120_000ms = 240_000ms, not a single attempt's timeout (PR #1122 round 1 review, P1 — the
464
+ // prior 150_000 sized for one attempt only). 270s clears the two-attempt worst case with
465
+ // room for the preceding cohort read (convex/digest/cohort.ts) and the surrounding
466
+ // request/response work neither the timeout nor the retry budget covers.
467
+ "chain.runDigest": 27e4
468
+ };
469
+ var WIDEST_ROUTE_LATENCY_BUDGET_MS = Math.max(
470
+ ...Object.values(ROUTE_LATENCY_BUDGET_MS),
471
+ ...Object.values(ROUTE_LATENCY_BUDGET_OVERRIDE_MS)
472
+ );
473
+ var ROUTE_TYPE_ENTRIES = {
474
+ "resolveWorkspace": "query",
475
+ "feedback.submit": "mutation",
476
+ "feedback.listOwn": "query",
477
+ "feedback.list": "action",
478
+ "feedback.note": "action",
479
+ "feedback.group": "action",
480
+ "feedback.status": "action",
481
+ "organisation.status": "query",
482
+ "organisation.removeMembership": "mutation",
483
+ "organisation.setOwnership": "mutation",
484
+ "organisation.hierarchy.show": "query",
485
+ "organisation.hierarchy.setParent": "mutation",
486
+ "organisation.hierarchy.clearParent": "mutation",
487
+ "acceptPolicy.approve": "mutation",
488
+ "acceptPolicy.retire": "mutation",
489
+ "acceptPolicy.list": "query",
490
+ "chain.seed": "mutation",
491
+ "chain.listCollections": "query",
492
+ "chain.getCollection": "query",
493
+ "chain.getCollectionFields": "query",
494
+ "chain.auditCollections": "query",
495
+ "chain.exportDefinitions": "query",
496
+ "chain.updateCollection": "mutation",
497
+ "chain.listEntries": "query",
498
+ "chain.listEntriesPaged": "query",
499
+ "chain.getEntry": "query",
500
+ "chain.preflightEntry": "query",
501
+ "chain.listByRole": "query",
502
+ "chain.getQualityVerdict": "query",
503
+ "chain.batchGetEntries": "query",
504
+ "chain.createEntry": "action",
505
+ "chain.updateEntry": "mutation",
506
+ "chain.restoreArchivedEntry": "mutation",
507
+ "chain.moveToCollection": "mutation",
508
+ "outcomes.recordReading": "mutation",
509
+ "outcomes.listReadings": "query",
510
+ "outcomes.showOutcome": "query",
511
+ "conflicts.list": "query",
512
+ "conflicts.resolve": "mutation",
513
+ "conflicts.summary": "query",
514
+ "conflicts.snooze": "mutation",
515
+ "conflicts.reverdict": "mutation",
516
+ "direction.list": "query",
517
+ "direction.refresh": "action",
518
+ "direction.defer": "mutation",
519
+ "question.create": "mutation",
520
+ "question.assign": "mutation",
521
+ "question.answer": "mutation",
522
+ "question.forceClose": "mutation",
523
+ "question.list": "query",
524
+ "chain.classifyCollection": "action",
525
+ "chain.classifyStrategyCategory": "query",
526
+ "chain.batchClassifyHeuristic": "query",
527
+ "chain.resolveCollection": "action",
528
+ "chain.searchEntries": "query",
529
+ "chain.searchByCanonicalName": "query",
530
+ "chain.commitEntry": "mutation",
531
+ "chain.verifyEntry": "mutation",
532
+ "chain.confirmEntry": "mutation",
533
+ "chain.tickChecklistItem": "mutation",
534
+ "chain.batchCommitConstellation": "mutation",
535
+ "chain.listEntryHistory": "query",
536
+ "chain.listEntryVersions": "query",
537
+ "chain.createEntryRelation": "mutation",
538
+ "chain.createEntryRelations": "mutation",
539
+ "chain.removeEntryRelation": "mutation",
540
+ "chain.normalizeEntryDataLLM": "action",
541
+ "chain.decomposeContent": "action",
542
+ "chain.normalizeEntryDataPreview": "action",
543
+ "chain.listEntryRelations": "query",
544
+ "chain.evaluateCoherence": "query",
545
+ "chain.validateCommitConstellation": "query",
546
+ "chain.getCaptureContract": "query",
547
+ "chain.gatherContext": "query",
548
+ "chain.getConstellation": "query",
549
+ "agentKnowledge.facilitateEnvelope": "action",
550
+ "agentKnowledge.wrapupEnvelope": "action",
551
+ "agentKnowledge.captureEnvelope": "action",
552
+ "chain.graphGatherContext": "query",
553
+ "chain.assembleBuildContext": "query",
554
+ "chain.qualityCheck": "query",
555
+ "chain.changeDetection": "query",
556
+ "chain.structuralAggregation": "query",
557
+ "chain.detectSemanticConflicts": "action",
558
+ "chain.taskAwareGatherContext": "query",
559
+ "chain.journeyAwareGatherContext": "query",
560
+ "chain.resolveTaskStartup": "query",
561
+ "chain.resolveTaskStartupHybrid": "action",
562
+ "chain.taskAwareHybridGatherContext": "action",
563
+ "chain.gatherFromSeeds": "query",
564
+ "chain.getEntryNeighborhood": "query",
565
+ "chain.deepChainWalk": "action",
566
+ "chain.recordBriefRun": "mutation",
567
+ "chain.getLastBriefRun": "query",
568
+ "chain.incrementalChanges": "query",
569
+ "chain.compoundQuery": "action",
570
+ "chain.recordSessionSignal": "mutation",
571
+ "chain.workspaceReadiness": "query",
572
+ "chain.getCaptureHealth": "query",
573
+ "chain.getGroundingHealth": "query",
574
+ "chain.recordGroundingOutcome": "mutation",
575
+ "chain.capture": "query",
576
+ "chain.runDigest": "action",
577
+ "chain.checkCardinalityWarning": "query",
578
+ "chain.ingestDocument": "action",
579
+ "chain.getOrientEntries": "query",
580
+ "chain.getOrientView": "action",
581
+ "chain.getRitualsSurface": "query",
582
+ "chain.getGovernanceWithRelations": "action",
583
+ "chain.classifyGovernance": "query",
584
+ "chain.getVocabulary": "query",
585
+ "scoreboard.get": "action",
586
+ "rework.report": "mutation",
587
+ "chain.listLabels": "query",
588
+ "chain.createLabel": "mutation",
589
+ "chain.updateLabel": "mutation",
590
+ "chain.deleteLabel": "mutation",
591
+ "chain.applyLabel": "mutation",
592
+ "chain.removeLabel": "mutation",
593
+ "chain.listEntriesByLabel": "query",
594
+ "gaps.record": "mutation",
595
+ "gaps.resolve": "mutation",
596
+ "gaps.top": "query",
597
+ "gaps.stats": "query",
598
+ "agent.startSession": "mutation",
599
+ "agent.resumeSession": "mutation",
600
+ "agent.closeSession": "mutation",
601
+ "agent.markOriented": "mutation",
602
+ "agent.touchSession": "mutation",
603
+ "agent.recordActivity": "mutation",
604
+ "agent.getSession": "query",
605
+ "agent.getActiveSession": "query",
606
+ "agent.activityStats": "query",
607
+ "agent.getActivityByDay": "query",
608
+ "agent.validateSession": "query",
609
+ "agent.getSessionWrapup": "query",
610
+ "agent.recordWrapup": "mutation",
611
+ "agent.reportOrientMetric": "mutation",
612
+ "agent.listSessions": "query",
613
+ "agent.showConversation": "query",
614
+ "usage.getWorkspaceSummary": "query",
615
+ "quality.evaluateHeuristicAndSchedule": "mutation",
616
+ "quality.reEvaluateEntry": "mutation",
617
+ "quality.evaluateAtCapture": "action",
618
+ "quality.evaluateAtCommit": "action",
619
+ "quality.evaluateForReview": "action",
620
+ "quality.getCachedVerdict": "query",
621
+ "quality.getLatestVerdictForEntry": "query",
622
+ "quality.spineCheck": "action",
623
+ "quality.scheduleSpineCheck": "mutation",
624
+ "quality.getLatestSpineVerdict": "query",
625
+ "onboarding.chat": "action",
626
+ "workspace.health": "query",
627
+ "workspace.healthAll": "query",
628
+ "performance.getVitalsSummary": "query",
629
+ "performance.getApiOverview": "query",
630
+ "performance.getRouteBreakdown": "query",
631
+ "performance.getSlowSamples": "query",
632
+ "performance.getSampleCount": "query",
633
+ "chainwork.getWorkflowRun": "query",
634
+ "chainwork.getLatestWorkflowRun": "query",
635
+ "chainwork.recordWorkflowCheckpoint": "mutation",
636
+ "chainwork.finalizeWorkflowRun": "action",
637
+ "chainwork.getLastVerifiedBrief": "query",
638
+ "staging.getCommittedSourceRefs": "query",
639
+ "staging.commitStagingEntryWithClassification": "action",
640
+ "organisation.seatInRole": "mutation",
641
+ "organisation.unseatFromRole": "mutation",
642
+ "apiKeys.revokeCallingKey": "mutation",
643
+ "people.invite": "mutation",
644
+ "people.list": "query"
645
+ };
646
+ var ROUTE_TYPE_BY_NAME = Object.assign(
647
+ /* @__PURE__ */ Object.create(null),
648
+ ROUTE_TYPE_ENTRIES
649
+ );
650
+ function latencyBudgetMsForRouteType(type) {
651
+ return ROUTE_LATENCY_BUDGET_MS[type];
652
+ }
653
+ function latencyBudgetMsForRoute(routeName) {
654
+ if (Object.hasOwn(ROUTE_LATENCY_BUDGET_OVERRIDE_MS, routeName)) {
655
+ return ROUTE_LATENCY_BUDGET_OVERRIDE_MS[routeName];
656
+ }
657
+ const type = ROUTE_TYPE_BY_NAME[routeName];
658
+ return type ? latencyBudgetMsForRouteType(type) : WIDEST_ROUTE_LATENCY_BUDGET_MS;
659
+ }
660
+
661
+ // src/lib/gatewaySeamStore.ts
662
+ var AUDIT_BUFFER_SIZE = 50;
663
+ var auditBufferByWorkspace = /* @__PURE__ */ new Map();
664
+ var nextAuditSeq = 0;
665
+ function getAuditLog(scopes) {
666
+ const merged = [];
667
+ for (const scope of new Set(scopes)) {
668
+ const bucket = auditBufferByWorkspace.get(scope);
669
+ if (bucket) merged.push(...bucket);
670
+ }
671
+ return merged.sort((a, b) => a.seq - b.seq);
672
+ }
673
+ function emptyByRoute() {
674
+ return /* @__PURE__ */ Object.create(null);
675
+ }
676
+ function emptySeamCounters() {
677
+ return { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0, byRoute: emptyByRoute() };
678
+ }
679
+ var MAX_SEAM_WORKSPACES = 200;
680
+ var seamCountersByWorkspace = /* @__PURE__ */ new Map();
681
+ function touchBoundedWorkspaceMap(map, workspace, create) {
682
+ const existing = map.get(workspace);
683
+ if (existing !== void 0) {
684
+ map.delete(workspace);
685
+ map.set(workspace, existing);
686
+ return existing;
687
+ }
688
+ if (map.size >= MAX_SEAM_WORKSPACES) {
689
+ const leastRecentlyRecorded = map.keys().next().value;
690
+ if (leastRecentlyRecorded !== void 0) map.delete(leastRecentlyRecorded);
691
+ }
692
+ const created = create();
693
+ map.set(workspace, created);
694
+ return created;
695
+ }
696
+ function getOrCreateWorkspaceCounters(workspace) {
697
+ return touchBoundedWorkspaceMap(seamCountersByWorkspace, workspace, emptySeamCounters);
698
+ }
699
+ function getOrCreateWorkspaceAuditBuffer(workspace) {
700
+ return touchBoundedWorkspaceMap(auditBufferByWorkspace, workspace, () => []);
701
+ }
702
+ function getGatewaySeamCounters(workspace) {
703
+ const counters = seamCountersByWorkspace.get(workspace);
704
+ if (!counters) return emptySeamCounters();
705
+ return {
706
+ calls: counters.calls,
707
+ errors: counters.errors,
708
+ timeouts: counters.timeouts,
709
+ totalDurationMs: counters.totalDurationMs,
710
+ maxDurationMs: counters.maxDurationMs,
711
+ // Copied onto a null prototype, not left as `Object.fromEntries`' plain object — the
712
+ // snapshot is indexed by route name downstream (`getMergedGatewaySeamCounters`) and would
713
+ // reintroduce the inherited-member collision `emptySeamCounters` documents.
714
+ byRoute: Object.assign(emptyByRoute(), Object.fromEntries(Object.entries(counters.byRoute).map(([k, v]) => [k, { ...v }])))
715
+ };
716
+ }
717
+ function getMergedGatewaySeamCounters(scopes) {
718
+ const merged = emptySeamCounters();
719
+ for (const scope of new Set(scopes)) {
720
+ const part = getGatewaySeamCounters(scope);
721
+ merged.calls += part.calls;
722
+ merged.errors += part.errors;
723
+ merged.timeouts += part.timeouts;
724
+ merged.totalDurationMs += part.totalDurationMs;
725
+ merged.maxDurationMs = Math.max(merged.maxDurationMs, part.maxDurationMs);
726
+ for (const [route, r] of Object.entries(part.byRoute)) {
727
+ const into = merged.byRoute[route] ??= { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0 };
728
+ into.calls += r.calls;
729
+ into.errors += r.errors;
730
+ into.timeouts += r.timeouts;
731
+ into.totalDurationMs += r.totalDurationMs;
732
+ into.maxDurationMs = Math.max(into.maxDurationMs, r.maxDurationMs);
733
+ }
734
+ }
735
+ return merged;
736
+ }
737
+ function appendAuditEntry(entry) {
738
+ const stored = { ...entry, seq: nextAuditSeq++ };
739
+ const bucket = getOrCreateWorkspaceAuditBuffer(stored.workspace);
740
+ bucket.push(stored);
741
+ if (bucket.length > AUDIT_BUFFER_SIZE) bucket.shift();
742
+ }
743
+ function recordSeamCounters(workspace, fn, status, durationMs, timedOut) {
744
+ const seamCounters = getOrCreateWorkspaceCounters(workspace);
745
+ const route = seamCounters.byRoute[fn] ??= {
746
+ calls: 0,
747
+ errors: 0,
748
+ timeouts: 0,
749
+ totalDurationMs: 0,
750
+ maxDurationMs: 0
751
+ };
752
+ seamCounters.calls += 1;
753
+ seamCounters.totalDurationMs += durationMs;
754
+ if (durationMs > seamCounters.maxDurationMs) seamCounters.maxDurationMs = durationMs;
755
+ route.calls += 1;
756
+ route.totalDurationMs += durationMs;
757
+ if (durationMs > route.maxDurationMs) route.maxDurationMs = durationMs;
758
+ if (status === "error") {
759
+ seamCounters.errors += 1;
760
+ route.errors += 1;
761
+ if (timedOut) {
762
+ seamCounters.timeouts += 1;
763
+ route.timeouts += 1;
764
+ }
765
+ }
766
+ }
767
+ function formatGatewaySeamSummary(seam) {
768
+ const lines = [
769
+ "\n\n---\n\n# Gateway seam (process lifetime)\n",
770
+ `Calls: ${seam.calls} \u2014 errors: ${seam.errors}, of which timeouts: ${seam.timeouts}`
771
+ ];
772
+ if (seam.calls > 0) {
773
+ lines.push(`Mean: ${Math.round(seam.totalDurationMs / seam.calls)}ms \u2014 slowest: ${seam.maxDurationMs}ms`);
774
+ }
775
+ if (seam.timeouts > 0) {
776
+ lines.push(`\u26A0 ${seam.timeouts} call(s) hit their latency budget \u2014 those outcomes are unknown, not failed.`);
777
+ }
778
+ return lines.join("\n");
779
+ }
780
+
781
+ // src/gatewaySeam.ts
782
+ var KernelCallError = class extends Error {
783
+ status;
784
+ code;
785
+ /** WP-316 S1a: Structured commit validation — required field keys missing from entry.data. */
786
+ missingRequiredFields;
787
+ /** WP-316 S1a: Structured commit validation — field-level data errors. */
788
+ fieldErrors;
789
+ /**
790
+ * WP-465 slice ⑤: structured diagnostics carried by an `ok:false` kernel envelope
791
+ * (e.g. `coherencyRefusals`, `blockers`). The gateway forwards these verbatim at
792
+ * HTTP 200; without preserving them here a refused/blocked envelope would collapse
793
+ * into a bare code+message and the caller could not surface the per-offender routes.
794
+ */
795
+ diagnostics;
796
+ constructor(message, status, code, missingRequiredFields, fieldErrors, diagnostics) {
797
+ super(message);
798
+ this.name = "KernelCallError";
799
+ this.status = status;
800
+ this.code = code;
801
+ this.missingRequiredFields = missingRequiredFields;
802
+ this.fieldErrors = fieldErrors;
803
+ this.diagnostics = diagnostics;
804
+ }
805
+ };
806
+ var GatewayTimeoutError = class extends Error {
807
+ /** Gateway route name that was aborted. */
808
+ fn;
809
+ /** The declared budget, in ms, that this route was given (see routeLatencyBudget). */
810
+ budgetMs;
811
+ /** Wall-clock ms actually spent before the abort. */
812
+ elapsedMs;
813
+ /**
814
+ * True when the aborted call could have landed a write server-side despite this client
815
+ * giving up. Derived by `mayHaveLandedOnTimeout` from the route contract's own function
816
+ * type (`mutation`/`action` mutate; `query` does not) AND the call's own arguments (a
817
+ * `preview: true` dry run writes nothing) — never guessed from the name.
818
+ */
819
+ mayHaveLanded;
820
+ constructor(fn, budgetMs, elapsedMs, mayHaveLanded) {
821
+ super(
822
+ `MCP call "${fn}" exceeded its ${budgetMs}ms latency budget (waited ${elapsedMs}ms).` + (mayHaveLanded ? " This route writes, so the server may have completed it \u2014 verify before retrying." : "")
823
+ );
824
+ this.name = "GatewayTimeoutError";
825
+ this.fn = fn;
826
+ this.budgetMs = budgetMs;
827
+ this.elapsedMs = elapsedMs;
828
+ this.mayHaveLanded = mayHaveLanded;
829
+ }
830
+ };
831
+ function routeMayMutate(fn) {
832
+ const type = ROUTE_TYPE_BY_NAME[fn];
833
+ return type === void 0 || type !== "query";
834
+ }
835
+ function isDryRunCall(args) {
836
+ return args?.preview === true;
837
+ }
838
+ function mayHaveLandedOnTimeout(fn, args) {
839
+ return routeMayMutate(fn) && !isDryRunCall(args);
840
+ }
841
+ function throwClassifiedGatewayFailure(err, fn, args, budgetMs, elapsedMs, phase, record) {
842
+ if (err?.name === "TimeoutError" || err?.name === "AbortError") {
843
+ const timeoutErr = new GatewayTimeoutError(fn, budgetMs, elapsedMs, mayHaveLandedOnTimeout(fn, args));
844
+ record(timeoutErr.message, true);
845
+ throw timeoutErr;
846
+ }
847
+ const detail = err?.message ?? String(err);
848
+ record(phase === "network" ? detail : `${phase}: ${detail}`, false);
849
+ throw new Error(`MCP call "${fn}" ${phase} error: ${detail}`);
850
+ }
851
+ function emptyBodyOrThrowClassified(err, fn, args, budgetMs, elapsedMs, statusOk, record) {
852
+ if (statusOk) throwClassifiedGatewayFailure(err, fn, args, budgetMs, elapsedMs, "response body", record);
853
+ return {};
854
+ }
855
+ function shouldLogAudit(status) {
856
+ return status === "error" || process.env.MCP_DEBUG === "1";
857
+ }
858
+ function recordGatewayCall(params) {
859
+ const { fn, status, durationMs, workspace, errorMsg, toolContext, budgetMs, timedOut } = params;
860
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
861
+ const entry = { ts, fn, workspace, status, durationMs };
862
+ if (errorMsg) entry.error = errorMsg;
863
+ if (toolContext) entry.toolContext = toolContext;
864
+ if (budgetMs !== void 0) entry.budgetMs = budgetMs;
865
+ if (timedOut) entry.timedOut = true;
866
+ appendAuditEntry(entry);
867
+ recordSeamCounters(workspace, fn, status, durationMs, timedOut === true);
868
+ trackToolCall(fn, status, durationMs, workspace, errorMsg);
869
+ if (!shouldLogAudit(status)) return;
870
+ const base = `[MCP-AUDIT] ${ts} fn=${fn} workspace=${workspace} status=${status} duration=${durationMs}ms${budgetMs !== void 0 ? ` budget=${budgetMs}ms` : ""}${timedOut ? " timedOut=true" : ""}`;
871
+ process.stderr.write(
872
+ status === "error" && errorMsg ? `${base} error=${JSON.stringify(errorMsg)}
873
+ ` : `${base}
874
+ `
875
+ );
876
+ }
877
+
878
+ // src/client.ts
879
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
880
+
881
+ // src/errors.ts
882
+ var MCP_CALL_ERROR_RE = /^MCP call "([^"]+)" failed \(\d+\): (.+)$/s;
883
+ function asKernelCallError(err) {
884
+ if (!(err instanceof Error)) return void 0;
885
+ const e = err;
886
+ if (typeof e.code !== "string") return void 0;
887
+ const match = MCP_CALL_ERROR_RE.exec(e.message);
888
+ if (!match) return void 0;
889
+ return {
890
+ code: e.code,
891
+ fn: match[1],
892
+ cleanMessage: match[2],
893
+ // NOTE: match[2] not match[1] — group 1 is now fn scope (DEC-798)
894
+ missingRequiredFields: Array.isArray(e.missingRequiredFields) ? e.missingRequiredFields : void 0,
895
+ fieldErrors: Array.isArray(e.fieldErrors) ? e.fieldErrors : void 0
896
+ };
897
+ }
898
+ var GateError = class extends Error {
899
+ constructor(message) {
900
+ super(message);
901
+ this.name = "GateError";
902
+ }
903
+ };
904
+ var BackendError = class extends Error {
905
+ constructor(message) {
906
+ super(message);
907
+ this.name = "BackendError";
908
+ }
909
+ };
910
+ var ValidationError = class extends Error {
911
+ constructor(message) {
912
+ super(message);
913
+ this.name = "ValidationError";
914
+ }
915
+ };
916
+ var PASS_SESSION_ID = "Call `session action=start`, then pass the sessionId it returns as `sessionId` on every write: the connection does not remember your session between calls.";
917
+ var GATE_PATTERNS = [
918
+ {
919
+ pattern: /session required|no active.*session|call.*session.*start/i,
920
+ code: "SESSION_REQUIRED",
921
+ recovery: PASS_SESSION_ID,
922
+ action: { tool: "session", description: "Start session", parameters: { action: "start" } }
923
+ },
924
+ {
925
+ pattern: /session.*closed/i,
926
+ code: "SESSION_CLOSED",
927
+ recovery: "Start a new session.",
928
+ action: { tool: "session", description: "Start new session", parameters: { action: "start" } }
929
+ },
930
+ {
931
+ pattern: /orientation required|call.*orient/i,
932
+ code: "ORIENTATION_REQUIRED",
933
+ recovery: "Run orient with a task, then retry.",
934
+ action: { tool: "orient", description: "Orient with a task", parameters: { action: "task" } }
935
+ },
936
+ {
937
+ pattern: /read.?only.*scope/i,
938
+ code: "READONLY_SCOPE",
939
+ recovery: "This API key cannot write. Use a readwrite key.",
940
+ action: { tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }
941
+ }
942
+ ];
943
+ function orientationRefusal(err) {
944
+ const gate = GATE_PATTERNS.find((g) => g.code === "ORIENTATION_REQUIRED");
945
+ const mcpErr = asKernelCallError(err);
946
+ const message = mcpErr?.cleanMessage ?? (err instanceof Error ? err.message : String(err));
947
+ if (!gate || !gate.pattern.test(message)) return null;
948
+ return { code: gate.code, message, recovery: gate.recovery, availableActions: [gate.action] };
949
+ }
950
+ function classifyError(err) {
951
+ const message = err instanceof Error ? err.message : String(err);
952
+ if (err instanceof GateError) {
953
+ return {
954
+ code: "PERMISSION_DENIED",
955
+ message,
956
+ recovery: "Check permissions or use a readwrite API key.",
957
+ availableActions: [{ tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }]
958
+ };
959
+ }
960
+ if (err instanceof BackendError) {
961
+ return { code: "BACKEND_ERROR", message, recovery: "Retry the operation or check backend health." };
962
+ }
963
+ if (err instanceof ValidationError) {
964
+ return { code: "VALIDATION_ERROR", message };
965
+ }
966
+ const mcpErr = asKernelCallError(err);
967
+ if (mcpErr) {
968
+ const { code: convexCode, fn, cleanMessage, missingRequiredFields, fieldErrors } = mcpErr;
969
+ if (convexCode === "VALIDATION_FAILED") {
970
+ const hasDiagnostics = missingRequiredFields?.length || fieldErrors?.length;
971
+ return {
972
+ code: "VALIDATION_ERROR",
973
+ fn,
974
+ message: cleanMessage,
975
+ ...hasDiagnostics ? {
976
+ diagnostics: {
977
+ ...missingRequiredFields?.length ? { missingRequiredFields } : {},
978
+ ...fieldErrors?.length ? { fieldErrors } : {}
979
+ }
980
+ } : {}
981
+ };
982
+ }
983
+ if (convexCode === "NOT_FOUND") {
984
+ return {
985
+ code: "NOT_FOUND",
986
+ fn,
987
+ message: cleanMessage,
988
+ recovery: "Use context action=search to find the correct ID.",
989
+ availableActions: [{ tool: "context", description: "Search entries", parameters: { action: "search" } }]
990
+ };
991
+ }
992
+ if (convexCode?.startsWith("AUTHZ_")) {
993
+ return {
994
+ code: "PERMISSION_DENIED",
995
+ fn,
996
+ message: cleanMessage,
997
+ recovery: "Check permissions or use a readwrite API key.",
998
+ availableActions: [{ tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }]
999
+ };
1000
+ }
1001
+ if (convexCode === "WORKSPACE_SLUG_TAKEN" || convexCode === "PERSON_ALREADY_EXISTS") {
1002
+ return {
1003
+ code: "DUPLICATE",
1004
+ fn,
1005
+ message: cleanMessage,
1006
+ recovery: "Use context action=get to inspect the existing entry.",
1007
+ availableActions: [{ tool: "context", description: "Get entry", parameters: { action: "get" } }]
1008
+ };
1009
+ }
1010
+ if (convexCode === "rate-limited") {
1011
+ return {
1012
+ code: "rate-limited",
1013
+ fn,
1014
+ message: cleanMessage,
1015
+ recovery: "Rate limit hit for this API key. Wait the number of seconds in the message (Retry-After), then retry the same call unchanged \u2014 it is safe to retry."
1016
+ };
1017
+ }
1018
+ return { code: convexCode, fn, message: cleanMessage };
1019
+ }
1020
+ for (const { pattern, code, recovery, action } of GATE_PATTERNS) {
1021
+ if (pattern.test(message)) {
1022
+ return { code, message, recovery, availableActions: [action] };
1023
+ }
1024
+ }
1025
+ if (err?.name === "GatewayTimeoutError") {
1026
+ const mayHaveLanded = err.mayHaveLanded === true;
1027
+ return {
1028
+ code: "GATEWAY_TIMEOUT",
1029
+ message,
1030
+ recovery: mayHaveLanded ? "The server was reachable and may have completed this write \u2014 verify before retrying, since a blind retry can duplicate it." : "The server was reachable but did not answer in time. Nothing was written \u2014 safe to retry."
1031
+ };
1032
+ }
1033
+ if (/network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(message)) {
1034
+ return { code: "BACKEND_UNAVAILABLE", message, recovery: "Retry in a few seconds." };
1035
+ }
1036
+ return { code: "INTERNAL_ERROR", message: message || "An unexpected error occurred." };
1037
+ }
1038
+ function retryIsFutile(code) {
1039
+ return code === "VALIDATION_ERROR";
1040
+ }
1041
+
1042
+ // src/lib/conversation.ts
1043
+ import { createHash as createHash2 } from "crypto";
1044
+ var MAX_RAW_LEN = 128;
1045
+ var SAFE_CHARS_RE = /^[A-Za-z0-9._-]+$/;
1046
+ function normalizeConversationId(raw) {
1047
+ if (raw.length > MAX_RAW_LEN || !SAFE_CHARS_RE.test(raw)) {
1048
+ return createHash2("sha256").update(raw).digest("hex");
1049
+ }
1050
+ return raw;
1051
+ }
1052
+ function pickRawConversationId() {
1053
+ const candidates = [
1054
+ process.env.PB_CONVERSATION_ID,
1055
+ process.env.CLAUDE_CODE_SESSION_ID,
1056
+ process.env.CURSOR_CONVERSATION_ID,
1057
+ process.env.CODEX_TUI_SESSION_LOG_PATH
1058
+ ];
1059
+ for (const candidate of candidates) {
1060
+ if (candidate && candidate.trim().length > 0) return candidate.trim();
1061
+ }
1062
+ return null;
1063
+ }
1064
+ function resolveConversationId() {
1065
+ const raw = pickRawConversationId();
1066
+ if (!raw) return null;
1067
+ return normalizeConversationId(raw);
1068
+ }
1069
+
1070
+ // src/lib/toolActionCounts.ts
1071
+ var toolActionCounts = /* @__PURE__ */ new Map();
1072
+ function actionCountKey(tool, action) {
1073
+ return action ? `${tool}:${action}` : tool;
1074
+ }
1075
+ function recordToolAction(tool, action) {
1076
+ const key = actionCountKey(tool, action);
1077
+ toolActionCounts.set(key, (toolActionCounts.get(key) ?? 0) + 1);
1078
+ }
1079
+
1080
+ // src/lib/deploymentUrlResolver.ts
1081
+ function parseFallbackUrls(raw) {
1082
+ if (!raw) return [];
1083
+ return raw.split(",").map((u) => u.trim()).filter(Boolean);
1084
+ }
1085
+ async function checkKeyAgainstCandidates(candidates, apiKey, timeoutMs) {
1086
+ let rejections = 0;
1087
+ for (const url of candidates) {
1088
+ let data;
1089
+ try {
1090
+ const res = await fetch(`${url}/api/key-check`, {
1091
+ method: "POST",
1092
+ headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
1093
+ signal: AbortSignal.timeout(timeoutMs)
1094
+ });
1095
+ if (!res.ok) continue;
1096
+ data = await res.json();
1097
+ } catch {
1098
+ continue;
1099
+ }
1100
+ if (data?.ok === true) {
1101
+ return { verdict: "ok", url, deploymentUrl: data.deploymentUrl ?? url, workspaceName: data.workspaceName };
1102
+ }
1103
+ if (data?.ok === false && data.error === "rate_limited") {
1104
+ return { verdict: "rate_limited", url, deploymentUrl: url, retryAfterSeconds: data.retryAfterSeconds };
1105
+ }
1106
+ if (data?.ok === false && data.error === "invalid_key") rejections++;
1107
+ }
1108
+ return candidates.length > 0 && rejections === candidates.length ? { verdict: "rejected" } : { verdict: "unreachable" };
1109
+ }
1110
+ async function probeDeploymentCandidates(candidates, apiKey) {
1111
+ const result = await checkKeyAgainstCandidates(candidates, apiKey, 3e3);
1112
+ return result.verdict === "ok" || result.verdict === "rate_limited" ? result.url : null;
1113
+ }
1114
+
1115
+ // src/client.ts
1116
+ var _conversationId;
1117
+ function getConversationId() {
1118
+ const mcpSid = getRequestMcpSessionId();
1119
+ if (mcpSid) return sessionLifecycle().conversationId ?? `mcp:${mcpSid}`;
1120
+ if (getRequestApiKey()) return null;
1121
+ if (_conversationId === void 0) _conversationId = resolveConversationId();
1122
+ return _conversationId;
1123
+ }
1124
+ var toolContextStore = new AsyncLocalStorage2();
1125
+ function runWithToolContext(ctx, fn) {
1126
+ recordToolAction(ctx.tool, ctx.action);
1127
+ trackCompoundToolAction(ctx.tool, ctx.action, state().workspaceId ?? "unresolved");
1128
+ return toolContextStore.run(ctx, fn);
1129
+ }
1130
+ function getToolContext() {
1131
+ return toolContextStore.getStore() ?? null;
1132
+ }
1133
+ var DEFAULT_CLOUD_URL = "https://gateway.productbrain.io";
1134
+ var CACHE_TTL_MS = 6e4;
1135
+ var CACHEABLE_FNS = [
1136
+ "chain.gatherContext",
1137
+ "chain.graphGatherContext",
1138
+ "chain.journeyAwareGatherContext",
1139
+ "chain.assembleBuildContext"
1140
+ ];
1141
+ function isCacheable(fn) {
1142
+ return CACHEABLE_FNS.includes(fn);
1143
+ }
1144
+ function invalidatesReadCache(fn) {
1145
+ return routeMayMutate(fn) && !TOUCH_EXCLUDED.has(fn);
1146
+ }
1147
+ var readCache = /* @__PURE__ */ new Map();
1148
+ function cacheKey(fn, args) {
1149
+ return `${cacheScope()}:${fn}:${JSON.stringify(args)}`;
1150
+ }
1151
+ function getCached(fn, args) {
1152
+ if (!isCacheable(fn)) return void 0;
1153
+ const key = cacheKey(fn, args);
1154
+ const entry = readCache.get(key);
1155
+ if (!entry || Date.now() > entry.expiresAt) {
1156
+ if (entry) readCache.delete(key);
1157
+ return void 0;
1158
+ }
1159
+ return entry.data;
1160
+ }
1161
+ function setCached(fn, args, data) {
1162
+ if (!isCacheable(fn)) return;
1163
+ const key = cacheKey(fn, args);
1164
+ readCache.set(key, { data, expiresAt: Date.now() + CACHE_TTL_MS });
1165
+ }
1166
+ function invalidateReadCache() {
1167
+ readCache.clear();
1168
+ }
1169
+ var _stdioState = {
1170
+ workspaceId: null,
1171
+ workspaceSlug: null,
1172
+ workspaceName: null,
1173
+ workspaceCreatedAt: null,
1174
+ agentSessionId: null,
1175
+ apiKeyId: null,
1176
+ apiKeyScope: "readwrite",
1177
+ sessionOriented: false,
1178
+ sessionClosed: false,
1179
+ lastAccess: 0,
1180
+ deploymentUrl: null
1181
+ };
1182
+ function state() {
1183
+ const reqKey = getRequestApiKey();
1184
+ if (reqKey) return getKeyState(reqKey);
1185
+ return _stdioState;
1186
+ }
1187
+ function cacheScope() {
1188
+ const key = getRequestApiKey();
1189
+ return key ? hashKey(key) : "stdio";
1190
+ }
1191
+ function getActiveApiKey() {
1192
+ const fromRequest = getRequestApiKey();
1193
+ if (fromRequest) return fromRequest;
1194
+ const fromEnv = process.env.PRODUCTBRAIN_API_KEY;
1195
+ if (!fromEnv) throw new Error("No API key available \u2014 set PRODUCTBRAIN_API_KEY or provide Bearer token");
1196
+ return fromEnv;
1197
+ }
1198
+ var _sessionLifecycleByStream = /* @__PURE__ */ new Map();
1199
+ var MAX_SESSION_STREAMS = 500;
1200
+ function sessionLifecycle() {
1201
+ const seated = seatedSessionStore.getStore();
1202
+ if (seated) return seated;
1203
+ const mcpSid = getRequestMcpSessionId();
1204
+ if (!mcpSid) return state();
1205
+ const key = `${cacheScope()}:${mcpSid}`;
1206
+ let lc = _sessionLifecycleByStream.get(key);
1207
+ if (!lc) {
1208
+ if (_sessionLifecycleByStream.size >= MAX_SESSION_STREAMS) {
1209
+ const oldest = _sessionLifecycleByStream.keys().next().value;
1210
+ if (oldest !== void 0) _sessionLifecycleByStream.delete(oldest);
1211
+ }
1212
+ lc = { agentSessionId: null, sessionOriented: false, sessionClosed: false };
1213
+ _sessionLifecycleByStream.set(key, lc);
1214
+ }
1215
+ return lc;
1216
+ }
1217
+ function getAgentSessionId() {
1218
+ return sessionLifecycle().agentSessionId;
1219
+ }
1220
+ function isSessionOriented() {
1221
+ return sessionLifecycle().sessionOriented;
1222
+ }
1223
+ function setSessionOriented(value) {
1224
+ sessionLifecycle().sessionOriented = value;
1225
+ }
1226
+ function getApiKeyScope() {
1227
+ return state().apiKeyScope;
1228
+ }
1229
+ var seatedSessionStore = new AsyncLocalStorage2();
1230
+ var getApiKeyId = () => state().apiKeyId;
1231
+ var runWithSeatedSession = (lc, fn) => seatedSessionStore.run(lc, fn);
1232
+ var forgetStreamLifecycle = (keyHash, mcpSessionId) => _sessionLifecycleByStream.delete(`${keyHash}:${mcpSessionId}`);
1233
+ async function startAgentSession(role) {
1234
+ const workspaceId = await getWorkspaceId();
1235
+ const s = state();
1236
+ if (!s.apiKeyId) {
1237
+ throw new Error("Cannot start session: API key ID not resolved. Ensure workspace resolution completed.");
1238
+ }
1239
+ const result = await kernelCall("agent.startSession", {
1240
+ workspaceId,
1241
+ apiKeyId: s.apiKeyId,
1242
+ clientKind: "mcp",
1243
+ // WP-479 E2: resolved once at process startup and held — see getConversationId() above.
1244
+ conversationId: getConversationId() ?? void 0,
1245
+ role
1246
+ });
1247
+ const lc = sessionLifecycle();
1248
+ if (lc.agentSessionId) resetTouchThrottle(lc.agentSessionId);
1249
+ lc.agentSessionId = result.sessionId;
1250
+ s.apiKeyScope = result.toolsScope;
1251
+ lc.sessionOriented = false;
1252
+ lc.sessionClosed = false;
1253
+ resetTouchThrottle(result.sessionId);
1254
+ return result;
1255
+ }
1256
+ async function closeAgentSession() {
1257
+ const lc = sessionLifecycle();
1258
+ if (!lc.agentSessionId) return;
1259
+ const sessionId = lc.agentSessionId;
1260
+ try {
1261
+ await kernelCall("agent.closeSession", {
1262
+ sessionId,
1263
+ status: "closed"
1264
+ });
1265
+ } finally {
1266
+ resetTouchThrottle(sessionId);
1267
+ lc.sessionClosed = true;
1268
+ lc.agentSessionId = null;
1269
+ lc.sessionOriented = false;
1270
+ }
1271
+ }
1272
+ async function orphanAgentSession() {
1273
+ const lc = sessionLifecycle();
1274
+ if (!lc.agentSessionId) return;
1275
+ const sessionId = lc.agentSessionId;
1276
+ try {
1277
+ await kernelCall("agent.closeSession", {
1278
+ sessionId,
1279
+ status: "orphaned"
1280
+ });
1281
+ } catch {
1282
+ } finally {
1283
+ resetTouchThrottle(sessionId);
1284
+ lc.agentSessionId = null;
1285
+ lc.sessionOriented = false;
1286
+ }
1287
+ }
1288
+ var _lastTouchAtBySession = /* @__PURE__ */ new Map();
1289
+ var TOUCH_THROTTLE_MS = 5e3;
1290
+ function touchSessionActivity() {
1291
+ const sessionId = sessionLifecycle().agentSessionId;
1292
+ if (!sessionId) return;
1293
+ const now = Date.now();
1294
+ const lastTouchAt = _lastTouchAtBySession.get(sessionId) ?? 0;
1295
+ if (now - lastTouchAt < TOUCH_THROTTLE_MS) return;
1296
+ _lastTouchAtBySession.set(sessionId, now);
1297
+ kernelCall("agent.touchSession", {
1298
+ sessionId
1299
+ }).catch(() => {
1300
+ });
1301
+ }
1302
+ function resetTouchThrottle(sessionId) {
1303
+ if (sessionId) {
1304
+ _lastTouchAtBySession.delete(sessionId);
1305
+ return;
1306
+ }
1307
+ _lastTouchAtBySession.clear();
1308
+ }
1309
+ async function recordSessionActivity(activity) {
1310
+ const sessionId = sessionLifecycle().agentSessionId;
1311
+ if (!sessionId) return;
1312
+ try {
1313
+ await kernelCall("agent.recordActivity", {
1314
+ sessionId,
1315
+ ...activity
1316
+ });
1317
+ } catch {
1318
+ }
1319
+ }
1320
+ function bootstrap() {
1321
+ const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;
1322
+ process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;
1323
+ warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });
1324
+ const pbKey = process.env.PRODUCTBRAIN_API_KEY;
1325
+ if (!pbKey?.startsWith("pb_sk_")) {
1326
+ process.stderr.write(
1327
+ "[MCP] Warning: PRODUCTBRAIN_API_KEY is not set or invalid. Tool calls will fail until a valid key is provided.\n"
1328
+ );
1329
+ }
1330
+ }
1331
+ function bootstrapHttp() {
1332
+ const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;
1333
+ process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;
1334
+ warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });
1335
+ }
1336
+ async function resolveDeploymentUrl() {
1337
+ const s = state();
1338
+ if (s.deploymentUrl) return s.deploymentUrl;
1339
+ const primaryUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_CLOUD_URL).replace(/\/$/, "");
1340
+ const fallbacks = parseFallbackUrls(process.env.CONVEX_FALLBACK_URLS);
1341
+ if (fallbacks.length === 0) {
1342
+ return primaryUrl;
1343
+ }
1344
+ const candidates = [primaryUrl, ...fallbacks.map((u) => u.replace(/\/$/, ""))];
1345
+ let apiKey;
1346
+ try {
1347
+ apiKey = getActiveApiKey();
1348
+ } catch {
1349
+ return primaryUrl;
1350
+ }
1351
+ const found = await probeDeploymentCandidates(candidates, apiKey);
1352
+ if (found) {
1353
+ s.deploymentUrl = found;
1354
+ return found;
1355
+ }
1356
+ return candidates[0];
1357
+ }
1358
+ function audit(fn, status, durationMs, errorMsg, meta) {
1359
+ recordGatewayCall({
1360
+ fn,
1361
+ status,
1362
+ durationMs,
1363
+ errorMsg,
1364
+ workspace: state().workspaceId ?? cacheScope(),
1365
+ toolContext: getToolContext(),
1366
+ budgetMs: meta?.budgetMs,
1367
+ timedOut: meta?.timedOut
1368
+ });
1369
+ }
1370
+ var TOUCH_EXCLUDED = /* @__PURE__ */ new Set([
1371
+ "agent.touchSession",
1372
+ "agent.startSession",
1373
+ "agent.markOriented",
1374
+ "agent.recordActivity",
1375
+ "agent.recordWrapup",
1376
+ "agent.closeSession",
1377
+ // WP-376 α.3: orient byte report is itself a heartbeat-equivalent observability
1378
+ // write that already patches the same agentSessions row. A follow-up touchSession
1379
+ // would create a redundant second write per orient call (DEC-50 OCC anti-pattern).
1380
+ "agent.reportOrientMetric"
1381
+ ]);
1382
+ var MCP_TELEMETRY_SOURCE = "mcp";
1383
+ async function callGateway(fn, args) {
1384
+ const siteUrl = await resolveDeploymentUrl();
1385
+ const apiKey = getActiveApiKey();
1386
+ const budgetMs = latencyBudgetMsForRoute(fn);
1387
+ const start = Date.now();
1388
+ let res;
1389
+ try {
1390
+ res = await fetch(`${siteUrl}/api/aki`, {
1391
+ method: "POST",
1392
+ signal: AbortSignal.timeout(budgetMs),
1393
+ headers: {
1394
+ "Content-Type": "application/json",
1395
+ Authorization: `Bearer ${apiKey}`,
1396
+ // DEC-1207: attribute this connector for the gateway's context.served seam. MCP is
1397
+ // the dominant agent surface (INS-1706); without this header it was emitting nothing.
1398
+ "x-pb-source": MCP_TELEMETRY_SOURCE
1399
+ },
1400
+ body: JSON.stringify({ fn, args })
1401
+ });
1402
+ } catch (err) {
1403
+ throwClassifiedGatewayFailure(
1404
+ err,
1405
+ fn,
1406
+ args,
1407
+ budgetMs,
1408
+ Date.now() - start,
1409
+ "network",
1410
+ (m, t) => audit(fn, "error", Date.now() - start, m, { budgetMs, timedOut: t })
1411
+ );
1412
+ }
1413
+ let json;
1414
+ try {
1415
+ json = await res.json();
1416
+ } catch (err) {
1417
+ json = emptyBodyOrThrowClassified(
1418
+ err,
1419
+ fn,
1420
+ args,
1421
+ budgetMs,
1422
+ Date.now() - start,
1423
+ res.ok,
1424
+ (m, t) => audit(fn, "error", Date.now() - start, m, { budgetMs, timedOut: t })
1425
+ );
1426
+ }
1427
+ if (!res.ok || json.ok === false) {
1428
+ const errJson = json;
1429
+ const msg = errJson.error ?? errJson.message ?? "unknown error";
1430
+ audit(fn, "error", Date.now() - start, errJson.code ? `${msg} [${errJson.code}]` : msg, { budgetMs });
1431
+ throw new KernelCallError(
1432
+ `MCP call "${fn}" failed (${res.status}): ${msg}`,
1433
+ res.status,
1434
+ errJson.code,
1435
+ Array.isArray(errJson.missingRequiredFields) ? errJson.missingRequiredFields : void 0,
1436
+ Array.isArray(errJson.fieldErrors) ? errJson.fieldErrors : void 0,
1437
+ errJson.diagnostics && typeof errJson.diagnostics === "object" ? errJson.diagnostics : void 0
1438
+ );
1439
+ }
1440
+ audit(fn, "ok", Date.now() - start, void 0, { budgetMs });
1441
+ const { data, summary, next, _meta } = json;
1442
+ return {
1443
+ data,
1444
+ summary: summary || fn,
1445
+ next,
1446
+ _meta
1447
+ };
1448
+ }
1449
+ async function kernelCall(fn, args = {}) {
1450
+ const cached = getCached(fn, args);
1451
+ if (cached !== void 0) {
1452
+ return cached;
1453
+ }
1454
+ const { data } = await callGateway(fn, args);
1455
+ if (invalidatesReadCache(fn)) {
1456
+ invalidateReadCache();
1457
+ } else {
1458
+ setCached(fn, args, data);
1459
+ }
1460
+ if (getAgentSessionId() && !TOUCH_EXCLUDED.has(fn)) {
1461
+ touchSessionActivity();
1462
+ }
1463
+ return data;
1464
+ }
1465
+ async function kernelCallEnvelope(fn, args = {}) {
1466
+ const { data, summary, next, _meta } = await callGateway(fn, args);
1467
+ if (getAgentSessionId() && !TOUCH_EXCLUDED.has(fn)) {
1468
+ touchSessionActivity();
1469
+ }
1470
+ return { ok: true, summary, data, next, _meta };
1471
+ }
1472
+ var resolveInFlightMap = /* @__PURE__ */ new Map();
1473
+ async function getWorkspaceId() {
1474
+ const s = state();
1475
+ if (s.workspaceId) return s.workspaceId;
1476
+ const apiKey = getActiveApiKey();
1477
+ const existing = resolveInFlightMap.get(apiKey);
1478
+ if (existing) return existing;
1479
+ const promise = resolveWorkspaceWithRetry().finally(() => resolveInFlightMap.delete(apiKey));
1480
+ resolveInFlightMap.set(apiKey, promise);
1481
+ return promise;
1482
+ }
1483
+ async function resolveWorkspaceWithRetry(maxRetries = 2) {
1484
+ let lastError = null;
1485
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1486
+ try {
1487
+ const workspace = await kernelCall("resolveWorkspace", {});
1488
+ if (!workspace) {
1489
+ throw new Error(
1490
+ `API key is valid but no workspace is associated. Run \`npx ${MCP_NPX_PACKAGE} setup\` or regenerate your key.`
1491
+ );
1492
+ }
1493
+ const s = state();
1494
+ s.workspaceId = workspace._id;
1495
+ s.workspaceSlug = workspace.slug;
1496
+ s.workspaceName = workspace.name;
1497
+ s.workspaceCreatedAt = workspace.createdAt ?? null;
1498
+ if (workspace.keyScope) s.apiKeyScope = workspace.keyScope;
1499
+ if (workspace.keyId) s.apiKeyId = workspace.keyId;
1500
+ return s.workspaceId;
1501
+ } catch (err) {
1502
+ lastError = err;
1503
+ const isTransient = err?.name === "GatewayTimeoutError" || /network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(err.message);
1504
+ if (!isTransient || attempt === maxRetries) break;
1505
+ const delay = 1e3 * (attempt + 1);
1506
+ process.stderr.write(
1507
+ `[MCP] Workspace resolution failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms...
1508
+ `
1509
+ );
1510
+ await new Promise((r) => setTimeout(r, delay));
1511
+ }
1512
+ }
1513
+ throw lastError;
1514
+ }
1515
+ async function getWorkspaceContext() {
1516
+ const workspaceId = await getWorkspaceId();
1517
+ const s = state();
1518
+ return {
1519
+ workspaceId,
1520
+ workspaceSlug: s.workspaceSlug ?? "unknown",
1521
+ workspaceName: s.workspaceName ?? "unknown",
1522
+ createdAt: s.workspaceCreatedAt
1523
+ };
1524
+ }
1525
+ async function kernelQuery(fn, args = {}) {
1526
+ const workspaceId = await getWorkspaceId();
1527
+ return kernelCall(fn, { ...args, workspaceId });
1528
+ }
1529
+ async function kernelMutation(fn, args = {}) {
1530
+ const workspaceId = await getWorkspaceId();
1531
+ return kernelCall(fn, { ...args, workspaceId });
1532
+ }
1533
+ function requireActiveSession() {
1534
+ const lc = sessionLifecycle();
1535
+ if (!lc.agentSessionId) {
1536
+ throw new Error(
1537
+ "Active session required (SOS-iszqu7). Call `session action=start` then `orient` first."
1538
+ );
1539
+ }
1540
+ if (lc.sessionClosed) {
1541
+ throw new Error(
1542
+ "Session has been closed (SOS-iszqu7). Start a new session with `session action=start`."
1543
+ );
1544
+ }
1545
+ }
1546
+ function requireWriteAccess() {
1547
+ const lc = sessionLifecycle();
1548
+ const s = state();
1549
+ if (!lc.agentSessionId) {
1550
+ throw new Error(`Agent session required for write operations. ${PASS_SESSION_ID}`);
1551
+ }
1552
+ if (lc.sessionClosed) {
1553
+ throw new Error(
1554
+ "Agent session has been closed. Write tools are no longer available."
1555
+ );
1556
+ }
1557
+ if (s.apiKeyScope === "read") {
1558
+ throw new Error(
1559
+ "This API key has read-only scope. Write tools are not available."
1560
+ );
1561
+ }
1562
+ }
1563
+ function requireVendorTriageAccess() {
1564
+ const lc = sessionLifecycle();
1565
+ const s = state();
1566
+ if (!lc.agentSessionId) {
1567
+ throw new Error(
1568
+ "Agent session required for feedback triage. Call `session action=start` first."
1569
+ );
1570
+ }
1571
+ if (lc.sessionClosed) {
1572
+ throw new Error(
1573
+ "Agent session has been closed. Feedback triage is no longer available."
1574
+ );
1575
+ }
1576
+ if (s.apiKeyScope === "read") {
1577
+ throw new Error(
1578
+ "This API key has read-only scope. Feedback triage is not available."
1579
+ );
1580
+ }
1581
+ }
1582
+ async function recoverSessionState() {
1583
+ const s = state();
1584
+ if (!s.workspaceId) return;
1585
+ try {
1586
+ const session = await kernelCall("agent.getActiveSession", {
1587
+ workspaceId: s.workspaceId,
1588
+ // WP-479 E2: required disambiguation arg for the per-apiKey-quota re-scoped
1589
+ // getActiveSession — absent identity (null) still resolves to the keyless/legacy path
1590
+ // server-side.
1591
+ conversationId: getConversationId() ?? void 0
1592
+ });
1593
+ if (session && session.status === "active") {
1594
+ const lc = sessionLifecycle();
1595
+ lc.agentSessionId = session._id;
1596
+ lc.sessionOriented = session.oriented;
1597
+ s.apiKeyScope = session.toolsScope;
1598
+ lc.sessionClosed = false;
1599
+ }
1600
+ } catch {
1601
+ }
1602
+ }
1603
+
1604
+ // src/prod-fallthrough.ts
1605
+ function warnOnProdFallthrough(resolved, opts) {
1606
+ if (opts.explicit) return;
1607
+ if (resolved.replace(/\/$/, "") !== DEFAULT_CLOUD_URL.replace(/\/$/, "")) return;
1608
+ process.stderr.write(
1609
+ `[MCP] No deployment URL configured \u2014 defaulting to the production gateway ${DEFAULT_CLOUD_URL}. Set CONVEX_SITE_URL or PRODUCTBRAIN_URL to target a different deployment.
1610
+ `
1611
+ );
1612
+ }
1613
+
1614
+ // src/lib/appUrl.ts
1615
+ function appUrl() {
1616
+ return (process.env.PRODUCTBRAIN_APP_URL ?? "https://work.productbrain.io").replace(/\/$/, "");
1617
+ }
1618
+ function apiKeysUrl() {
1619
+ return `${appUrl()}/settings/api-keys`;
1620
+ }
1621
+
1622
+ export {
1623
+ initAnalytics,
1624
+ trackSessionStarted,
1625
+ trackSetupStarted,
1626
+ trackSetupCompleted,
1627
+ trackQualityVerdict,
1628
+ trackQualityCheck,
1629
+ trackCaptureClassifierEvaluated,
1630
+ trackCaptureClassifierAutoRouted,
1631
+ trackCaptureClassifierFallback,
1632
+ trackChainEntryCommitted,
1633
+ trackKnowledgeGap,
1634
+ trackCaptureQualityHints,
1635
+ trackCollectionClassified,
1636
+ trackSessionCaptureRate,
1637
+ trackZeroCaptureAuditFired,
1638
+ trackCaptureContractMiss,
1639
+ trackCommitErrorByCode,
1640
+ trackClassifierDivergence,
1641
+ getPostHogClient,
1642
+ shutdownAnalytics,
1643
+ getAuditLog,
1644
+ getMergedGatewaySeamCounters,
1645
+ formatGatewaySeamSummary,
1646
+ KernelCallError,
1647
+ hashKey,
1648
+ runWithAuth,
1649
+ getKeyState,
1650
+ MCP_NPX_PACKAGE,
1651
+ resolveClient,
1652
+ writeClientConfig,
1653
+ PASS_SESSION_ID,
1654
+ orientationRefusal,
1655
+ classifyError,
1656
+ retryIsFutile,
1657
+ warnOnProdFallthrough,
1658
+ parseFallbackUrls,
1659
+ checkKeyAgainstCandidates,
1660
+ getConversationId,
1661
+ runWithToolContext,
1662
+ DEFAULT_CLOUD_URL,
1663
+ cacheScope,
1664
+ getAgentSessionId,
1665
+ isSessionOriented,
1666
+ setSessionOriented,
1667
+ getApiKeyScope,
1668
+ getApiKeyId,
1669
+ runWithSeatedSession,
1670
+ forgetStreamLifecycle,
1671
+ startAgentSession,
1672
+ closeAgentSession,
1673
+ orphanAgentSession,
1674
+ recordSessionActivity,
1675
+ bootstrap,
1676
+ bootstrapHttp,
1677
+ kernelCall,
1678
+ kernelCallEnvelope,
1679
+ getWorkspaceId,
1680
+ getWorkspaceContext,
1681
+ kernelQuery,
1682
+ kernelMutation,
1683
+ requireActiveSession,
1684
+ requireWriteAccess,
1685
+ requireVendorTriageAccess,
1686
+ recoverSessionState,
1687
+ appUrl,
1688
+ apiKeysUrl
1689
+ };
1690
+ //# sourceMappingURL=chunk-XEKHBSS7.js.map