@opengeni/api-router 2.5.0 → 2.6.4-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.d.ts +2 -1
- package/dist/app.js +3 -1
- package/dist/auth/managed-auth-attempt-context.d.ts +5 -1
- package/dist/auth/managed-auth.d.ts +24 -1
- package/dist/{chunk-QESX7HDK.js → chunk-UOI7KAP3.js} +3240 -791
- package/dist/chunk-UOI7KAP3.js.map +1 -0
- package/dist/http/sse.d.ts +9 -0
- package/dist/index.js +117 -5
- package/dist/index.js.map +1 -1
- package/dist/interaction-metrics.d.ts +2 -0
- package/dist/mcp/company-brain-governed-writes.d.ts +1 -1
- package/dist/mcp/company-profile-agent-admin.d.ts +6 -6
- package/dist/mcp/remember.d.ts +2 -2
- package/dist/mcp/scheduled-task-view.d.ts +3 -3
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/session-view.d.ts +1 -0
- package/dist/mcp/session-wait.d.ts +19 -0
- package/dist/model-catalog.d.ts +5 -31
- package/dist/routes/api-keys.d.ts +2 -0
- package/dist/routes/browser-sessions.d.ts +1 -0
- package/dist/routes/computer-sessions.d.ts +12 -0
- package/dist/routes/managed-auth-session-sets.d.ts +7 -0
- package/dist/routes/workspaces.d.ts +1 -1
- package/dist/sandbox/metrics-ingestion.d.ts +16 -0
- package/dist/workspace-delete-observability.d.ts +10 -0
- package/package.json +19 -19
- package/src/app.ts +206 -29
- package/src/auth/managed-auth-attempt-context.ts +40 -3
- package/src/auth/managed-auth-session-adapter.ts +1 -0
- package/src/auth/managed-auth.ts +164 -4
- package/src/http/sse.ts +279 -45
- package/src/index.ts +13 -1
- package/src/integrations/oauth-client.ts +8 -1
- package/src/integrations/provider-oauth.ts +12 -2
- package/src/integrations/slack-interactions.ts +77 -37
- package/src/interaction-metrics.ts +30 -0
- package/src/mcp/company-brain-governed-writes.ts +38 -23
- package/src/mcp/company-profile-agent-admin.ts +7 -7
- package/src/mcp/remember.ts +19 -8
- package/src/mcp/server.ts +328 -27
- package/src/mcp/session-wait.ts +56 -8
- package/src/model-catalog.ts +45 -337
- package/src/routes/api-integrations.ts +2 -2
- package/src/routes/api-keys.ts +149 -7
- package/src/routes/automations.ts +186 -19
- package/src/routes/browser-sessions.ts +10 -2
- package/src/routes/capabilities.ts +3 -3
- package/src/routes/codex.ts +483 -74
- package/src/routes/company-profile.ts +64 -0
- package/src/routes/computer-sessions.ts +103 -1
- package/src/routes/connections.ts +271 -16
- package/src/routes/github.ts +0 -8
- package/src/routes/integration-facets.ts +8 -5
- package/src/routes/interaction-resources.ts +7 -1
- package/src/routes/managed-auth-session-sets.ts +199 -2
- package/src/routes/organization-memberships.ts +28 -8
- package/src/routes/packs.ts +5 -5
- package/src/routes/plugins.ts +2 -2
- package/src/routes/pr-review.ts +80 -4
- package/src/routes/scheduled-tasks.ts +47 -11
- package/src/routes/sessions.ts +75 -53
- package/src/routes/skills.ts +3 -3
- package/src/routes/supergrok.ts +3 -2
- package/src/routes/workspaces.ts +665 -56
- package/src/sandbox/channel-a.ts +10 -4
- package/src/sandbox/machines.ts +13 -6
- package/src/sandbox/metrics-ingestion.ts +157 -3
- package/src/sandbox/viewer.ts +20 -1
- package/src/workspace-delete-observability.ts +75 -0
- package/dist/chunk-QESX7HDK.js.map +0 -1
|
@@ -123,6 +123,7 @@ import {
|
|
|
123
123
|
requireAccessContext,
|
|
124
124
|
requireAccessGrant,
|
|
125
125
|
requireSessionAuthorizationListScope,
|
|
126
|
+
resolveWorkspaceCatalogSettings,
|
|
126
127
|
type ApiRouteDeps,
|
|
127
128
|
} from "@opengeni/core";
|
|
128
129
|
import { publishDurableSessionEvents } from "@opengeni/events";
|
|
@@ -1058,6 +1059,23 @@ export function registerSlackInteractionRoutes(app: Hono, deps: ApiRouteDeps): v
|
|
|
1058
1059
|
});
|
|
1059
1060
|
}
|
|
1060
1061
|
|
|
1062
|
+
async function withCatalogSettings(
|
|
1063
|
+
deps: ApiRouteDeps,
|
|
1064
|
+
grant: Pick<AccessGrant, "accountId" | "workspaceId">,
|
|
1065
|
+
): Promise<ApiRouteDeps> {
|
|
1066
|
+
const catalogSourceSettings = deps.catalogSourceSettings ?? deps.settings;
|
|
1067
|
+
return {
|
|
1068
|
+
...deps,
|
|
1069
|
+
catalogSourceSettings,
|
|
1070
|
+
settings: (
|
|
1071
|
+
await resolveWorkspaceCatalogSettings(deps.db, catalogSourceSettings, {
|
|
1072
|
+
accountId: grant.accountId,
|
|
1073
|
+
workspaceId: grant.workspaceId,
|
|
1074
|
+
})
|
|
1075
|
+
).settings,
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1061
1079
|
async function publishSlackAppHome(
|
|
1062
1080
|
deps: ApiRouteDeps,
|
|
1063
1081
|
installation: SlackInstallationRoute,
|
|
@@ -2152,17 +2170,22 @@ async function processSlackInboxEntry(deps: ApiRouteDeps, entry: SlackInteractio
|
|
|
2152
2170
|
preparedAttachments,
|
|
2153
2171
|
preparedModelContext,
|
|
2154
2172
|
);
|
|
2155
|
-
session = await createSessionForRequest(
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2173
|
+
session = await createSessionForRequest(
|
|
2174
|
+
await withCatalogSettings(deps, grant),
|
|
2175
|
+
grant,
|
|
2176
|
+
interaction.workspaceId,
|
|
2177
|
+
{
|
|
2178
|
+
requestedSessionId: interaction.sessionReservationId,
|
|
2179
|
+
initialMessage: prepared.entry.text,
|
|
2180
|
+
...(prepared.modelContext ? { modelContext: prepared.modelContext } : {}),
|
|
2181
|
+
instructions: SLACK_SESSION_INSTRUCTIONS,
|
|
2182
|
+
firstPartyMcpTools: slackTaskFirstPartyMcpTools(deps.settings),
|
|
2183
|
+
resources: preparedAttachments.resources,
|
|
2184
|
+
...(preferredModel ? { model: preferredModel } : {}),
|
|
2185
|
+
idempotencyKey: `slack:${entry.connectionId}:${entry.providerEventId}`,
|
|
2186
|
+
clientEventId: `slack:${entry.providerEventId}`,
|
|
2187
|
+
},
|
|
2188
|
+
);
|
|
2166
2189
|
} catch (error) {
|
|
2167
2190
|
if (error instanceof HTTPException) {
|
|
2168
2191
|
await client.postMessage({
|
|
@@ -2741,21 +2764,26 @@ async function processSlackReactionInboxEntry(
|
|
|
2741
2764
|
const preparedEntry = slackReactionPreparedEntry(entry, context, preparedTask);
|
|
2742
2765
|
let session: Awaited<ReturnType<typeof createSessionForRequest>>;
|
|
2743
2766
|
try {
|
|
2744
|
-
session = await createSessionForRequest(
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2767
|
+
session = await createSessionForRequest(
|
|
2768
|
+
await withCatalogSettings(deps, grant),
|
|
2769
|
+
grant,
|
|
2770
|
+
interaction.workspaceId,
|
|
2771
|
+
{
|
|
2772
|
+
requestedSessionId: interaction.sessionReservationId,
|
|
2773
|
+
initialMessage: preparedEntry.text,
|
|
2774
|
+
instructions: SLACK_SESSION_INSTRUCTIONS,
|
|
2775
|
+
// The exact reacted message and bounded containing thread are already in
|
|
2776
|
+
// the prompt; do not expose general Slack history tools for this trigger.
|
|
2777
|
+
firstPartyMcpTools: resolveFirstPartyMcpToolPolicy(deps.settings).default,
|
|
2778
|
+
resources: preparedTask.resources,
|
|
2779
|
+
...(preferredModel ? { model: preferredModel } : {}),
|
|
2780
|
+
// Every reaction entry converging on this route must use the same create
|
|
2781
|
+
// key. This closes the same-owner multi-event race while the owner check
|
|
2782
|
+
// above prevents a different subject from winning creation authority.
|
|
2783
|
+
idempotencyKey: `slack-interaction:${interaction.id}`,
|
|
2784
|
+
clientEventId: `slack:${entry.providerEventId}`,
|
|
2785
|
+
},
|
|
2786
|
+
);
|
|
2759
2787
|
// The route-wide create key converges every replica on one reserved
|
|
2760
2788
|
// session, but its first writer's initial message is the only event created
|
|
2761
2789
|
// by that operation. Replay this exact Slack event through the normal
|
|
@@ -3143,11 +3171,17 @@ async function acceptSlackReactionTask(
|
|
|
3143
3171
|
}
|
|
3144
3172
|
return;
|
|
3145
3173
|
}
|
|
3146
|
-
await acceptSessionUserMessage(
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3174
|
+
await acceptSessionUserMessage(
|
|
3175
|
+
await withCatalogSettings(deps, grant),
|
|
3176
|
+
grant,
|
|
3177
|
+
grant.workspaceId,
|
|
3178
|
+
sessionId,
|
|
3179
|
+
{
|
|
3180
|
+
text: entry.text,
|
|
3181
|
+
resources,
|
|
3182
|
+
clientEventId,
|
|
3183
|
+
},
|
|
3184
|
+
);
|
|
3151
3185
|
}
|
|
3152
3186
|
|
|
3153
3187
|
async function continueSlackSession(
|
|
@@ -3230,12 +3264,18 @@ async function continueSlackSession(
|
|
|
3230
3264
|
if (!hasPermission(grant.permissions, "sessions:control")) {
|
|
3231
3265
|
throw new SlackInteractionPermanentError("sessions_control_denied");
|
|
3232
3266
|
}
|
|
3233
|
-
await acceptSessionUserMessage(
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3267
|
+
await acceptSessionUserMessage(
|
|
3268
|
+
await withCatalogSettings(deps, grant),
|
|
3269
|
+
grant,
|
|
3270
|
+
interaction.workspaceId,
|
|
3271
|
+
interaction.sessionId,
|
|
3272
|
+
{
|
|
3273
|
+
text: entry.text,
|
|
3274
|
+
...(options.modelContext ? { modelContext: options.modelContext } : {}),
|
|
3275
|
+
resources,
|
|
3276
|
+
clientEventId: `slack:${entry.providerEventId}`,
|
|
3277
|
+
},
|
|
3278
|
+
);
|
|
3239
3279
|
}
|
|
3240
3280
|
|
|
3241
3281
|
const SLACK_ACTION_ID_BY_KIND: Record<SlackInteractionActionKind, string> = {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
interactionOperationMetricObserver,
|
|
17
17
|
type Observability,
|
|
18
18
|
} from "@opengeni/observability";
|
|
19
|
+
import type { ComputerFrameEvidenceMismatchReason } from "@opengeni/runtime/sandbox";
|
|
19
20
|
|
|
20
21
|
type InteractionActionReceipt = BrowserActionReceipt | ComputerActionReceipt;
|
|
21
22
|
type InteractionLifecycleMutation =
|
|
@@ -32,6 +33,14 @@ const STALE_INTERACTION_ERROR_CODES = new Set([
|
|
|
32
33
|
"attempt_stale",
|
|
33
34
|
]);
|
|
34
35
|
|
|
36
|
+
const COMPUTER_FRAME_EVIDENCE_MISMATCH_REASONS = new Set<ComputerFrameEvidenceMismatchReason>([
|
|
37
|
+
"frame_session_mismatch",
|
|
38
|
+
"frame_target_mismatch",
|
|
39
|
+
"frame_controller_mismatch",
|
|
40
|
+
"frame_media_mismatch",
|
|
41
|
+
"frame_digest_mismatch",
|
|
42
|
+
]);
|
|
43
|
+
|
|
35
44
|
export function observeBrowserActionResult(
|
|
36
45
|
observability: Observability | null | undefined,
|
|
37
46
|
startedAtMs: number,
|
|
@@ -62,6 +71,27 @@ export function observeComputerActionResult(
|
|
|
62
71
|
});
|
|
63
72
|
}
|
|
64
73
|
|
|
74
|
+
export function observeComputerFrameEvidenceMismatch(
|
|
75
|
+
observability: Observability | null | undefined,
|
|
76
|
+
reason: ComputerFrameEvidenceMismatchReason,
|
|
77
|
+
): void {
|
|
78
|
+
if (!observability || !COMPUTER_FRAME_EVIDENCE_MISMATCH_REASONS.has(reason)) return;
|
|
79
|
+
try {
|
|
80
|
+
observability.incrementCounter({
|
|
81
|
+
name: "opengeni_computer_frame_evidence_mismatches_total",
|
|
82
|
+
help: "Computer frame evidence rejected at the controller-to-API boundary by bounded reason.",
|
|
83
|
+
labels: { reason },
|
|
84
|
+
});
|
|
85
|
+
} catch {
|
|
86
|
+
// Observability cannot alter the fail-closed frame boundary.
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
observability.warn("Computer frame evidence mismatch", { reason });
|
|
90
|
+
} catch {
|
|
91
|
+
// Observability cannot alter the fail-closed frame boundary.
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
65
95
|
export function observeLifecycleResult(
|
|
66
96
|
observability: Observability | null | undefined,
|
|
67
97
|
startedAtMs: number,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AGENT_AUTHORED_DURABLE_TEXT_STYLE,
|
|
3
2
|
AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_MAX_CHARS,
|
|
4
3
|
AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_TOO_LONG_MESSAGE,
|
|
4
|
+
AGENT_AUTHORED_INSTRUCTION_POLICY_STYLE,
|
|
5
5
|
AGENT_AUTHORED_PREFERENCE_CONTENT_MAX_CHARS,
|
|
6
6
|
AGENT_AUTHORED_PREFERENCE_CONTENT_TOO_LONG_MESSAGE,
|
|
7
|
+
AGENT_AUTHORED_SKILL_STYLE,
|
|
7
8
|
PREFERENCE_REGISTRY_DESCRIPTOR_DESCRIPTION_MAX_CHARS,
|
|
8
9
|
PREFERENCE_REGISTRY_STABLE_KEY_MAX_CHARS,
|
|
9
10
|
PREFERENCE_REGISTRY_TITLE_MAX_CHARS,
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
type CompanyBrainGovernedWriteAttempt,
|
|
12
13
|
} from "@opengeni/contracts";
|
|
13
14
|
import { createCompanyBrainLearningPolicyRouter } from "@opengeni/core";
|
|
14
|
-
import type
|
|
15
|
+
import { PreferenceRegistryStableKeyConflictError, type Database } from "@opengeni/db";
|
|
15
16
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16
17
|
import * as z from "zod/v4";
|
|
17
18
|
|
|
@@ -66,6 +67,20 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
66
67
|
input: RegisterCompanyBrainGovernedWriteToolsInput,
|
|
67
68
|
): void {
|
|
68
69
|
const router = input.router ?? createCompanyBrainLearningPolicyRouter({ db: input.db });
|
|
70
|
+
const writeResult = async (write: () => ReturnType<typeof router.write>) => {
|
|
71
|
+
try {
|
|
72
|
+
return input.json(await write());
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error instanceof PreferenceRegistryStableKeyConflictError) {
|
|
75
|
+
return input.json({
|
|
76
|
+
status: "not_proposed",
|
|
77
|
+
code: "preference_stable_key_conflict",
|
|
78
|
+
message: error.message,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
69
84
|
input.server.registerTool(
|
|
70
85
|
"knowledge_propose",
|
|
71
86
|
{
|
|
@@ -75,8 +90,8 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
75
90
|
},
|
|
76
91
|
async (request) => {
|
|
77
92
|
await input.authorize();
|
|
78
|
-
return
|
|
79
|
-
|
|
93
|
+
return writeResult(() =>
|
|
94
|
+
router.write({
|
|
80
95
|
attempt: input.attempt,
|
|
81
96
|
request: { kind: "propose_knowledge", ...request },
|
|
82
97
|
}),
|
|
@@ -93,8 +108,8 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
93
108
|
},
|
|
94
109
|
async (request) => {
|
|
95
110
|
await input.authorize();
|
|
96
|
-
return
|
|
97
|
-
|
|
111
|
+
return writeResult(() =>
|
|
112
|
+
router.write({
|
|
98
113
|
attempt: input.attempt,
|
|
99
114
|
request: { kind: "correct_knowledge", ...request },
|
|
100
115
|
}),
|
|
@@ -111,8 +126,8 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
111
126
|
},
|
|
112
127
|
async (request) => {
|
|
113
128
|
await input.authorize();
|
|
114
|
-
return
|
|
115
|
-
|
|
129
|
+
return writeResult(() =>
|
|
130
|
+
router.write({
|
|
116
131
|
attempt: input.attempt,
|
|
117
132
|
request: { kind: "promote_task_note_knowledge", ...request },
|
|
118
133
|
}),
|
|
@@ -124,9 +139,9 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
124
139
|
"task_note_promote_instruction_policy",
|
|
125
140
|
{
|
|
126
141
|
description:
|
|
127
|
-
"Atomically promote one still-active note from this exact root task tree into
|
|
128
|
-
`Use this only for a universal always-on rule, never for an incident, fact, decision, outcome, or conditional procedure. Once
|
|
129
|
-
"
|
|
142
|
+
"Atomically promote one still-active note from this exact root task tree into a workspace instruction-policy proposal. The note bytes remain exact evidence and draft content. " +
|
|
143
|
+
`Use this only for a universal always-on rule, never for an incident, fact, decision, outcome, or conditional procedure. Once active, those bytes are composed verbatim into the prompt of every session the target applies to, so a note over ${AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters is rejected here rather than truncated: write a fresh minimal imperative note instead of promoting a long working note. ` +
|
|
144
|
+
"Off creates nothing; Review first keeps the proposal inactive; Autonomous may activate an eligible proposal through the governed instruction lifecycle with an undoable receipt. This never widens scope.",
|
|
130
145
|
inputSchema: {
|
|
131
146
|
...taskNotePromotion,
|
|
132
147
|
target: WorkspaceInstructionPolicyTarget,
|
|
@@ -136,8 +151,8 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
136
151
|
},
|
|
137
152
|
async (request) => {
|
|
138
153
|
await input.authorize();
|
|
139
|
-
return
|
|
140
|
-
|
|
154
|
+
return writeResult(() =>
|
|
155
|
+
router.write({
|
|
141
156
|
attempt: input.attempt,
|
|
142
157
|
request: { kind: "promote_task_note_instruction_policy", ...request },
|
|
143
158
|
}),
|
|
@@ -172,8 +187,8 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
172
187
|
},
|
|
173
188
|
async (request) => {
|
|
174
189
|
await input.authorize();
|
|
175
|
-
return
|
|
176
|
-
|
|
190
|
+
return writeResult(() =>
|
|
191
|
+
router.write({
|
|
177
192
|
attempt: input.attempt,
|
|
178
193
|
request: { kind: "promote_task_note_preference", ...request },
|
|
179
194
|
}),
|
|
@@ -185,9 +200,9 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
185
200
|
"instruction_policy_propose",
|
|
186
201
|
{
|
|
187
202
|
description:
|
|
188
|
-
"Materialize an evidence-backed
|
|
189
|
-
`Use this only for a minimal universal rule, never for an incident, fact, decision, outcome, or conditional procedure. Once
|
|
190
|
-
"
|
|
203
|
+
"Materialize an evidence-backed workspace instruction-policy proposal. " +
|
|
204
|
+
`Use this only for a minimal universal rule, never for an incident, fact, decision, outcome, or conditional procedure. Once active, this content is composed verbatim into the prompt of every session the target applies to (every session in this workspace for a global charter or policy, every session bound to the role for a role policy), so keep it under ${AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters. ${AGENT_AUTHORED_INSTRUCTION_POLICY_STYLE} ` +
|
|
205
|
+
"Off creates nothing; Review first keeps the proposal inactive; Autonomous may activate an eligible proposal through the governed instruction lifecycle with an undoable receipt.",
|
|
191
206
|
inputSchema: {
|
|
192
207
|
...evidence,
|
|
193
208
|
target: WorkspaceInstructionPolicyTarget,
|
|
@@ -206,8 +221,8 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
206
221
|
},
|
|
207
222
|
async (request) => {
|
|
208
223
|
await input.authorize();
|
|
209
|
-
return
|
|
210
|
-
|
|
224
|
+
return writeResult(() =>
|
|
225
|
+
router.write({
|
|
211
226
|
attempt: input.attempt,
|
|
212
227
|
request: { kind: "propose_instruction_policy", ...request },
|
|
213
228
|
}),
|
|
@@ -220,7 +235,7 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
220
235
|
{
|
|
221
236
|
description:
|
|
222
237
|
"Materialize an evidence-backed workspace Skill proposal in the structured preference authority. Use this only for reusable conditional how-to guidance, never for an incident, fact, decision, outcome, or universal always-on rule. " +
|
|
223
|
-
`Its short title and description are what get composed into every session prompt; the content is retrieved on demand, so its length is retrieval cost rather than standing prompt cost. Keep the content under ${AGENT_AUTHORED_PREFERENCE_CONTENT_MAX_CHARS} characters. ${
|
|
238
|
+
`Its short title and description are what get composed into every session prompt; the content is retrieved on demand, so its length is retrieval cost rather than standing prompt cost. Keep the content under ${AGENT_AUTHORED_PREFERENCE_CONTENT_MAX_CHARS} characters. ${AGENT_AUTHORED_SKILL_STYLE} ` +
|
|
224
239
|
"Under Suggest it stays inactive for human review; under Automatic an eligible decision is activated through the governed preference lifecycle with an undoable receipt. It never creates mandatory authority.",
|
|
225
240
|
inputSchema: {
|
|
226
241
|
...evidence,
|
|
@@ -251,8 +266,8 @@ export function registerCompanyBrainGovernedWriteTools(
|
|
|
251
266
|
},
|
|
252
267
|
async (request) => {
|
|
253
268
|
await input.authorize();
|
|
254
|
-
return
|
|
255
|
-
|
|
269
|
+
return writeResult(() =>
|
|
270
|
+
router.write({
|
|
256
271
|
attempt: input.attempt,
|
|
257
272
|
request: { kind: "propose_preference", ...request },
|
|
258
273
|
}),
|
|
@@ -109,12 +109,12 @@ function boundedIssueMessage(error: z.ZodError): string {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
* Register the
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
112
|
+
* Register the organization profile administration path. This is not derived
|
|
113
|
+
* workspace learning: the initiating human must be the organization's active
|
|
114
|
+
* owner (the same authority as the manual `account:admin` route). The separate
|
|
115
|
+
* organization policy either blocks the proposal, keeps the exact bound
|
|
116
|
+
* human-confirmation path, or permits immediate activation through the native
|
|
117
|
+
* company-profile lifecycle.
|
|
118
118
|
*/
|
|
119
119
|
export function registerCompanyProfileAgentAdminTools(
|
|
120
120
|
input: RegisterCompanyProfileAgentAdminToolsInput,
|
|
@@ -127,7 +127,7 @@ export function registerCompanyProfileAgentAdminTools(
|
|
|
127
127
|
"Prepare the organization's small, stable identity: identity says who the organization is, and mission says why it exists. " +
|
|
128
128
|
"Once activated, both fields are mandatory prompt context in every root session for the whole organization, so use one plain descriptive statement per field with no products, customers, goals, constraints, procedures, or marketing copy. Those details belong in organization-scoped Documents and are retrieved only when relevant. " +
|
|
129
129
|
`Each field is bounded to ${AGENT_AUTHORED_COMPANY_PROFILE_SCALAR_MAX_CHARS} characters for agent-authored proposals. ` +
|
|
130
|
-
"This
|
|
130
|
+
"This is independent of workspace learning policy and follows the organization owner's Agent-managed identity mode. Off creates nothing. Review first returns status=confirmation_required with the exact `humanInput` payload; call `request_human_input` with it verbatim, then call `company_profile_confirm` with the returned requestId. Autonomous may return status=activated immediately after exact live-owner, stale-head, and compare-and-swap checks; do not ask for another confirmation in that case.",
|
|
131
131
|
inputSchema: {
|
|
132
132
|
operationId: z.string().uuid(),
|
|
133
133
|
identity: scalar,
|
package/src/mcp/remember.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type RememberLane,
|
|
12
12
|
} from "@opengeni/contracts";
|
|
13
13
|
import { RememberError, createRememberRouter } from "@opengeni/core";
|
|
14
|
-
import type
|
|
14
|
+
import { PreferenceRegistryStableKeyConflictError, type Database } from "@opengeni/db";
|
|
15
15
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16
16
|
import * as z from "zod/v4";
|
|
17
17
|
|
|
@@ -59,7 +59,7 @@ const laneFields = {
|
|
|
59
59
|
/**
|
|
60
60
|
* Explicit user-directed remember. Content becomes exact task-note evidence,
|
|
61
61
|
* promotion runs through the frozen workspace learning policy, and activation
|
|
62
|
-
* is either automatic (
|
|
62
|
+
* is either automatic (Skill or instruction under Automatic mode) or requires exactly one
|
|
63
63
|
* bound human confirmation through the built-in `request_human_input` tool.
|
|
64
64
|
* Tool input cannot select scope beyond the workspace, active authority, or
|
|
65
65
|
* another learning-policy source.
|
|
@@ -70,9 +70,9 @@ export function registerRememberTools(input: RegisterRememberToolsInput): void {
|
|
|
70
70
|
"remember",
|
|
71
71
|
{
|
|
72
72
|
description:
|
|
73
|
-
"
|
|
74
|
-
`Write the instruction lane as the shortest complete rule, at most ${AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters and normally 1-3 imperative sentences, with no numbered steps, examples, rationale, or restated defaults. Keep a Skill under ${AGENT_AUTHORED_PREFERENCE_CONTENT_MAX_CHARS} characters
|
|
75
|
-
"Under
|
|
73
|
+
"Create governed durable knowledge, a Skill, or a mandatory workspace instruction. When the agent-only memory_save tool is available, use it instead for ordinary durable facts, decisions, incidents, bug fixes, and confirmed outcomes; those Memory writes are autonomous and independent of Learning mode. Use lane=knowledge only when memory_save is unavailable and the user explicitly requests reviewed workspace knowledge; lane=preference creates a Skill for reusable conditional how-to guidance; lane=instruction_policy is only for a universal always/never rule that should apply to nearly every task. " +
|
|
74
|
+
`Write the instruction lane as the shortest complete rule, at most ${AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters and normally 1-3 imperative sentences, with no numbered steps, examples, rationale, or restated defaults. Keep a Skill under ${AGENT_AUTHORED_PREFERENCE_CONTENT_MAX_CHARS} characters with one clear trigger and outcome, only the necessary steps, checks, and exceptions, and a one-sentence descriptor. Do not copy one item into multiple lanes. ` +
|
|
75
|
+
"Under Autonomous learning, an eligible Skill or workspace instruction may activate immediately through its governed lifecycle. Under Review first, it remains inactive and the receipt returns status=confirmation_required with the exact `humanInput` payload: call `request_human_input` with it verbatim, then call `remember_confirm` with the returned requestId. Off creates no governed change. Reviewed Knowledge always needs confirmation. Do not use lane=knowledge for facts you merely inferred; use memory_save when available, otherwise knowledge_propose or task notes. Confirmed lane=knowledge content keeps its reviewed claim provenance and materializes its exact approved text into Memory for later `memory_search` retrieval.",
|
|
76
76
|
inputSchema: {
|
|
77
77
|
lane: z.enum(["preference", "instruction_policy", "knowledge"]),
|
|
78
78
|
...laneFields,
|
|
@@ -120,9 +120,20 @@ export function registerRememberTools(input: RegisterRememberToolsInput): void {
|
|
|
120
120
|
: lane === "instruction_policy"
|
|
121
121
|
? { ...base, lane, target: request.target }
|
|
122
122
|
: { ...base, lane, subject: request.subject ?? content.slice(0, 80) };
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
try {
|
|
124
|
+
return input.json(
|
|
125
|
+
await router.remember({ attempt: input.attempt, request: rememberRequest }),
|
|
126
|
+
);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof PreferenceRegistryStableKeyConflictError) {
|
|
129
|
+
return input.json({
|
|
130
|
+
status: "not_remembered",
|
|
131
|
+
code: "preference_stable_key_conflict",
|
|
132
|
+
message: error.message,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
126
137
|
},
|
|
127
138
|
);
|
|
128
139
|
|