@opengeni/api-router 2.13.0-canary.0 → 2.13.0-canary.2
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-BJWHVMOL.js → chunk-K2NU4KRK.js} +3165 -3662
- package/dist/{chunk-BJWHVMOL.js.map → chunk-K2NU4KRK.js.map} +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/integrations/github-lens-connect.d.ts +2 -2
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/documents.d.ts +5 -13
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +36 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/server.d.ts +4 -4
- package/dist/routes/host-mcp-resolvers.d.ts +3 -0
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/sandbox-file-artifacts.d.ts +6 -2
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway.d.ts +2 -1
- package/package.json +19 -19
- package/src/access-grant-rls.ts +13 -1
- package/src/app.ts +4 -11
- package/src/codemode.ts +4 -8
- package/src/index.ts +4 -3
- package/src/integrations/atlassian.ts +31 -28
- package/src/integrations/google-drive.ts +33 -30
- package/src/integrations/marketplace-plugin.ts +1 -1
- package/src/integrations/provider-oauth.ts +11 -2
- package/src/mcp/documents.ts +57 -428
- package/src/mcp/files.ts +76 -58
- package/src/mcp/first-party-tool-permissions.ts +9 -0
- package/src/mcp/knowledge-entries.ts +276 -0
- package/src/mcp/server.ts +7 -361
- package/src/routes/company-brain.ts +58 -107
- package/src/routes/documents.ts +78 -294
- package/src/routes/files.ts +68 -3
- package/src/routes/host-mcp-resolvers.ts +41 -0
- package/src/routes/knowledge.ts +386 -0
- package/src/routes/organization-memberships.ts +33 -0
- package/src/routes/scheduled-tasks.ts +9 -1
- package/src/routes/sessions.ts +20 -3
- package/src/routes/skill-content.ts +8 -2
- package/src/routes/video-generation.ts +7 -5
- package/src/routes/workspace-learning.ts +12 -82
- package/src/routes/workspace-state.ts +34 -25
- package/src/routes/workspaces.ts +20 -10
- package/src/sandbox-file-artifacts.ts +54 -2
- package/src/scheduled-task-deletion.ts +3 -2
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway.ts +14 -25
- package/dist/mcp/company-brain-governed-writes.d.ts +0 -26
- package/dist/mcp/remember.d.ts +0 -28
- package/src/mcp/company-brain-governed-writes.ts +0 -271
- package/src/mcp/remember.ts +0 -191
package/src/mcp/server.ts
CHANGED
|
@@ -34,7 +34,6 @@ import {
|
|
|
34
34
|
stableJson,
|
|
35
35
|
compactSessionEventResult,
|
|
36
36
|
sessionEventLatestClassToSemanticClass,
|
|
37
|
-
MemorySlackPublicationDistribution,
|
|
38
37
|
SessionMcpCredentialUpdateInput,
|
|
39
38
|
ToolAuthNeededPayload,
|
|
40
39
|
VariableSetVariableName,
|
|
@@ -116,13 +115,8 @@ import {
|
|
|
116
115
|
readVariableSetSecretAtomically,
|
|
117
116
|
recordSyncedSocialPosts,
|
|
118
117
|
listVariableSets,
|
|
119
|
-
MEMORY_CORRECT_TOOL_DESCRIPTION,
|
|
120
|
-
MEMORY_SAVE_TOOL_DESCRIPTION,
|
|
121
|
-
MEMORY_SEARCH_TOOL_DESCRIPTION,
|
|
122
118
|
requireScheduledTask,
|
|
123
119
|
requireSession,
|
|
124
|
-
searchWorkspaceMemories,
|
|
125
|
-
memoryWriteScopeForAgentScope,
|
|
126
120
|
type MemoryAgentScope,
|
|
127
121
|
serializeEffectiveSessionControl,
|
|
128
122
|
setSessionGoalStatusWithEvent,
|
|
@@ -150,11 +144,7 @@ import {
|
|
|
150
144
|
acceptSessionHumanInputResponse,
|
|
151
145
|
HumanInputResponseValidationError,
|
|
152
146
|
} from "@opengeni/db";
|
|
153
|
-
import {
|
|
154
|
-
appendAndPublishEvents,
|
|
155
|
-
appendAndPublishTurnEventsFenced,
|
|
156
|
-
publishDurableSessionEvents,
|
|
157
|
-
} from "@opengeni/events";
|
|
147
|
+
import { appendAndPublishTurnEventsFenced, publishDurableSessionEvents } from "@opengeni/events";
|
|
158
148
|
import { allowedFirstPartyMcpToolsForSession, codemodeWorkspaceUrl } from "@opengeni/config";
|
|
159
149
|
import {
|
|
160
150
|
createSignedState,
|
|
@@ -170,6 +160,7 @@ import type { AnySchema, ZodRawShapeCompat } from "@modelcontextprotocol/sdk/ser
|
|
|
170
160
|
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
171
161
|
import { HTTPException } from "hono/http-exception";
|
|
172
162
|
import * as z4 from "zod/v4";
|
|
163
|
+
import { registerKnowledgeEntryTools } from "./knowledge-entries";
|
|
173
164
|
import {
|
|
174
165
|
FIRST_PARTY_TOOL_AUTHORIZATION,
|
|
175
166
|
type FirstPartyToolAuthorization,
|
|
@@ -180,7 +171,6 @@ import {
|
|
|
180
171
|
authorizedSocialConnectionsForGrant,
|
|
181
172
|
authorizedAtlassianConnectionsForGrant,
|
|
182
173
|
buildCapabilityCatalog,
|
|
183
|
-
correctWorkspaceMemoryWithSlackPublication,
|
|
184
174
|
nativeConnectionCapabilityRecommendations,
|
|
185
175
|
requireLiveAgentAttemptAuthorization,
|
|
186
176
|
requireSessionAuthorization,
|
|
@@ -188,7 +178,6 @@ import {
|
|
|
188
178
|
resolveWorkspaceCatalogSettings,
|
|
189
179
|
SessionAuthorizationDeniedError,
|
|
190
180
|
SessionAuthorizationUnavailableError,
|
|
191
|
-
saveWorkspaceMemoryWithSlackPublication,
|
|
192
181
|
searchCapabilityCatalogItems,
|
|
193
182
|
type ResolvedSessionAuthorization,
|
|
194
183
|
} from "@opengeni/core";
|
|
@@ -318,9 +307,7 @@ import {
|
|
|
318
307
|
} from "../integrations/atlassian";
|
|
319
308
|
import { AtlassianConnectionMetadata } from "@opengeni/contracts/atlassian";
|
|
320
309
|
import { registerEditableArtifactAgentTools } from "./editable-artifacts";
|
|
321
|
-
import { registerCompanyBrainGovernedWriteTools } from "./company-brain-governed-writes";
|
|
322
310
|
import { registerCompanyProfileAgentAdminTools } from "./company-profile-agent-admin";
|
|
323
|
-
import { registerRememberTools } from "./remember";
|
|
324
311
|
import { mintSandboxCodemodeToken } from "@opengeni/runtime/sandbox";
|
|
325
312
|
import { deleteScheduledTaskWithDurableCleanup } from "../scheduled-task-deletion";
|
|
326
313
|
import { observeWorkDiscovery, summarizeWorkDiscoveryRows } from "../work-discovery-observability";
|
|
@@ -671,22 +658,10 @@ export function buildOpenGeniMcpServer(
|
|
|
671
658
|
if (sessionId !== null) {
|
|
672
659
|
registerGoalTools(server, deps, grant, sessionId, json);
|
|
673
660
|
}
|
|
674
|
-
//
|
|
675
|
-
//
|
|
676
|
-
if (
|
|
677
|
-
|
|
678
|
-
options.workspaceMemoryEnabled === true &&
|
|
679
|
-
options.sessionMemory?.mode !== "off"
|
|
680
|
-
) {
|
|
681
|
-
registerMemoryTools(
|
|
682
|
-
server,
|
|
683
|
-
deps,
|
|
684
|
-
grant,
|
|
685
|
-
sessionId,
|
|
686
|
-
json,
|
|
687
|
-
options.workspaceMemoryPromptMode ?? "retrieval_only",
|
|
688
|
-
options.sessionMemory ?? null,
|
|
689
|
-
);
|
|
661
|
+
// Agent learning controls authoring at the accepted-attempt write boundary.
|
|
662
|
+
// Reading existing Knowledge and Skills remains available when authoring is Off.
|
|
663
|
+
if (sessionId !== null) {
|
|
664
|
+
registerKnowledgeEntryTools(server, deps, grant, sessionId);
|
|
690
665
|
}
|
|
691
666
|
server.registerTool(
|
|
692
667
|
"artifacts_list",
|
|
@@ -713,19 +688,7 @@ export function buildOpenGeniMcpServer(
|
|
|
713
688
|
registerWorkClaimTools(server, deps, grant, sessionId, json);
|
|
714
689
|
}
|
|
715
690
|
const attempt = exactAgentAttemptClaims(grant)!;
|
|
716
|
-
|
|
717
|
-
server,
|
|
718
|
-
db: deps.db,
|
|
719
|
-
attempt: {
|
|
720
|
-
accountId: grant.accountId,
|
|
721
|
-
workspaceId: grant.workspaceId,
|
|
722
|
-
...attempt,
|
|
723
|
-
},
|
|
724
|
-
authorize: async () => {
|
|
725
|
-
await authorizeFirstPartySession(deps, grant, sessionId, "session.first_party_mcp.call");
|
|
726
|
-
},
|
|
727
|
-
json,
|
|
728
|
-
});
|
|
691
|
+
|
|
729
692
|
registerCompanyProfileAgentAdminTools({
|
|
730
693
|
server,
|
|
731
694
|
db: deps.db,
|
|
@@ -740,19 +703,6 @@ export function buildOpenGeniMcpServer(
|
|
|
740
703
|
},
|
|
741
704
|
json,
|
|
742
705
|
});
|
|
743
|
-
registerRememberTools({
|
|
744
|
-
server,
|
|
745
|
-
db: deps.db,
|
|
746
|
-
attempt: {
|
|
747
|
-
accountId: grant.accountId,
|
|
748
|
-
workspaceId: grant.workspaceId,
|
|
749
|
-
...attempt,
|
|
750
|
-
},
|
|
751
|
-
authorize: async () => {
|
|
752
|
-
await authorizeFirstPartySession(deps, grant, sessionId, "session.first_party_mcp.call");
|
|
753
|
-
},
|
|
754
|
-
json,
|
|
755
|
-
});
|
|
756
706
|
}
|
|
757
707
|
if (sessionId !== null && exactAgentAttemptClaims(grant) !== null) {
|
|
758
708
|
registerWorkspaceArtifactTools(server, deps, grant, sessionId, json);
|
|
@@ -3544,9 +3494,6 @@ function registerPreferenceRegistryTools(
|
|
|
3544
3494
|
);
|
|
3545
3495
|
}
|
|
3546
3496
|
|
|
3547
|
-
const MemoryKindSchema = z4.enum(["preference", "semantic", "procedural", "decision", "episodic"]);
|
|
3548
|
-
const MemoryWriteKindSchema = z4.enum(["semantic", "decision", "episodic"]);
|
|
3549
|
-
|
|
3550
3497
|
function scheduledTaskReceipt(
|
|
3551
3498
|
operation: string,
|
|
3552
3499
|
task: ScheduledTask,
|
|
@@ -3637,307 +3584,6 @@ export function memorySlackPublicationActor(
|
|
|
3637
3584
|
};
|
|
3638
3585
|
}
|
|
3639
3586
|
|
|
3640
|
-
function memoryPreview(text: string): string {
|
|
3641
|
-
const normalized = text.replace(/\s+/g, " ").trim();
|
|
3642
|
-
return normalized.length <= 120 ? normalized : `${normalized.slice(0, 119)}…`;
|
|
3643
|
-
}
|
|
3644
|
-
|
|
3645
|
-
function registerMemoryTools(
|
|
3646
|
-
server: McpServer,
|
|
3647
|
-
deps: ApiRouteDeps,
|
|
3648
|
-
grant: AccessGrant,
|
|
3649
|
-
sessionId: string,
|
|
3650
|
-
json: JsonResult,
|
|
3651
|
-
promptMode: WorkspaceMemoryPromptMode,
|
|
3652
|
-
sessionMemory: MemoryAgentScope | null,
|
|
3653
|
-
): void {
|
|
3654
|
-
// Read layers: workspace plus the session's private layer. Write layer: the
|
|
3655
|
-
// session's narrowest layer. A null scope is the pre-0426 workspace path.
|
|
3656
|
-
const agentScope: MemoryAgentScope = sessionMemory ?? {
|
|
3657
|
-
mode: "workspace",
|
|
3658
|
-
userSubjectId: null,
|
|
3659
|
-
rootSessionId: null,
|
|
3660
|
-
};
|
|
3661
|
-
const writeScope = memoryWriteScopeForAgentScope(agentScope);
|
|
3662
|
-
if (!writeScope) return;
|
|
3663
|
-
const publicationInputSchema = z4.object({
|
|
3664
|
-
importance: z4.enum(["major", "normal", "minor"]),
|
|
3665
|
-
audience: z4.literal("workspace"),
|
|
3666
|
-
slackMode: z4.enum(["auto", "review", "never"]),
|
|
3667
|
-
shareSummary: z4.string().trim().min(1).max(4_096),
|
|
3668
|
-
});
|
|
3669
|
-
|
|
3670
|
-
server.registerTool(
|
|
3671
|
-
"memory_search",
|
|
3672
|
-
{
|
|
3673
|
-
description: `${MEMORY_SEARCH_TOOL_DESCRIPTION} All existing Memory kinds are searchable. Legacy preference and procedure records are historical context, not active instructions; Skills and workspace instructions remain the behavioral authorities. When workspace Memory is enabled, use memory_save autonomously for durable facts, decisions, incidents, fixes, and outcomes, and memory_correct when an existing record is wrong or outdated.`,
|
|
3674
|
-
inputSchema: {
|
|
3675
|
-
query: z4.string().min(1),
|
|
3676
|
-
kind: MemoryKindSchema.optional(),
|
|
3677
|
-
limit: z4.number().int().positive().max(20).optional(),
|
|
3678
|
-
},
|
|
3679
|
-
},
|
|
3680
|
-
async ({ query, kind, limit }) =>
|
|
3681
|
-
json({
|
|
3682
|
-
results: await searchWorkspaceMemories(
|
|
3683
|
-
deps.db,
|
|
3684
|
-
grant.workspaceId,
|
|
3685
|
-
{
|
|
3686
|
-
query,
|
|
3687
|
-
...(kind ? { kind } : {}),
|
|
3688
|
-
...(limit ? { limit } : {}),
|
|
3689
|
-
agentPromptMode: promptMode,
|
|
3690
|
-
agentScope,
|
|
3691
|
-
},
|
|
3692
|
-
deps.getDocumentServices().embedder,
|
|
3693
|
-
),
|
|
3694
|
-
}),
|
|
3695
|
-
);
|
|
3696
|
-
|
|
3697
|
-
// Memory writes are agent-only. Human creation and curation use the REST/UI
|
|
3698
|
-
// surface; a non-attempt MCP principal may search but cannot mutate Memory.
|
|
3699
|
-
if (exactAgentAttemptClaims(grant) === null) return;
|
|
3700
|
-
|
|
3701
|
-
server.registerTool(
|
|
3702
|
-
"memory_save",
|
|
3703
|
-
{
|
|
3704
|
-
description: MEMORY_SAVE_TOOL_DESCRIPTION,
|
|
3705
|
-
inputSchema: {
|
|
3706
|
-
text: z4.string().min(1),
|
|
3707
|
-
kind: MemoryWriteKindSchema,
|
|
3708
|
-
confidence: z4.number().min(0).max(1).optional(),
|
|
3709
|
-
replaces_id: z4.string().min(1).optional(),
|
|
3710
|
-
slack_publication: publicationInputSchema.optional(),
|
|
3711
|
-
},
|
|
3712
|
-
},
|
|
3713
|
-
async ({ text, kind, confidence, replaces_id, slack_publication }) => {
|
|
3714
|
-
const actor = await requireLiveAgentAttemptAuthorization(deps.db, grant, sessionId);
|
|
3715
|
-
const result = await saveWorkspaceMemoryWithSlackPublication(
|
|
3716
|
-
deps.db,
|
|
3717
|
-
{
|
|
3718
|
-
accountId: grant.accountId,
|
|
3719
|
-
workspaceId: grant.workspaceId,
|
|
3720
|
-
sessionId,
|
|
3721
|
-
text,
|
|
3722
|
-
kind,
|
|
3723
|
-
...(confidence !== undefined ? { confidence } : {}),
|
|
3724
|
-
...(replaces_id ? { replacesId: replaces_id } : {}),
|
|
3725
|
-
origin: "agent",
|
|
3726
|
-
scope: writeScope,
|
|
3727
|
-
},
|
|
3728
|
-
slack_publication
|
|
3729
|
-
? {
|
|
3730
|
-
distribution: MemorySlackPublicationDistribution.parse(slack_publication),
|
|
3731
|
-
actor: memorySlackPublicationActor(actor, sessionId, grant.subjectLabel ?? null)
|
|
3732
|
-
.actor,
|
|
3733
|
-
ownerLabel: actor.initiator.label ?? grant.subjectLabel ?? null,
|
|
3734
|
-
}
|
|
3735
|
-
: null,
|
|
3736
|
-
deps.getDocumentServices().embedder,
|
|
3737
|
-
);
|
|
3738
|
-
let timelineWarning: string | null = null;
|
|
3739
|
-
try {
|
|
3740
|
-
await appendAndPublishEvents(deps.db, deps.bus, grant.workspaceId, sessionId, [
|
|
3741
|
-
{
|
|
3742
|
-
type: "memory.saved",
|
|
3743
|
-
payload: {
|
|
3744
|
-
memoryId: result.memory.id,
|
|
3745
|
-
kind: result.memory.kind,
|
|
3746
|
-
preview: memoryPreview(result.memory.text),
|
|
3747
|
-
deduped: result.deduped,
|
|
3748
|
-
...(result.superseded ? { supersededMemoryId: result.superseded.id } : {}),
|
|
3749
|
-
},
|
|
3750
|
-
},
|
|
3751
|
-
]);
|
|
3752
|
-
} catch {
|
|
3753
|
-
timelineWarning = "Memory committed, but its session timeline event could not be recorded.";
|
|
3754
|
-
console.warn("workspace memory save: committed without session timeline event", {
|
|
3755
|
-
errorClass: "MemoryTimelineOperationError",
|
|
3756
|
-
errorCode: "memory_save_timeline_append_failed",
|
|
3757
|
-
origin: "api",
|
|
3758
|
-
workspaceId: grant.workspaceId,
|
|
3759
|
-
sessionId,
|
|
3760
|
-
memoryId: result.memory.id,
|
|
3761
|
-
});
|
|
3762
|
-
}
|
|
3763
|
-
const changed = !result.deduped || result.updated || result.superseded !== null;
|
|
3764
|
-
const outcome =
|
|
3765
|
-
result.updated || result.superseded !== null
|
|
3766
|
-
? "updated"
|
|
3767
|
-
: result.deduped
|
|
3768
|
-
? "unchanged"
|
|
3769
|
-
: "created";
|
|
3770
|
-
return json(
|
|
3771
|
-
mcpMutationReceipt({
|
|
3772
|
-
operation: "memory_save",
|
|
3773
|
-
committed: true,
|
|
3774
|
-
outcome,
|
|
3775
|
-
changed,
|
|
3776
|
-
resource: {
|
|
3777
|
-
type: "knowledge_memory",
|
|
3778
|
-
id: result.memory.id,
|
|
3779
|
-
state: result.memory.status,
|
|
3780
|
-
},
|
|
3781
|
-
relatedResources: result.superseded
|
|
3782
|
-
? [
|
|
3783
|
-
{
|
|
3784
|
-
type: "knowledge_memory",
|
|
3785
|
-
id: result.superseded.id,
|
|
3786
|
-
state: result.superseded.status,
|
|
3787
|
-
},
|
|
3788
|
-
]
|
|
3789
|
-
: undefined,
|
|
3790
|
-
timestamp: result.memory.updatedAt,
|
|
3791
|
-
idempotency: { status: "not_supported" },
|
|
3792
|
-
warnings: [
|
|
3793
|
-
...(!result.embedded
|
|
3794
|
-
? ["Memory committed without a vector embedding; keyword search remains available."]
|
|
3795
|
-
: []),
|
|
3796
|
-
...(timelineWarning ? [timelineWarning] : []),
|
|
3797
|
-
],
|
|
3798
|
-
facts: {
|
|
3799
|
-
deduped: result.deduped,
|
|
3800
|
-
dedupeReason: result.dedupeReason,
|
|
3801
|
-
updatedInPlace: result.updated,
|
|
3802
|
-
embedded: result.embedded,
|
|
3803
|
-
slackPublicationDecision: result.slackPublication.decision?.eligible
|
|
3804
|
-
? "eligible"
|
|
3805
|
-
: (result.slackPublication.decision?.reason ?? "not_requested"),
|
|
3806
|
-
slackPublicationId:
|
|
3807
|
-
result.slackPublication.enqueue?.kind === "enqueued" ||
|
|
3808
|
-
result.slackPublication.enqueue?.kind === "replayed"
|
|
3809
|
-
? result.slackPublication.enqueue.publication.id
|
|
3810
|
-
: null,
|
|
3811
|
-
slackPublicationState:
|
|
3812
|
-
result.slackPublication.enqueue?.kind === "enqueued" ||
|
|
3813
|
-
result.slackPublication.enqueue?.kind === "replayed"
|
|
3814
|
-
? result.slackPublication.enqueue.publication.state
|
|
3815
|
-
: null,
|
|
3816
|
-
},
|
|
3817
|
-
}),
|
|
3818
|
-
);
|
|
3819
|
-
},
|
|
3820
|
-
);
|
|
3821
|
-
|
|
3822
|
-
server.registerTool(
|
|
3823
|
-
"memory_correct",
|
|
3824
|
-
{
|
|
3825
|
-
description: MEMORY_CORRECT_TOOL_DESCRIPTION,
|
|
3826
|
-
inputSchema: {
|
|
3827
|
-
id: z4.string().min(1),
|
|
3828
|
-
reason: z4.string().min(1).optional(),
|
|
3829
|
-
replacement_text: z4.string().min(1).optional(),
|
|
3830
|
-
slack_publication: publicationInputSchema.optional(),
|
|
3831
|
-
},
|
|
3832
|
-
},
|
|
3833
|
-
async ({ id, reason, replacement_text, slack_publication }) => {
|
|
3834
|
-
const actor = await requireLiveAgentAttemptAuthorization(deps.db, grant, sessionId);
|
|
3835
|
-
const result = await correctWorkspaceMemoryWithSlackPublication(
|
|
3836
|
-
deps.db,
|
|
3837
|
-
{
|
|
3838
|
-
accountId: grant.accountId,
|
|
3839
|
-
workspaceId: grant.workspaceId,
|
|
3840
|
-
sessionId,
|
|
3841
|
-
id,
|
|
3842
|
-
...(reason ? { reason } : {}),
|
|
3843
|
-
...(replacement_text ? { replacementText: replacement_text } : {}),
|
|
3844
|
-
origin: "agent",
|
|
3845
|
-
agentScope,
|
|
3846
|
-
},
|
|
3847
|
-
slack_publication
|
|
3848
|
-
? {
|
|
3849
|
-
distribution: MemorySlackPublicationDistribution.parse(slack_publication),
|
|
3850
|
-
actor: memorySlackPublicationActor(actor, sessionId, grant.subjectLabel ?? null)
|
|
3851
|
-
.actor,
|
|
3852
|
-
ownerLabel: actor.initiator.label ?? grant.subjectLabel ?? null,
|
|
3853
|
-
}
|
|
3854
|
-
: null,
|
|
3855
|
-
deps.getDocumentServices().embedder,
|
|
3856
|
-
);
|
|
3857
|
-
let timelineWarning: string | null = null;
|
|
3858
|
-
try {
|
|
3859
|
-
await appendAndPublishEvents(deps.db, deps.bus, grant.workspaceId, sessionId, [
|
|
3860
|
-
{
|
|
3861
|
-
type: "memory.corrected",
|
|
3862
|
-
payload: {
|
|
3863
|
-
memoryId: result.memory.id,
|
|
3864
|
-
kind: result.memory.kind,
|
|
3865
|
-
preview: memoryPreview(result.memory.text),
|
|
3866
|
-
action: result.action,
|
|
3867
|
-
...(reason ? { reason: memoryPreview(reason) } : {}),
|
|
3868
|
-
...(result.replacement
|
|
3869
|
-
? {
|
|
3870
|
-
replacementMemoryId: result.replacement.id,
|
|
3871
|
-
replacementPreview: memoryPreview(result.replacement.text),
|
|
3872
|
-
}
|
|
3873
|
-
: {}),
|
|
3874
|
-
},
|
|
3875
|
-
},
|
|
3876
|
-
]);
|
|
3877
|
-
} catch {
|
|
3878
|
-
timelineWarning =
|
|
3879
|
-
"Memory correction committed, but its session timeline event could not be recorded.";
|
|
3880
|
-
console.warn("workspace memory correction: committed without session timeline event", {
|
|
3881
|
-
errorClass: "MemoryTimelineOperationError",
|
|
3882
|
-
errorCode: "memory_correct_timeline_append_failed",
|
|
3883
|
-
origin: "api",
|
|
3884
|
-
workspaceId: grant.workspaceId,
|
|
3885
|
-
sessionId,
|
|
3886
|
-
memoryId: result.memory.id,
|
|
3887
|
-
});
|
|
3888
|
-
}
|
|
3889
|
-
return json(
|
|
3890
|
-
mcpMutationReceipt({
|
|
3891
|
-
operation: "memory_correct",
|
|
3892
|
-
committed: true,
|
|
3893
|
-
outcome: "updated",
|
|
3894
|
-
changed: true,
|
|
3895
|
-
resource: {
|
|
3896
|
-
type: "knowledge_memory",
|
|
3897
|
-
id: result.memory.id,
|
|
3898
|
-
state: result.memory.status,
|
|
3899
|
-
},
|
|
3900
|
-
relatedResources: result.replacement
|
|
3901
|
-
? [
|
|
3902
|
-
{
|
|
3903
|
-
type: "knowledge_memory",
|
|
3904
|
-
id: result.replacement.id,
|
|
3905
|
-
state: result.replacement.status,
|
|
3906
|
-
},
|
|
3907
|
-
]
|
|
3908
|
-
: undefined,
|
|
3909
|
-
timestamp: (result.replacement ?? result.memory).updatedAt,
|
|
3910
|
-
idempotency: { status: "not_supported" },
|
|
3911
|
-
warnings: timelineWarning ? [timelineWarning] : [],
|
|
3912
|
-
facts: {
|
|
3913
|
-
correctionAction: result.action,
|
|
3914
|
-
slackPublicationDecision: result.slackPublication.decision?.eligible
|
|
3915
|
-
? "eligible"
|
|
3916
|
-
: (result.slackPublication.decision?.reason ?? "not_requested"),
|
|
3917
|
-
slackPublicationId:
|
|
3918
|
-
result.slackPublication.enqueue?.kind === "enqueued" ||
|
|
3919
|
-
result.slackPublication.enqueue?.kind === "replayed"
|
|
3920
|
-
? result.slackPublication.enqueue.publication.id
|
|
3921
|
-
: null,
|
|
3922
|
-
slackPublicationState:
|
|
3923
|
-
result.slackPublication.enqueue?.kind === "enqueued" ||
|
|
3924
|
-
result.slackPublication.enqueue?.kind === "replayed"
|
|
3925
|
-
? result.slackPublication.enqueue.publication.state
|
|
3926
|
-
: null,
|
|
3927
|
-
},
|
|
3928
|
-
}),
|
|
3929
|
-
);
|
|
3930
|
-
},
|
|
3931
|
-
);
|
|
3932
|
-
}
|
|
3933
|
-
|
|
3934
|
-
// Fleet tools (M7 bring-your-own-compute). Session-scoped (they steer THIS
|
|
3935
|
-
// session's active-sandbox pointer + reach the workspace's enrolled machines),
|
|
3936
|
-
// registered only with the worker-signed sessionId claim + the selfhosted flag.
|
|
3937
|
-
// The agent uses these to list the fleet (its Modal box + enrolled machines),
|
|
3938
|
-
// attach/swap the active sandbox mid-conversation (heterogeneous, single-active,
|
|
3939
|
-
// epoch-fenced), run a one-off op on a specific machine without swapping, and
|
|
3940
|
-
// surface provisioning (enroll-a-machine) instructions to a human.
|
|
3941
3587
|
function registerFleetTools(
|
|
3942
3588
|
server: McpServer,
|
|
3943
3589
|
deps: ApiRouteDeps,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withAccessGrantSessionRlsContext } from "../access-grant-rls";
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
2
3
|
import {
|
|
3
4
|
COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS,
|
|
@@ -6,15 +7,17 @@ import {
|
|
|
6
7
|
CompanyBrainContextReceiptPage,
|
|
7
8
|
CompanyBrainKnowledgeProposalPage,
|
|
8
9
|
CompanyBrainOkfPackage,
|
|
9
|
-
|
|
10
|
-
KnowledgeBrowseResponse,
|
|
11
|
-
KnowledgeGetResponse,
|
|
12
|
-
KnowledgeRecordId,
|
|
13
|
-
KnowledgeSearchRequest,
|
|
14
|
-
KnowledgeSearchResponse,
|
|
10
|
+
WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT,
|
|
15
11
|
type WorkspaceInstructionPolicyRevision,
|
|
16
12
|
} from "@opengeni/contracts";
|
|
17
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
hasPermission,
|
|
15
|
+
requireAccessGrant,
|
|
16
|
+
requireAccessGrantAuthorization,
|
|
17
|
+
knowledgeContextForAccess,
|
|
18
|
+
type AccessGrantAuthorization,
|
|
19
|
+
type ApiRouteDeps,
|
|
20
|
+
} from "@opengeni/core";
|
|
18
21
|
import {
|
|
19
22
|
getCurrentPreferenceRegistryGovernanceMetadata,
|
|
20
23
|
getWorkspace,
|
|
@@ -25,14 +28,8 @@ import {
|
|
|
25
28
|
listCompanyBrainPreferenceGuidance,
|
|
26
29
|
listCompanyProfile,
|
|
27
30
|
listWorkspaceInstructionPolicyRevisions,
|
|
28
|
-
|
|
31
|
+
listKnowledgeEntries,
|
|
29
32
|
} from "@opengeni/db";
|
|
30
|
-
import {
|
|
31
|
-
browseEffectiveKnowledge,
|
|
32
|
-
getDocumentInventory,
|
|
33
|
-
getEffectiveKnowledgeRecord,
|
|
34
|
-
searchEffectiveKnowledge,
|
|
35
|
-
} from "@opengeni/documents";
|
|
36
33
|
import type { Hono } from "hono";
|
|
37
34
|
import { HTTPException } from "hono/http-exception";
|
|
38
35
|
import { z } from "zod";
|
|
@@ -40,10 +37,6 @@ import { z } from "zod";
|
|
|
40
37
|
import { createCompanyBrainOkfPackage, serializeCompanyBrainOkf } from "../company-brain-okf";
|
|
41
38
|
import { projectWorkspaceState } from "../workspace-state-projection";
|
|
42
39
|
|
|
43
|
-
const BASE_LIMIT = 24;
|
|
44
|
-
const TOPIC_LIMIT = 24;
|
|
45
|
-
const TOPIC_MAX_CHARS = 96;
|
|
46
|
-
|
|
47
40
|
const inspectorQuery = z
|
|
48
41
|
.object({
|
|
49
42
|
attemptId: z.string().uuid().optional(),
|
|
@@ -105,26 +98,6 @@ function decodeReceiptCursor(value: string, workspaceId: string, subjectId: stri
|
|
|
105
98
|
}
|
|
106
99
|
}
|
|
107
100
|
|
|
108
|
-
async function parsedJson<T>(
|
|
109
|
-
context: { req: { json: () => Promise<unknown> } },
|
|
110
|
-
schema: z.ZodType<T>,
|
|
111
|
-
) {
|
|
112
|
-
const result = schema.safeParse(await context.req.json().catch(() => null));
|
|
113
|
-
if (!result.success) throw new HTTPException(400, { message: "invalid Company Brain request" });
|
|
114
|
-
return result.data;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function knowledgeHttpError(error: unknown): never {
|
|
118
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
-
if (message.includes("belongs to a different scope")) {
|
|
120
|
-
throw new HTTPException(409, { message });
|
|
121
|
-
}
|
|
122
|
-
if (message.includes("invalid knowledge") || message.includes("knowledge browse")) {
|
|
123
|
-
throw new HTTPException(400, { message });
|
|
124
|
-
}
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
101
|
async function readCompanyBrainPackage(
|
|
129
102
|
deps: ApiRouteDeps,
|
|
130
103
|
input: {
|
|
@@ -132,6 +105,7 @@ async function readCompanyBrainPackage(
|
|
|
132
105
|
accountId: string;
|
|
133
106
|
subjectId: string;
|
|
134
107
|
canInspectKnowledge: boolean;
|
|
108
|
+
access: AccessGrantAuthorization;
|
|
135
109
|
},
|
|
136
110
|
) {
|
|
137
111
|
const generatedAt = new Date().toISOString();
|
|
@@ -159,15 +133,13 @@ async function readCompanyBrainPackage(
|
|
|
159
133
|
subjectId: input.subjectId,
|
|
160
134
|
}),
|
|
161
135
|
input.canInspectKnowledge
|
|
162
|
-
?
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
listWorkspaceStateMemoryRecords(deps.db, input.workspaceId),
|
|
170
|
-
]).then(([documents, memories]) => ({ documents, memories }))
|
|
136
|
+
? withAccessGrantSessionRlsContext(deps, input.access.grant, async () =>
|
|
137
|
+
listKnowledgeEntries(
|
|
138
|
+
deps.db,
|
|
139
|
+
await knowledgeContextForAccess(deps, input.access, "documents:search"),
|
|
140
|
+
{ limit: WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT, view: "published" },
|
|
141
|
+
),
|
|
142
|
+
)
|
|
171
143
|
: Promise.resolve(null),
|
|
172
144
|
]);
|
|
173
145
|
if (!workspace) throw new HTTPException(404, { message: "workspace not found" });
|
|
@@ -223,12 +195,19 @@ export function registerCompanyBrainRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
223
195
|
|
|
224
196
|
app.get(base, async (context) => {
|
|
225
197
|
const workspaceId = context.req.param("workspaceId");
|
|
226
|
-
const
|
|
198
|
+
const access = await requireAccessGrantAuthorization(
|
|
199
|
+
context,
|
|
200
|
+
deps,
|
|
201
|
+
workspaceId,
|
|
202
|
+
"workspace:read",
|
|
203
|
+
);
|
|
204
|
+
const { grant } = access;
|
|
227
205
|
const result = await readCompanyBrainPackage(deps, {
|
|
228
206
|
workspaceId,
|
|
229
207
|
accountId: grant.accountId,
|
|
230
208
|
subjectId: grant.subjectId,
|
|
231
209
|
canInspectKnowledge: hasPermission(grant.permissions, "documents:search"),
|
|
210
|
+
access,
|
|
232
211
|
});
|
|
233
212
|
context.header("cache-control", "private, no-store");
|
|
234
213
|
return context.json(CompanyBrainOkfPackage.parse(result));
|
|
@@ -236,12 +215,19 @@ export function registerCompanyBrainRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
236
215
|
|
|
237
216
|
app.get(`${base}/export`, async (context) => {
|
|
238
217
|
const workspaceId = context.req.param("workspaceId");
|
|
239
|
-
const
|
|
218
|
+
const access = await requireAccessGrantAuthorization(
|
|
219
|
+
context,
|
|
220
|
+
deps,
|
|
221
|
+
workspaceId,
|
|
222
|
+
"workspace:read",
|
|
223
|
+
);
|
|
224
|
+
const { grant } = access;
|
|
240
225
|
const result = await readCompanyBrainPackage(deps, {
|
|
241
226
|
workspaceId,
|
|
242
227
|
accountId: grant.accountId,
|
|
243
228
|
subjectId: grant.subjectId,
|
|
244
229
|
canInspectKnowledge: hasPermission(grant.permissions, "documents:search"),
|
|
230
|
+
access,
|
|
245
231
|
});
|
|
246
232
|
context.header("cache-control", "private, no-store");
|
|
247
233
|
context.header("content-type", "text/markdown; charset=utf-8");
|
|
@@ -252,69 +238,34 @@ export function registerCompanyBrainRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
252
238
|
return context.body(serializeCompanyBrainOkf(result));
|
|
253
239
|
});
|
|
254
240
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
surface: "human",
|
|
267
|
-
...request,
|
|
268
|
-
},
|
|
269
|
-
deps.getDocumentServices(),
|
|
241
|
+
for (const path of [
|
|
242
|
+
`${base}/knowledge/search`,
|
|
243
|
+
`${base}/knowledge/record`,
|
|
244
|
+
`${base}/knowledge/browse`,
|
|
245
|
+
]) {
|
|
246
|
+
app.all(path, async (context) => {
|
|
247
|
+
await requireAccessGrant(
|
|
248
|
+
context,
|
|
249
|
+
deps,
|
|
250
|
+
context.req.param("workspaceId")!,
|
|
251
|
+
"documents:search",
|
|
270
252
|
);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return knowledgeHttpError(error);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
app.get(`${base}/knowledge/record`, async (context) => {
|
|
279
|
-
const workspaceId = context.req.param("workspaceId");
|
|
280
|
-
const grant = await requireAccessGrant(context, deps, workspaceId, "documents:search");
|
|
281
|
-
const parsedId = KnowledgeRecordId.safeParse(context.req.query("id"));
|
|
282
|
-
if (!parsedId.success)
|
|
283
|
-
throw new HTTPException(400, { message: "knowledge record id is invalid" });
|
|
284
|
-
const record = await getEffectiveKnowledgeRecord(deps.db, {
|
|
285
|
-
accountId: grant.accountId,
|
|
286
|
-
workspaceId,
|
|
287
|
-
initiatingSubjectId: grant.subjectId,
|
|
288
|
-
surface: "human",
|
|
289
|
-
id: parsedId.data,
|
|
290
|
-
});
|
|
291
|
-
if (!record) throw new HTTPException(404, { message: "knowledge record not found" });
|
|
292
|
-
context.header("cache-control", "private, no-store");
|
|
293
|
-
return context.json(KnowledgeGetResponse.parse({ record }));
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
app.post(`${base}/knowledge/browse`, async (context) => {
|
|
297
|
-
const workspaceId = context.req.param("workspaceId");
|
|
298
|
-
const grant = await requireAccessGrant(context, deps, workspaceId, "documents:search");
|
|
299
|
-
const request = await parsedJson(context, KnowledgeBrowseRequest);
|
|
300
|
-
try {
|
|
301
|
-
const response = await browseEffectiveKnowledge(deps.db, {
|
|
302
|
-
accountId: grant.accountId,
|
|
303
|
-
workspaceId,
|
|
304
|
-
initiatingSubjectId: grant.subjectId,
|
|
305
|
-
surface: "human",
|
|
306
|
-
...request,
|
|
253
|
+
throw new HTTPException(410, {
|
|
254
|
+
message:
|
|
255
|
+
"Knowledge retrieval moved to /knowledge/entries. Use canonical entries, revisions and groups.",
|
|
307
256
|
});
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
} catch (error) {
|
|
311
|
-
return knowledgeHttpError(error);
|
|
312
|
-
}
|
|
313
|
-
});
|
|
257
|
+
});
|
|
258
|
+
}
|
|
314
259
|
|
|
315
260
|
app.get(`${base}/context-receipts`, async (context) => {
|
|
316
261
|
const workspaceId = context.req.param("workspaceId");
|
|
317
|
-
const
|
|
262
|
+
const access = await requireAccessGrantAuthorization(
|
|
263
|
+
context,
|
|
264
|
+
deps,
|
|
265
|
+
workspaceId,
|
|
266
|
+
"workspace:read",
|
|
267
|
+
);
|
|
268
|
+
const { grant } = access;
|
|
318
269
|
const parsed = inspectorQuery.safeParse(context.req.query());
|
|
319
270
|
if (!parsed.success) throw new HTTPException(400, { message: "invalid context receipt query" });
|
|
320
271
|
const before = parsed.data.cursor
|