@opengeni/api-router 2.10.0-canary.6 → 2.11.0-canary.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/dist/app.js +1 -1
- package/dist/{chunk-NWSVDE7O.js → chunk-EAYCECQK.js} +30234 -29169
- package/dist/chunk-EAYCECQK.js.map +1 -0
- package/dist/codemode.d.ts +13 -0
- package/dist/index.js +1 -1
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/interaction-agent-access.d.ts +19 -0
- package/dist/mcp/documents.d.ts +8 -1
- package/dist/mcp/first-party-tool-permissions.d.ts +572 -0
- package/dist/mcp/server.d.ts +12 -1
- package/dist/mcp/session-view.d.ts +2 -0
- package/dist/routes/api-keys.d.ts +16 -1
- package/dist/routes/feedback.d.ts +3 -0
- package/dist/routes/model-connection-access.d.ts +3 -0
- package/dist/routes/organization-sessions.d.ts +26 -0
- package/dist/routes/supergrok.d.ts +10 -1
- package/dist/site-session-origin.d.ts +6 -0
- package/dist/workspace-tool-gateway.d.ts +2 -1
- package/package.json +19 -19
- package/src/app.ts +39 -6
- package/src/codemode.ts +42 -6
- package/src/integrations/slack-interactions.ts +24 -13
- package/src/interaction-agent-access.ts +60 -0
- package/src/mcp/documents.ts +22 -1
- package/src/mcp/first-party-tool-permissions.ts +278 -0
- package/src/mcp/server.ts +240 -263
- package/src/routes/api-keys.ts +48 -4
- package/src/routes/browser-sessions.ts +9 -6
- package/src/routes/computer-sessions.ts +9 -6
- package/src/routes/documents.ts +12 -0
- package/src/routes/feedback.ts +113 -0
- package/src/routes/files.ts +37 -2
- package/src/routes/machines.ts +32 -1
- package/src/routes/model-connection-access.ts +161 -0
- package/src/routes/organization-sessions.ts +342 -0
- package/src/routes/sessions.ts +51 -2
- package/src/routes/supergrok.ts +183 -5
- package/src/routes/workspaces.ts +15 -4
- package/src/site-session-origin.ts +28 -0
- package/src/workspace-tool-gateway.ts +30 -2
- package/dist/chunk-NWSVDE7O.js.map +0 -1
package/src/mcp/server.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createChannel,
|
|
3
|
+
listChannels,
|
|
4
|
+
getChannel,
|
|
5
|
+
updateChannel,
|
|
6
|
+
reorderChannels,
|
|
7
|
+
deleteChannel,
|
|
8
|
+
setSessionChannel,
|
|
9
|
+
} from "@opengeni/db";
|
|
1
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
11
|
import {
|
|
3
12
|
prepareWorkspaceArtifactUpload,
|
|
@@ -110,6 +119,8 @@ import {
|
|
|
110
119
|
requireScheduledTask,
|
|
111
120
|
requireSession,
|
|
112
121
|
searchWorkspaceMemories,
|
|
122
|
+
memoryWriteScopeForAgentScope,
|
|
123
|
+
type MemoryAgentScope,
|
|
113
124
|
serializeEffectiveSessionControl,
|
|
114
125
|
setSessionGoalStatusWithEvent,
|
|
115
126
|
recordSessionGoalProgressWithEvent,
|
|
@@ -156,6 +167,10 @@ import type { AnySchema, ZodRawShapeCompat } from "@modelcontextprotocol/sdk/ser
|
|
|
156
167
|
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
157
168
|
import { HTTPException } from "hono/http-exception";
|
|
158
169
|
import * as z4 from "zod/v4";
|
|
170
|
+
import {
|
|
171
|
+
FIRST_PARTY_TOOL_AUTHORIZATION,
|
|
172
|
+
type FirstPartyToolAuthorization,
|
|
173
|
+
} from "./first-party-tool-permissions";
|
|
159
174
|
import {
|
|
160
175
|
hasLiteralPermission,
|
|
161
176
|
hasPermission,
|
|
@@ -313,6 +328,13 @@ export type McpServerOptions = {
|
|
|
313
328
|
requestOrigin?: string | null;
|
|
314
329
|
workspaceMemoryEnabled?: boolean | undefined;
|
|
315
330
|
workspaceMemoryPromptMode?: WorkspaceMemoryPromptMode | undefined;
|
|
331
|
+
/**
|
|
332
|
+
* The bound session's typed Memory selector (migration 0427), resolved by
|
|
333
|
+
* the route from the session row. `off` registers no Memory tools; `user`
|
|
334
|
+
* and `session` read the workspace layer plus their own private layer and
|
|
335
|
+
* save into that private layer. Omitted/null keeps the workspace layer.
|
|
336
|
+
*/
|
|
337
|
+
sessionMemory?: MemoryAgentScope | null | undefined;
|
|
316
338
|
};
|
|
317
339
|
|
|
318
340
|
const ORCHESTRATION_FAILURE_CODE_MAX_LENGTH = 128;
|
|
@@ -437,253 +459,6 @@ function orchestrationFailureResult(tool: OrchestrationToolName, error: unknown)
|
|
|
437
459
|
};
|
|
438
460
|
}
|
|
439
461
|
|
|
440
|
-
type FirstPartyToolAuthorization = {
|
|
441
|
-
sessionRequired?: true;
|
|
442
|
-
allOf?: readonly Permission[];
|
|
443
|
-
anyOf?: readonly Permission[];
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Authorization is deliberately complete and separate from visibility. The
|
|
448
|
-
* `satisfies Record` check makes every catalog addition choose an explicit
|
|
449
|
-
* registration predicate before it can compile.
|
|
450
|
-
*/
|
|
451
|
-
const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
452
|
-
set_session_title: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
453
|
-
goal_set: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
454
|
-
goal_update: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
455
|
-
goal_progress: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
456
|
-
wait_for_input: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
457
|
-
goal_complete: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
458
|
-
goal_pause: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
459
|
-
goal_resume: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
460
|
-
memory_search: { sessionRequired: true, allOf: ["documents:search"] },
|
|
461
|
-
memory_save: { sessionRequired: true, allOf: ["documents:search"] },
|
|
462
|
-
memory_correct: { sessionRequired: true, allOf: ["documents:search"] },
|
|
463
|
-
preference_registry_summary: {
|
|
464
|
-
sessionRequired: true,
|
|
465
|
-
allOf: ["workspace:read"],
|
|
466
|
-
},
|
|
467
|
-
preference_registry_get: { sessionRequired: true, allOf: ["workspace:read"] },
|
|
468
|
-
task_notes_list: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
469
|
-
task_note_save: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
470
|
-
task_note_archive: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
471
|
-
task_note_replace: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
472
|
-
work_claim_upsert: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
473
|
-
work_claim_release: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
474
|
-
knowledge_propose: { sessionRequired: true, allOf: ["documents:search"] },
|
|
475
|
-
knowledge_correct: { sessionRequired: true, allOf: ["documents:search"] },
|
|
476
|
-
task_note_promote_knowledge: {
|
|
477
|
-
sessionRequired: true,
|
|
478
|
-
allOf: ["documents:search", "sessions:control"],
|
|
479
|
-
},
|
|
480
|
-
task_note_promote_instruction_policy: {
|
|
481
|
-
sessionRequired: true,
|
|
482
|
-
allOf: ["documents:search", "sessions:control", "workspace:read"],
|
|
483
|
-
},
|
|
484
|
-
task_note_promote_preference: {
|
|
485
|
-
sessionRequired: true,
|
|
486
|
-
allOf: ["documents:search", "sessions:control", "workspace:read"],
|
|
487
|
-
},
|
|
488
|
-
instruction_policy_propose: {
|
|
489
|
-
sessionRequired: true,
|
|
490
|
-
allOf: ["documents:search", "workspace:read"],
|
|
491
|
-
},
|
|
492
|
-
preference_propose: {
|
|
493
|
-
sessionRequired: true,
|
|
494
|
-
allOf: ["documents:search", "workspace:read"],
|
|
495
|
-
},
|
|
496
|
-
// Explicit user-directed remember composes task-note evidence with the
|
|
497
|
-
// governed promotion path, so it needs the union of both permission sets.
|
|
498
|
-
remember: {
|
|
499
|
-
sessionRequired: true,
|
|
500
|
-
allOf: ["documents:search", "sessions:control", "workspace:read"],
|
|
501
|
-
},
|
|
502
|
-
remember_confirm: {
|
|
503
|
-
sessionRequired: true,
|
|
504
|
-
allOf: ["documents:search", "sessions:control", "workspace:read"],
|
|
505
|
-
},
|
|
506
|
-
company_profile_propose: {
|
|
507
|
-
sessionRequired: true,
|
|
508
|
-
allOf: ["sessions:control", "workspace:read"],
|
|
509
|
-
},
|
|
510
|
-
company_profile_confirm: {
|
|
511
|
-
sessionRequired: true,
|
|
512
|
-
allOf: ["sessions:control", "workspace:read"],
|
|
513
|
-
},
|
|
514
|
-
sandboxes_list: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
515
|
-
sandbox_attach: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
516
|
-
sandbox_swap: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
517
|
-
run_on: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
518
|
-
sandbox_provision: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
519
|
-
connected_machine_remove: { allOf: ["enrollments:manage"] },
|
|
520
|
-
rig_list: { allOf: ["rigs:use"] },
|
|
521
|
-
rig_get: { allOf: ["rigs:use"] },
|
|
522
|
-
rig_propose_change: { allOf: ["rigs:use"] },
|
|
523
|
-
rig_verify: { allOf: ["rigs:use"] },
|
|
524
|
-
rig_promote: { allOf: ["rigs:manage"] },
|
|
525
|
-
sessions_list: { allOf: ["sessions:read"] },
|
|
526
|
-
session_get: { allOf: ["sessions:read"] },
|
|
527
|
-
session_events: { allOf: ["sessions:read"] },
|
|
528
|
-
// Blocking wait inside a running turn: the live attempt's own session is the
|
|
529
|
-
// self target, so the tool exists only for session-scoped grants.
|
|
530
|
-
session_wait: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
531
|
-
command_wait: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
532
|
-
command_read: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
533
|
-
session_create: { allOf: ["sessions:create"] },
|
|
534
|
-
session_send_message: { allOf: ["sessions:control"] },
|
|
535
|
-
session_pause: { allOf: ["sessions:control"] },
|
|
536
|
-
session_resume: { allOf: ["sessions:control"] },
|
|
537
|
-
session_steer: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
538
|
-
// A live attempt may answer another session's structured human-input
|
|
539
|
-
// request (never a tool approval, which stays human-only).
|
|
540
|
-
session_human_input_respond: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
541
|
-
set_other_session_title: { allOf: ["sessions:control"] },
|
|
542
|
-
interaction_discover: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
543
|
-
browser_open: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
544
|
-
browser_tabs: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
545
|
-
browser_observe: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
546
|
-
browser_act: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
547
|
-
browser_clipboard: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
548
|
-
browser_debug: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
549
|
-
browser_auth: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
550
|
-
interaction_request_human: {
|
|
551
|
-
sessionRequired: true,
|
|
552
|
-
allOf: ["sessions:control"],
|
|
553
|
-
},
|
|
554
|
-
browser_identity: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
555
|
-
browser_publish: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
556
|
-
browser_lifecycle: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
557
|
-
computer_open: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
558
|
-
computer_targets: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
559
|
-
computer_observe: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
560
|
-
computer_clipboard: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
561
|
-
computer_act: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
562
|
-
computer_lifecycle: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
563
|
-
variable_set_list: { allOf: ["variable-sets:list", "secrets:list"] },
|
|
564
|
-
environment_list: { allOf: ["variable-sets:list", "secrets:list"] },
|
|
565
|
-
variable_set_get_variable: {
|
|
566
|
-
sessionRequired: true,
|
|
567
|
-
allOf: ["variable-sets:read", "secrets:read"],
|
|
568
|
-
},
|
|
569
|
-
variable_set_set_variable: {
|
|
570
|
-
allOf: ["variable-sets:write", "secrets:write"],
|
|
571
|
-
},
|
|
572
|
-
environment_set_variable: { allOf: ["variable-sets:write", "secrets:write"] },
|
|
573
|
-
capability_catalog_search: {
|
|
574
|
-
sessionRequired: true,
|
|
575
|
-
allOf: ["workspace:read"],
|
|
576
|
-
},
|
|
577
|
-
capability_authorization_request: {
|
|
578
|
-
sessionRequired: true,
|
|
579
|
-
allOf: ["workspace:read"],
|
|
580
|
-
},
|
|
581
|
-
github_connect_link: { allOf: ["github:use"] },
|
|
582
|
-
github_repositories_list: { allOf: ["github:use"] },
|
|
583
|
-
social_connections_list: { allOf: ["connections:read"] },
|
|
584
|
-
social_posts_recent: { allOf: ["connections:read"] },
|
|
585
|
-
social_daily_analysis_context: { allOf: ["connections:read"] },
|
|
586
|
-
social_search_live: { allOf: ["connections:read"] },
|
|
587
|
-
social_mentions_live: { allOf: ["connections:read"] },
|
|
588
|
-
social_thread_fetch: { allOf: ["connections:read"] },
|
|
589
|
-
// Writes the social_posts store, so it takes the write scope like the REST
|
|
590
|
-
// equivalent (POST /social/posts is workspace:admin).
|
|
591
|
-
social_posts_sync: { allOf: ["connections:write"] },
|
|
592
|
-
// Publishes under the user's identity: connections:write keeps it out of the
|
|
593
|
-
// default agent permission set, unlike the read-only social tools above.
|
|
594
|
-
social_post_reply: { allOf: ["connections:write"] },
|
|
595
|
-
x_accounts_list: { allOf: ["connections:read"] },
|
|
596
|
-
x_search_live: { allOf: ["connections:read"] },
|
|
597
|
-
x_mentions_live: { allOf: ["connections:read"] },
|
|
598
|
-
x_thread_fetch: { allOf: ["connections:read"] },
|
|
599
|
-
x_posts_sync: { allOf: ["connections:write"] },
|
|
600
|
-
x_post_reply: { allOf: ["connections:write"] },
|
|
601
|
-
reddit_accounts_list: { allOf: ["connections:read"] },
|
|
602
|
-
reddit_search_live: { allOf: ["connections:read"] },
|
|
603
|
-
reddit_mentions_live: { allOf: ["connections:read"] },
|
|
604
|
-
reddit_thread_fetch: { allOf: ["connections:read"] },
|
|
605
|
-
reddit_posts_sync: { allOf: ["connections:write"] },
|
|
606
|
-
reddit_post_reply: { allOf: ["connections:write"] },
|
|
607
|
-
scheduled_tasks_list: {
|
|
608
|
-
anyOf: ["scheduled_tasks:manage", "scheduled_tasks:run"],
|
|
609
|
-
},
|
|
610
|
-
scheduled_tasks_get: {
|
|
611
|
-
anyOf: ["scheduled_tasks:manage", "scheduled_tasks:run"],
|
|
612
|
-
},
|
|
613
|
-
scheduled_tasks_create: { allOf: ["scheduled_tasks:manage"] },
|
|
614
|
-
scheduled_tasks_update: { allOf: ["scheduled_tasks:manage"] },
|
|
615
|
-
scheduled_tasks_pause: { allOf: ["scheduled_tasks:manage"] },
|
|
616
|
-
scheduled_tasks_resume: { allOf: ["scheduled_tasks:manage"] },
|
|
617
|
-
scheduled_tasks_trigger: { allOf: ["scheduled_tasks:run"] },
|
|
618
|
-
scheduled_tasks_delete: { allOf: ["scheduled_tasks:manage"] },
|
|
619
|
-
scheduled_task_runs_list: {
|
|
620
|
-
anyOf: ["scheduled_tasks:manage", "scheduled_tasks:run"],
|
|
621
|
-
},
|
|
622
|
-
slack_bot_list_channels: { allOf: ["connections:read"] },
|
|
623
|
-
slack_bot_search: { allOf: ["connections:read"] },
|
|
624
|
-
slack_bot_channel_history: { allOf: ["connections:read"] },
|
|
625
|
-
slack_bot_thread_replies: { allOf: ["connections:read"] },
|
|
626
|
-
slack_bot_list_users: { allOf: ["connections:read"] },
|
|
627
|
-
slack_bot_list_files: { allOf: ["connections:read"] },
|
|
628
|
-
slack_bot_file_info: { allOf: ["connections:read"] },
|
|
629
|
-
slack_bot_file_content: { allOf: ["connections:read"] },
|
|
630
|
-
slack_bot_post_message: { allOf: ["connections:read"] },
|
|
631
|
-
slack_bot_delete_message: { allOf: ["connections:read"] },
|
|
632
|
-
fiken_companies_list: { allOf: ["connections:read"] },
|
|
633
|
-
fiken_contacts_list: { allOf: ["connections:read"] },
|
|
634
|
-
fiken_products_list: { allOf: ["connections:read"] },
|
|
635
|
-
fiken_invoices_list: { allOf: ["connections:read"] },
|
|
636
|
-
fiken_invoice_get: { allOf: ["connections:read"] },
|
|
637
|
-
fiken_bank_accounts_list: { allOf: ["connections:read"] },
|
|
638
|
-
fiken_purchases_list: { allOf: ["connections:read"] },
|
|
639
|
-
fiken_sales_list: { allOf: ["connections:read"] },
|
|
640
|
-
// Writes into the workspace's real accounting ledger surface take the write
|
|
641
|
-
// scope, keeping them out of the default agent permission set.
|
|
642
|
-
fiken_contact_create: { allOf: ["connections:write"] },
|
|
643
|
-
fiken_invoice_draft_create: { allOf: ["connections:write"] },
|
|
644
|
-
atlassian_sources_list: { allOf: ["connections:read"] },
|
|
645
|
-
atlassian_search: { allOf: ["connections:read"] },
|
|
646
|
-
atlassian_get: { allOf: ["connections:read"] },
|
|
647
|
-
artifacts_list: { allOf: ["artifacts:read"] },
|
|
648
|
-
artifacts_get_source: { sessionRequired: true, allOf: ["artifacts:read"] },
|
|
649
|
-
artifacts_prepare_upload: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
650
|
-
artifacts_create: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
651
|
-
artifacts_publish: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
652
|
-
artifacts_rollback: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
653
|
-
artifacts_archive: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
654
|
-
artifacts_restore: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
655
|
-
sandbox_file_publish: {
|
|
656
|
-
sessionRequired: true,
|
|
657
|
-
allOf: ["files:read", "files:upload"],
|
|
658
|
-
},
|
|
659
|
-
editable_artifact_list: { sessionRequired: true, allOf: ["artifacts:read"] },
|
|
660
|
-
editable_artifact_create: {
|
|
661
|
-
sessionRequired: true,
|
|
662
|
-
allOf: ["artifacts:publish"],
|
|
663
|
-
},
|
|
664
|
-
editable_artifact_import: {
|
|
665
|
-
sessionRequired: true,
|
|
666
|
-
allOf: ["artifacts:publish", "files:read"],
|
|
667
|
-
},
|
|
668
|
-
editable_artifact_get: { sessionRequired: true, allOf: ["artifacts:read"] },
|
|
669
|
-
editable_artifact_inspect: {
|
|
670
|
-
sessionRequired: true,
|
|
671
|
-
allOf: ["artifacts:read"],
|
|
672
|
-
},
|
|
673
|
-
editable_artifact_apply: {
|
|
674
|
-
sessionRequired: true,
|
|
675
|
-
allOf: ["artifacts:publish"],
|
|
676
|
-
},
|
|
677
|
-
editable_artifact_export: {
|
|
678
|
-
sessionRequired: true,
|
|
679
|
-
allOf: ["artifacts:read"],
|
|
680
|
-
},
|
|
681
|
-
editable_artifact_export_status: {
|
|
682
|
-
sessionRequired: true,
|
|
683
|
-
allOf: ["artifacts:read", "files:upload"],
|
|
684
|
-
},
|
|
685
|
-
} satisfies Record<FirstPartyMcpToolName, FirstPartyToolAuthorization>;
|
|
686
|
-
|
|
687
462
|
const FIRST_PARTY_MCP_TOOL_NAME_SET = new Set<string>(FIRST_PARTY_MCP_TOOL_NAMES);
|
|
688
463
|
|
|
689
464
|
class PolicyMcpServer extends McpServer {
|
|
@@ -799,13 +574,20 @@ export function buildOpenGeniMcpServer(
|
|
|
799
574
|
typeof grant.metadata?.["sessionId"] === "string"
|
|
800
575
|
? (grant.metadata["sessionId"] as string)
|
|
801
576
|
: null;
|
|
577
|
+
// A session-scoped grant carries its model-visible selection as a signed
|
|
578
|
+
// claim. When that claim is absent the grant was not minted for the ordinary
|
|
579
|
+
// first-party MCP surface (today only the sandbox Codemode bearer has that
|
|
580
|
+
// shape), so it resolves to no session tools rather than the deployment
|
|
581
|
+
// default catalog: an omitted claim must never widen.
|
|
582
|
+
const signedSelection = grant.metadata?.["firstPartyMcpTools"] as
|
|
583
|
+
| FirstPartyMcpToolName[]
|
|
584
|
+
| undefined;
|
|
802
585
|
const selectedTools =
|
|
803
586
|
sessionId !== null
|
|
804
587
|
? new Set(
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
),
|
|
588
|
+
signedSelection === undefined
|
|
589
|
+
? []
|
|
590
|
+
: allowedFirstPartyMcpToolsForSession(deps.settings, signedSelection),
|
|
809
591
|
)
|
|
810
592
|
: null;
|
|
811
593
|
const nestedAgentDepth = grant.metadata?.["nestedAgentDepth"];
|
|
@@ -847,7 +629,13 @@ export function buildOpenGeniMcpServer(
|
|
|
847
629
|
if (sessionId !== null) {
|
|
848
630
|
registerGoalTools(server, deps, grant, sessionId, json);
|
|
849
631
|
}
|
|
850
|
-
|
|
632
|
+
// A session frozen with memoryScope "off" receives no Memory tools at all,
|
|
633
|
+
// independent of the workspace-level Memory setting.
|
|
634
|
+
if (
|
|
635
|
+
sessionId !== null &&
|
|
636
|
+
options.workspaceMemoryEnabled === true &&
|
|
637
|
+
options.sessionMemory?.mode !== "off"
|
|
638
|
+
) {
|
|
851
639
|
registerMemoryTools(
|
|
852
640
|
server,
|
|
853
641
|
deps,
|
|
@@ -855,6 +643,7 @@ export function buildOpenGeniMcpServer(
|
|
|
855
643
|
sessionId,
|
|
856
644
|
json,
|
|
857
645
|
options.workspaceMemoryPromptMode ?? "retrieval_only",
|
|
646
|
+
options.sessionMemory ?? null,
|
|
858
647
|
);
|
|
859
648
|
}
|
|
860
649
|
server.registerTool(
|
|
@@ -974,6 +763,7 @@ export function buildOpenGeniMcpServer(
|
|
|
974
763
|
sessionCreateVisible,
|
|
975
764
|
json,
|
|
976
765
|
);
|
|
766
|
+
registerProjectTools(server, deps, grant, can, json);
|
|
977
767
|
registerVariableSetTools(server, deps, grant, can, sessionId, json);
|
|
978
768
|
if (sessionId !== null && can("workspace:read")) {
|
|
979
769
|
registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json);
|
|
@@ -1585,7 +1375,8 @@ export function buildOpenGeniMcpServer(
|
|
|
1585
1375
|
server.registerTool(
|
|
1586
1376
|
"scheduled_tasks_create",
|
|
1587
1377
|
{
|
|
1588
|
-
description:
|
|
1378
|
+
description:
|
|
1379
|
+
"Create a scheduled task. Sessions generated for a task created from this session inherit this session's effective first-party tool selection and permission set; they never receive the deployment default catalog.",
|
|
1589
1380
|
inputSchema: {
|
|
1590
1381
|
name: z4.string(),
|
|
1591
1382
|
schedule: z4.unknown(),
|
|
@@ -3814,7 +3605,17 @@ function registerMemoryTools(
|
|
|
3814
3605
|
sessionId: string,
|
|
3815
3606
|
json: JsonResult,
|
|
3816
3607
|
promptMode: WorkspaceMemoryPromptMode,
|
|
3608
|
+
sessionMemory: MemoryAgentScope | null,
|
|
3817
3609
|
): void {
|
|
3610
|
+
// Read layers: workspace plus the session's private layer. Write layer: the
|
|
3611
|
+
// session's narrowest layer. A null scope is the pre-0426 workspace path.
|
|
3612
|
+
const agentScope: MemoryAgentScope = sessionMemory ?? {
|
|
3613
|
+
mode: "workspace",
|
|
3614
|
+
endUserSubjectId: null,
|
|
3615
|
+
rootSessionId: null,
|
|
3616
|
+
};
|
|
3617
|
+
const writeScope = memoryWriteScopeForAgentScope(agentScope);
|
|
3618
|
+
if (!writeScope) return;
|
|
3818
3619
|
const publicationInputSchema = z4.object({
|
|
3819
3620
|
importance: z4.enum(["major", "normal", "minor"]),
|
|
3820
3621
|
audience: z4.literal("workspace"),
|
|
@@ -3842,6 +3643,7 @@ function registerMemoryTools(
|
|
|
3842
3643
|
...(kind ? { kind } : {}),
|
|
3843
3644
|
...(limit ? { limit } : {}),
|
|
3844
3645
|
agentPromptMode: promptMode,
|
|
3646
|
+
agentScope,
|
|
3845
3647
|
},
|
|
3846
3648
|
deps.getDocumentServices().embedder,
|
|
3847
3649
|
),
|
|
@@ -3877,6 +3679,7 @@ function registerMemoryTools(
|
|
|
3877
3679
|
...(confidence !== undefined ? { confidence } : {}),
|
|
3878
3680
|
...(replaces_id ? { replacesId: replaces_id } : {}),
|
|
3879
3681
|
origin: "agent",
|
|
3682
|
+
scope: writeScope,
|
|
3880
3683
|
},
|
|
3881
3684
|
slack_publication
|
|
3882
3685
|
? {
|
|
@@ -3995,6 +3798,7 @@ function registerMemoryTools(
|
|
|
3995
3798
|
...(reason ? { reason } : {}),
|
|
3996
3799
|
...(replacement_text ? { replacementText: replacement_text } : {}),
|
|
3997
3800
|
origin: "agent",
|
|
3801
|
+
agentScope,
|
|
3998
3802
|
},
|
|
3999
3803
|
slack_publication
|
|
4000
3804
|
? {
|
|
@@ -4706,7 +4510,22 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4706
4510
|
.max(8)
|
|
4707
4511
|
.optional(),
|
|
4708
4512
|
activeOnly: z4.boolean().optional(),
|
|
4513
|
+
originSiteId: z4
|
|
4514
|
+
.string()
|
|
4515
|
+
.uuid()
|
|
4516
|
+
.optional()
|
|
4517
|
+
.describe(
|
|
4518
|
+
"Conversations created through this Site, regardless of their project. Keep unchanged when paging.",
|
|
4519
|
+
),
|
|
4709
4520
|
recentHours: z4.number().int().positive().max(WORK_DISCOVERY_RECENT_HOURS_MAX).optional(),
|
|
4521
|
+
projectId: z4
|
|
4522
|
+
.string()
|
|
4523
|
+
.uuid()
|
|
4524
|
+
.nullable()
|
|
4525
|
+
.optional()
|
|
4526
|
+
.describe(
|
|
4527
|
+
"Filter by workspace project; null selects unfiled sessions. Keep unchanged when paging.",
|
|
4528
|
+
),
|
|
4710
4529
|
rootSessionId: z4.string().uuid().optional(),
|
|
4711
4530
|
parentSessionId: z4.string().uuid().nullable().optional(),
|
|
4712
4531
|
subject: z4
|
|
@@ -4731,8 +4550,10 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4731
4550
|
query,
|
|
4732
4551
|
statuses,
|
|
4733
4552
|
activeOnly,
|
|
4553
|
+
originSiteId,
|
|
4734
4554
|
recentHours,
|
|
4735
4555
|
rootSessionId,
|
|
4556
|
+
projectId,
|
|
4736
4557
|
parentSessionId,
|
|
4737
4558
|
subject,
|
|
4738
4559
|
claimLimit,
|
|
@@ -4795,8 +4616,10 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4795
4616
|
...(query?.trim() ? { query } : {}),
|
|
4796
4617
|
...(statuses ? { statuses: statuses as SessionStatus[] } : {}),
|
|
4797
4618
|
activeOnly: activeOnly === true,
|
|
4619
|
+
...(originSiteId ? { originSiteId } : {}),
|
|
4798
4620
|
...(recentHours !== undefined ? { recentHours } : {}),
|
|
4799
4621
|
...(rootSessionId ? { rootSessionId } : {}),
|
|
4622
|
+
...(projectId !== undefined ? { channelId: projectId } : {}),
|
|
4800
4623
|
...(parentSessionId !== undefined ? { parentSessionId } : {}),
|
|
4801
4624
|
...(subject ? { subject: subject as WorkClaimSubjectFilter } : {}),
|
|
4802
4625
|
...(claimLimit !== undefined ? { claimLimit } : {}),
|
|
@@ -5260,6 +5083,14 @@ function registerWorkspaceOrchestrationTools(
|
|
|
5260
5083
|
const sessionCreateInput = z4
|
|
5261
5084
|
.object({
|
|
5262
5085
|
initialMessage: z4.string().min(1),
|
|
5086
|
+
projectId: z4
|
|
5087
|
+
.string()
|
|
5088
|
+
.uuid()
|
|
5089
|
+
.nullable()
|
|
5090
|
+
.optional()
|
|
5091
|
+
.describe(
|
|
5092
|
+
"Workspace project to file the new session into. Omit for existing default behavior; does not change runtime or visibility.",
|
|
5093
|
+
),
|
|
5263
5094
|
title: z4
|
|
5264
5095
|
.string()
|
|
5265
5096
|
.min(1)
|
|
@@ -5320,7 +5151,16 @@ function registerWorkspaceOrchestrationTools(
|
|
|
5320
5151
|
.array(z4.enum(FIRST_PARTY_MCP_TOOL_NAMES))
|
|
5321
5152
|
.optional()
|
|
5322
5153
|
.describe(
|
|
5323
|
-
"Exact model-visible first-party tool selection for the child. Omit to inherit this session's effective selection. To create a non-delegating leaf, provide a selection that omits session_create. This does not grant permissions.",
|
|
5154
|
+
"Exact model-visible first-party tool selection for the child. Omit to inherit this session's effective selection. An explicit selection may only narrow that selection: every listed tool must already be available to this session, and a wider list is rejected. To create a non-delegating leaf, provide a selection that omits session_create. This does not grant permissions.",
|
|
5155
|
+
),
|
|
5156
|
+
// The child's agent-access scope and end-user label are never model
|
|
5157
|
+
// choices: it inherits this session's exactly. Only the Memory
|
|
5158
|
+
// selector may be narrowed here (workspace > user > session > off).
|
|
5159
|
+
memoryScope: z4
|
|
5160
|
+
.enum(["workspace", "user", "session", "off"])
|
|
5161
|
+
.optional()
|
|
5162
|
+
.describe(
|
|
5163
|
+
"Optional Memory selector for the child. Omit to inherit this session's selector. An explicit value may only narrow it (workspace > user > session > off); off gives the child no Memory tools.",
|
|
5324
5164
|
),
|
|
5325
5165
|
// Omission is the ordinary safe sharing path. Literal "shared" remains
|
|
5326
5166
|
// available to advanced REST/SDK callers but is intentionally absent
|
|
@@ -5355,7 +5195,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
5355
5195
|
"session_create",
|
|
5356
5196
|
{
|
|
5357
5197
|
description:
|
|
5358
|
-
"Spawn a new agent session (a worker) only for a concrete, bounded subtask that can run independently and has a defined integration point in your current work. Do not delegate work you will also perform yourself; track the child and join its actual result before completing dependent work. Give the child a concise semantic title; if omitted, OpenGeni derives one from its delegated goal or initial message. The child inherits this session's visibility; a private session can only create a same-owner private child. Give a goal-bearing child its delegated objective. Its goal.rootConstraints may be an exact applicable subset of this accepted turn's frozen root constraints; omit that field to inherit all of them. Omit sandbox for the safe default: compatible children share the creator's box, while a different Variable Set, Rig, or machineTarget gets its own box. Use 'new' for deliberate isolation or {groupId} for a strict compatible sibling join. Put targetSandboxId and its optional workingDir together inside machineTarget; a machineTarget is always an own-box create even when the parent is backend none. To create a non-delegating leaf, pass a narrowed firstPartyMcpTools list that omits session_create; do not use a child-local depth override. Public REST/SDK callers retain advanced absolute depth and explicit shared-placement controls.",
|
|
5198
|
+
"Spawn a new agent session (a worker) only for a concrete, bounded subtask that can run independently and has a defined integration point in your current work. Do not delegate work you will also perform yourself; track the child and join its actual result before completing dependent work. Give the child a concise semantic title; if omitted, OpenGeni derives one from its delegated goal or initial message. The child inherits this session's visibility, agent-access scope and end-user label; a private session can only create a same-owner private child, and memoryScope may only narrow this session's selector. Give a goal-bearing child its delegated objective. Its goal.rootConstraints may be an exact applicable subset of this accepted turn's frozen root constraints; omit that field to inherit all of them. Omit sandbox for the safe default: compatible children share the creator's box, while a different Variable Set, Rig, or machineTarget gets its own box. Use 'new' for deliberate isolation or {groupId} for a strict compatible sibling join. Put targetSandboxId and its optional workingDir together inside machineTarget; a machineTarget is always an own-box create even when the parent is backend none. To create a non-delegating leaf, pass a narrowed firstPartyMcpTools list that omits session_create; do not use a child-local depth override. Public REST/SDK callers retain advanced absolute depth and explicit shared-placement controls.",
|
|
5359
5199
|
inputSchema: sessionCreateInput,
|
|
5360
5200
|
},
|
|
5361
5201
|
async (args) => {
|
|
@@ -5368,13 +5208,14 @@ function registerWorkspaceOrchestrationTools(
|
|
|
5368
5208
|
if (callerSessionId !== null) {
|
|
5369
5209
|
await authorizeFirstPartySession(deps, grant, callerSessionId, "session.child.create");
|
|
5370
5210
|
}
|
|
5371
|
-
const { machineTarget, title, ...request } = args;
|
|
5211
|
+
const { machineTarget, title, projectId, ...request } = args;
|
|
5372
5212
|
const result = await createSessionForRequestWithOutcome(
|
|
5373
5213
|
deps,
|
|
5374
5214
|
grant,
|
|
5375
5215
|
grant.workspaceId,
|
|
5376
5216
|
{
|
|
5377
5217
|
...request,
|
|
5218
|
+
...(projectId !== undefined ? { channelId: projectId } : {}),
|
|
5378
5219
|
...(machineTarget
|
|
5379
5220
|
? {
|
|
5380
5221
|
targetSandboxId: machineTarget.targetSandboxId,
|
|
@@ -5783,6 +5624,130 @@ function registerWorkspaceOrchestrationTools(
|
|
|
5783
5624
|
}
|
|
5784
5625
|
}
|
|
5785
5626
|
|
|
5627
|
+
function registerProjectTools(
|
|
5628
|
+
server: McpServer,
|
|
5629
|
+
deps: ApiRouteDeps,
|
|
5630
|
+
grant: AccessGrant,
|
|
5631
|
+
can: (permission: Permission) => boolean,
|
|
5632
|
+
json: JsonResult,
|
|
5633
|
+
): void {
|
|
5634
|
+
const projectIdSchema = z4.string().uuid();
|
|
5635
|
+
const nameSchema = z4.string().trim().min(1).max(80);
|
|
5636
|
+
const descriptionSchema = z4.string().max(2000);
|
|
5637
|
+
if (can("sessions:read")) {
|
|
5638
|
+
server.registerTool(
|
|
5639
|
+
"project_list",
|
|
5640
|
+
{
|
|
5641
|
+
description:
|
|
5642
|
+
"List workspace-shared projects in sidebar order (pinned first). Projects group sessions in the user's sidebar; they do not change execution or access. Use before creating a project to reuse an existing group.",
|
|
5643
|
+
inputSchema: {},
|
|
5644
|
+
},
|
|
5645
|
+
async () => json({ projects: await listChannels(deps.db, grant.workspaceId) }),
|
|
5646
|
+
);
|
|
5647
|
+
server.registerTool(
|
|
5648
|
+
"project_get",
|
|
5649
|
+
{
|
|
5650
|
+
description:
|
|
5651
|
+
"Read one workspace project: name, description, pin state and order. Use sessions_list with projectId to find its visible sessions.",
|
|
5652
|
+
inputSchema: { projectId: projectIdSchema },
|
|
5653
|
+
},
|
|
5654
|
+
async ({ projectId }) => {
|
|
5655
|
+
const project = await getChannel(deps.db, grant.workspaceId, projectId);
|
|
5656
|
+
if (!project) throw new Error("Project not found");
|
|
5657
|
+
return json({ project });
|
|
5658
|
+
},
|
|
5659
|
+
);
|
|
5660
|
+
}
|
|
5661
|
+
if (can("sessions:create")) {
|
|
5662
|
+
server.registerTool(
|
|
5663
|
+
"project_create",
|
|
5664
|
+
{
|
|
5665
|
+
description:
|
|
5666
|
+
"Create a workspace-shared sidebar group for related sessions. A project is organizational metadata, not a repository, working directory, or inherited agent configuration.",
|
|
5667
|
+
inputSchema: { name: nameSchema, description: descriptionSchema.optional() },
|
|
5668
|
+
},
|
|
5669
|
+
async ({ name, description }) =>
|
|
5670
|
+
json({
|
|
5671
|
+
project: await createChannel(deps.db, {
|
|
5672
|
+
accountId: grant.accountId,
|
|
5673
|
+
workspaceId: grant.workspaceId,
|
|
5674
|
+
name,
|
|
5675
|
+
description: description ?? null,
|
|
5676
|
+
createdBy: grant.subjectId,
|
|
5677
|
+
}),
|
|
5678
|
+
}),
|
|
5679
|
+
);
|
|
5680
|
+
server.registerTool(
|
|
5681
|
+
"project_update",
|
|
5682
|
+
{
|
|
5683
|
+
description:
|
|
5684
|
+
"Rename, describe or pin/unpin a workspace project. Pin state is shared by everyone in the workspace. Omitted fields stay unchanged; null clears the description.",
|
|
5685
|
+
inputSchema: {
|
|
5686
|
+
projectId: projectIdSchema,
|
|
5687
|
+
name: nameSchema.optional(),
|
|
5688
|
+
description: descriptionSchema.nullable().optional(),
|
|
5689
|
+
pinned: z4.boolean().optional(),
|
|
5690
|
+
},
|
|
5691
|
+
},
|
|
5692
|
+
async ({ projectId, ...update }) => {
|
|
5693
|
+
const project = await updateChannel(deps.db, grant.workspaceId, projectId, update);
|
|
5694
|
+
if (!project) throw new Error("Project not found");
|
|
5695
|
+
return json({ project });
|
|
5696
|
+
},
|
|
5697
|
+
);
|
|
5698
|
+
server.registerTool(
|
|
5699
|
+
"project_reorder",
|
|
5700
|
+
{
|
|
5701
|
+
description:
|
|
5702
|
+
"Replace the shared sidebar project order. Pass every current project ID exactly once, from project_list. Pinned projects still appear first. If projects changed, list again before retrying.",
|
|
5703
|
+
inputSchema: { projectIds: z4.array(projectIdSchema).min(1).max(200) },
|
|
5704
|
+
},
|
|
5705
|
+
async ({ projectIds }) => {
|
|
5706
|
+
const projects = await reorderChannels(deps.db, grant.workspaceId, projectIds);
|
|
5707
|
+
if (!projects)
|
|
5708
|
+
throw new Error("Projects changed or order is invalid; call project_list and retry");
|
|
5709
|
+
return json({ projects });
|
|
5710
|
+
},
|
|
5711
|
+
);
|
|
5712
|
+
server.registerTool(
|
|
5713
|
+
"project_delete",
|
|
5714
|
+
{
|
|
5715
|
+
description:
|
|
5716
|
+
"Delete a workspace-shared project. Its sessions are preserved and become unfiled; no running work is stopped.",
|
|
5717
|
+
inputSchema: { projectId: projectIdSchema },
|
|
5718
|
+
},
|
|
5719
|
+
async ({ projectId }) => {
|
|
5720
|
+
if (!(await deleteChannel(deps.db, grant.workspaceId, projectId)))
|
|
5721
|
+
throw new Error("Project not found");
|
|
5722
|
+
return json({ ok: true, projectId });
|
|
5723
|
+
},
|
|
5724
|
+
);
|
|
5725
|
+
}
|
|
5726
|
+
if (can("sessions:control")) {
|
|
5727
|
+
server.registerTool(
|
|
5728
|
+
"session_set_project",
|
|
5729
|
+
{
|
|
5730
|
+
description:
|
|
5731
|
+
"File a session into a workspace project, or pass projectId=null to unfile it. Projects group root sessions in the user's sidebar. Filing changes organization only, not visibility, instructions, runtime, or history.",
|
|
5732
|
+
inputSchema: { sessionId: z4.string().uuid(), projectId: projectIdSchema.nullable() },
|
|
5733
|
+
},
|
|
5734
|
+
async ({ sessionId, projectId }) => {
|
|
5735
|
+
await authorizeFirstPartySession(deps, grant, sessionId, "session.control");
|
|
5736
|
+
await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
5737
|
+
if (
|
|
5738
|
+
!(await setSessionChannel(deps.db, {
|
|
5739
|
+
workspaceId: grant.workspaceId,
|
|
5740
|
+
sessionId,
|
|
5741
|
+
channelId: projectId,
|
|
5742
|
+
}))
|
|
5743
|
+
)
|
|
5744
|
+
throw new Error("Session not found");
|
|
5745
|
+
return json({ ok: true, sessionId, projectId });
|
|
5746
|
+
},
|
|
5747
|
+
);
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5786
5751
|
// Variable-set management for agents. Generic reads remain metadata-only.
|
|
5787
5752
|
// Plaintext has one dedicated tool that additionally requires literal
|
|
5788
5753
|
// secrets:read plus exact live attempt/session authorization.
|
|
@@ -6460,7 +6425,8 @@ export function encodeSessionDiscoveryCursor(cursor: SessionDiscoveryCursor): st
|
|
|
6460
6425
|
const relevance = cursor.orderBy === "relevance";
|
|
6461
6426
|
return Buffer.from(
|
|
6462
6427
|
JSON.stringify({
|
|
6463
|
-
v: relevance ? 3 : 2,
|
|
6428
|
+
v: cursor.originSiteId ? 4 : relevance ? 3 : 2,
|
|
6429
|
+
...(cursor.originSiteId ? { originSiteId: cursor.originSiteId } : {}),
|
|
6464
6430
|
orderBy: cursor.orderBy,
|
|
6465
6431
|
...(relevance ? { sortRank: cursor.sortRank, filterHash: cursor.filterHash } : {}),
|
|
6466
6432
|
sortRevision: cursor.sortRevision,
|
|
@@ -6514,6 +6480,7 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
6514
6480
|
updatedAfter?: unknown;
|
|
6515
6481
|
sortRank?: unknown;
|
|
6516
6482
|
filterHash?: unknown;
|
|
6483
|
+
originSiteId?: unknown;
|
|
6517
6484
|
};
|
|
6518
6485
|
if (
|
|
6519
6486
|
parsed.v === undefined &&
|
|
@@ -6563,8 +6530,14 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
6563
6530
|
}
|
|
6564
6531
|
const isV2 = parsed.v === 2;
|
|
6565
6532
|
const isV3 = parsed.v === 3;
|
|
6533
|
+
const isV4 = parsed.v === 4;
|
|
6534
|
+
const relevance = parsed.orderBy === "relevance";
|
|
6566
6535
|
if (
|
|
6567
|
-
(!isV2 && !isV3) ||
|
|
6536
|
+
(!isV2 && !isV3 && !isV4) ||
|
|
6537
|
+
(isV4 &&
|
|
6538
|
+
(typeof parsed.originSiteId !== "string" ||
|
|
6539
|
+
!SESSION_DISCOVERY_UUID.test(parsed.originSiteId) ||
|
|
6540
|
+
!["createdAt", "updatedAt", "relevance"].includes(parsed.orderBy as string))) ||
|
|
6568
6541
|
(isV2 && parsed.orderBy !== "createdAt" && parsed.orderBy !== "updatedAt") ||
|
|
6569
6542
|
(isV3 && parsed.orderBy !== "relevance") ||
|
|
6570
6543
|
typeof parsed.sortRevision !== "string" ||
|
|
@@ -6578,7 +6551,7 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
6578
6551
|
throw new Error("invalid cursor fields");
|
|
6579
6552
|
}
|
|
6580
6553
|
if (
|
|
6581
|
-
|
|
6554
|
+
relevance &&
|
|
6582
6555
|
(!Number.isSafeInteger(parsed.sortRank) ||
|
|
6583
6556
|
(parsed.sortRank as number) < 0 ||
|
|
6584
6557
|
typeof parsed.filterHash !== "string" ||
|
|
@@ -6586,7 +6559,7 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
6586
6559
|
) {
|
|
6587
6560
|
throw new Error("invalid relevance cursor fields");
|
|
6588
6561
|
}
|
|
6589
|
-
if (
|
|
6562
|
+
if (!relevance && (parsed.sortRank !== undefined || parsed.filterHash !== undefined)) {
|
|
6590
6563
|
throw new Error("chronological cursor cannot carry relevance fields");
|
|
6591
6564
|
}
|
|
6592
6565
|
const sortAt = normalizeSessionDiscoveryTimestamp(parsed.sortAt, "cursor sortAt");
|
|
@@ -6609,19 +6582,20 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
6609
6582
|
if (parsed.orderBy === "createdAt" && (sortRevision !== "0" || snapshotRevision !== "0")) {
|
|
6610
6583
|
throw new Error("creation cursor cannot carry activity revisions");
|
|
6611
6584
|
}
|
|
6612
|
-
const orderBy: SessionDiscoveryOrderBy =
|
|
6585
|
+
const orderBy: SessionDiscoveryOrderBy = relevance
|
|
6613
6586
|
? "relevance"
|
|
6614
6587
|
: (parsed.orderBy as "createdAt" | "updatedAt");
|
|
6615
6588
|
return {
|
|
6616
6589
|
orderBy,
|
|
6617
|
-
sortRank:
|
|
6590
|
+
sortRank: relevance ? (parsed.sortRank as number) : null,
|
|
6618
6591
|
sortRevision,
|
|
6619
6592
|
sortAt,
|
|
6620
6593
|
id: parsed.id,
|
|
6621
6594
|
snapshotAt,
|
|
6622
6595
|
snapshotRevision,
|
|
6623
6596
|
updatedAfter: normalizedUpdatedAfter,
|
|
6624
|
-
filterHash:
|
|
6597
|
+
filterHash: relevance ? (parsed.filterHash as string) : null,
|
|
6598
|
+
...(isV4 ? { originSiteId: parsed.originSiteId as string } : {}),
|
|
6625
6599
|
};
|
|
6626
6600
|
} catch {
|
|
6627
6601
|
throw new Error("sessions_list cursor is invalid");
|
|
@@ -6688,6 +6662,7 @@ export function capSessionDiscoveryCompactPage(
|
|
|
6688
6662
|
snapshotRevision: page.snapshotRevision,
|
|
6689
6663
|
updatedAfter: page.updatedAfter,
|
|
6690
6664
|
filterHash: page.filterHash,
|
|
6665
|
+
...(page.originSiteId ? { originSiteId: page.originSiteId } : {}),
|
|
6691
6666
|
}
|
|
6692
6667
|
: page.nextCursor;
|
|
6693
6668
|
return {
|
|
@@ -6740,6 +6715,7 @@ export function capSessionDiscoveryPage(
|
|
|
6740
6715
|
: null;
|
|
6741
6716
|
return {
|
|
6742
6717
|
id: session.id,
|
|
6718
|
+
...(session.channelId ? { projectId: session.channelId } : {}),
|
|
6743
6719
|
title: title.text,
|
|
6744
6720
|
titleTruncated: title.truncated,
|
|
6745
6721
|
parentSessionId: session.parentSessionId,
|
|
@@ -6852,6 +6828,7 @@ export function capSessionDiscoveryPage(
|
|
|
6852
6828
|
snapshotRevision: page.snapshotRevision,
|
|
6853
6829
|
updatedAfter: page.updatedAfter,
|
|
6854
6830
|
filterHash: page.filterHash,
|
|
6831
|
+
...(page.originSiteId ? { originSiteId: page.originSiteId } : {}),
|
|
6855
6832
|
})
|
|
6856
6833
|
: null
|
|
6857
6834
|
: page.nextCursor
|