@opengeni/core 0.16.3 → 0.18.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/domain/memory-slack-publication.d.ts +107 -0
- package/dist/domain/personal-connection-delegations.d.ts +48 -0
- package/dist/domain/scheduled-tasks.d.ts +8 -3
- package/dist/domain/sessions.d.ts +11 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +495 -3
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/domain/capabilities.ts +15 -1
- package/src/domain/memory-slack-publication.ts +448 -0
- package/src/domain/personal-connection-delegations.ts +247 -0
- package/src/domain/scheduled-tasks.ts +79 -3
- package/src/domain/sessions.ts +29 -2
- package/src/index.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -2356,6 +2356,19 @@ async function requireCatalogItem(db, workspaceId, settings, capabilityId) {
|
|
|
2356
2356
|
return item;
|
|
2357
2357
|
}
|
|
2358
2358
|
function packCatalogItem(pack, source) {
|
|
2359
|
+
const customMetadata = { ...pack.metadata };
|
|
2360
|
+
for (const key of [
|
|
2361
|
+
"packId",
|
|
2362
|
+
"version",
|
|
2363
|
+
"connectors",
|
|
2364
|
+
"knowledge",
|
|
2365
|
+
"scheduledTaskTemplates",
|
|
2366
|
+
"sandboxImage",
|
|
2367
|
+
"sandboxProviderImages",
|
|
2368
|
+
"skills"
|
|
2369
|
+
]) {
|
|
2370
|
+
delete customMetadata[key];
|
|
2371
|
+
}
|
|
2359
2372
|
return CapabilityCatalogItem.parse({
|
|
2360
2373
|
id: `pack:${pack.id}`,
|
|
2361
2374
|
kind: "pack",
|
|
@@ -2370,6 +2383,7 @@ function packCatalogItem(pack, source) {
|
|
|
2370
2383
|
notes: "Enables role-scoped tools, connectors, knowledge, and scheduled-task templates."
|
|
2371
2384
|
},
|
|
2372
2385
|
metadata: {
|
|
2386
|
+
...customMetadata,
|
|
2373
2387
|
packId: pack.id,
|
|
2374
2388
|
version: pack.version,
|
|
2375
2389
|
connectors: pack.connectors,
|
|
@@ -2377,8 +2391,8 @@ function packCatalogItem(pack, source) {
|
|
|
2377
2391
|
scheduledTaskTemplates: pack.scheduledTaskTemplates,
|
|
2378
2392
|
// Runtime composition surface only: skill names, never file content.
|
|
2379
2393
|
...pack.sandboxImage ? { sandboxImage: pack.sandboxImage } : {},
|
|
2380
|
-
...pack.
|
|
2381
|
-
...pack.
|
|
2394
|
+
...pack.sandboxProviderImages ? { sandboxProviderImages: pack.sandboxProviderImages } : {},
|
|
2395
|
+
...pack.skills.length > 0 ? { skills: pack.skills.map((skill) => skill.name) } : {}
|
|
2382
2396
|
}
|
|
2383
2397
|
});
|
|
2384
2398
|
}
|
|
@@ -3151,6 +3165,158 @@ async function listRigChangesForApi(deps, workspaceId, rigId, limit) {
|
|
|
3151
3165
|
return await listRigChanges(deps.db, workspaceId, rigId, limit);
|
|
3152
3166
|
}
|
|
3153
3167
|
|
|
3168
|
+
// src/domain/personal-connection-delegations.ts
|
|
3169
|
+
import {
|
|
3170
|
+
getSessionTurnPersonalConnectionDelegations,
|
|
3171
|
+
getWorkspaceGrant as getWorkspaceGrant2,
|
|
3172
|
+
listConnectionsMetadata as listConnectionsMetadata2
|
|
3173
|
+
} from "@opengeni/db";
|
|
3174
|
+
function directPersonalConnectionSubjectId(turn) {
|
|
3175
|
+
if (turn.source !== "user" && turn.source !== "api" || turn.initiator.kind !== "subject") {
|
|
3176
|
+
return void 0;
|
|
3177
|
+
}
|
|
3178
|
+
return ["via", "viaTruncated", "provenanceError", "backfill"].some(
|
|
3179
|
+
(key) => Object.prototype.hasOwnProperty.call(turn.initiatorContext, key)
|
|
3180
|
+
) ? void 0 : turn.initiator.subjectId;
|
|
3181
|
+
}
|
|
3182
|
+
function personalConnectionDelegationSourceForGrant(grant) {
|
|
3183
|
+
const callerSessionId = grant.metadata?.["sessionId"];
|
|
3184
|
+
const callerTurnId = grant.metadata?.["turnId"];
|
|
3185
|
+
if (typeof callerSessionId === "string" && typeof callerTurnId === "string") {
|
|
3186
|
+
return { kind: "turn", sessionId: callerSessionId, turnId: callerTurnId };
|
|
3187
|
+
}
|
|
3188
|
+
if (grant.principalKind === "agent_attempt" || grant.principalKind === "service" || grant.serviceInitiator) {
|
|
3189
|
+
return { kind: "none" };
|
|
3190
|
+
}
|
|
3191
|
+
return { kind: "subject", subjectId: grant.subjectId };
|
|
3192
|
+
}
|
|
3193
|
+
function selectedPersonalConnectionServers(settings, tools) {
|
|
3194
|
+
const selected = new Set(tools.map((tool) => tool.id));
|
|
3195
|
+
return settings.mcpServers.filter(
|
|
3196
|
+
(server) => selected.has(server.id) && server.connectionRef?.subjectScope === "subject"
|
|
3197
|
+
);
|
|
3198
|
+
}
|
|
3199
|
+
function canonicalPersonalConnections(connections) {
|
|
3200
|
+
return [...connections].sort((left, right) => {
|
|
3201
|
+
const active = Number(right.status === "active") - Number(left.status === "active");
|
|
3202
|
+
if (active !== 0) return active;
|
|
3203
|
+
const updated = Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
|
|
3204
|
+
if (updated !== 0) return updated;
|
|
3205
|
+
const created = Date.parse(right.createdAt) - Date.parse(left.createdAt);
|
|
3206
|
+
if (created !== 0) return created;
|
|
3207
|
+
return right.id.localeCompare(left.id);
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
function sameProviderDomain(left, right) {
|
|
3211
|
+
return left.toLowerCase() === right.toLowerCase();
|
|
3212
|
+
}
|
|
3213
|
+
function personalConnectionDelegationsFromVisibleConnections(input) {
|
|
3214
|
+
const delegations = [];
|
|
3215
|
+
const connections = canonicalPersonalConnections(input.connections);
|
|
3216
|
+
for (const server of input.servers) {
|
|
3217
|
+
const ref = server.connectionRef;
|
|
3218
|
+
if (!ref || ref.subjectScope !== "subject") continue;
|
|
3219
|
+
const connection = connections.find(
|
|
3220
|
+
(candidate) => candidate.subjectId === input.subjectId && candidate.status === "active" && sameProviderDomain(candidate.providerDomain, ref.providerDomain) && (!ref.kind || candidate.kind === ref.kind) && (!ref.connectionId || candidate.id === ref.connectionId)
|
|
3221
|
+
);
|
|
3222
|
+
if (!connection) continue;
|
|
3223
|
+
delegations.push({
|
|
3224
|
+
serverId: server.id,
|
|
3225
|
+
connectionId: connection.id,
|
|
3226
|
+
ownerSubjectId: input.subjectId,
|
|
3227
|
+
providerDomain: connection.providerDomain,
|
|
3228
|
+
kind: connection.kind
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
return delegations;
|
|
3232
|
+
}
|
|
3233
|
+
function personalConnectionDelegationsFromParent(input) {
|
|
3234
|
+
return input.servers.flatMap((server) => {
|
|
3235
|
+
const ref = server.connectionRef;
|
|
3236
|
+
if (!ref || ref.subjectScope !== "subject") return [];
|
|
3237
|
+
const delegation = input.parentDelegations.find(
|
|
3238
|
+
(candidate) => candidate.serverId === server.id && sameProviderDomain(candidate.providerDomain, ref.providerDomain) && (!ref.kind || !candidate.kind || candidate.kind === ref.kind)
|
|
3239
|
+
);
|
|
3240
|
+
return delegation ? [{ ...delegation }] : [];
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
function personalConnectionDelegationsEqual(left, right) {
|
|
3244
|
+
if (left.length !== right.length) return false;
|
|
3245
|
+
const byServer = new Map(right.map((delegation) => [delegation.serverId, delegation]));
|
|
3246
|
+
return left.every((delegation) => {
|
|
3247
|
+
const other = byServer.get(delegation.serverId);
|
|
3248
|
+
return other?.connectionId === delegation.connectionId && other.ownerSubjectId === delegation.ownerSubjectId && sameProviderDomain(other.providerDomain, delegation.providerDomain) && other.kind === delegation.kind;
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
function personalConnectionDelegationForServer(delegations, server) {
|
|
3252
|
+
const ref = server.connectionRef;
|
|
3253
|
+
if (!ref || ref.subjectScope !== "subject") return null;
|
|
3254
|
+
return delegations.find(
|
|
3255
|
+
(delegation) => delegation.serverId === server.id && sameProviderDomain(delegation.providerDomain, ref.providerDomain) && (!ref.kind || !delegation.kind || delegation.kind === ref.kind)
|
|
3256
|
+
) ?? null;
|
|
3257
|
+
}
|
|
3258
|
+
function personalAuthorityUnavailable(request) {
|
|
3259
|
+
const ref = request.connectionRef;
|
|
3260
|
+
return {
|
|
3261
|
+
status: "auth_needed",
|
|
3262
|
+
reason: "personal_authority_unavailable",
|
|
3263
|
+
providerDomain: ref.providerDomain,
|
|
3264
|
+
...ref.provider ? { provider: ref.provider } : {},
|
|
3265
|
+
...ref.scopes ? { scopes: ref.scopes } : {},
|
|
3266
|
+
...ref.resource ? { resource: ref.resource } : {},
|
|
3267
|
+
...ref.selectedResources ? { selectedResources: ref.selectedResources } : {}
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
function withFrozenPersonalConnectionDelegations(input) {
|
|
3271
|
+
return async (request) => {
|
|
3272
|
+
let effectiveRequest = request;
|
|
3273
|
+
if (request.connectionRef.subjectScope === "subject") {
|
|
3274
|
+
const config = input.settings.mcpServers.find((server) => server.id === request.serverId);
|
|
3275
|
+
const delegation = config ? personalConnectionDelegationForServer(input.personalConnectionDelegations, config) : null;
|
|
3276
|
+
if (!delegation || !await input.ownerHasWorkspaceMembership(delegation.ownerSubjectId)) {
|
|
3277
|
+
return personalAuthorityUnavailable(request);
|
|
3278
|
+
}
|
|
3279
|
+
effectiveRequest = {
|
|
3280
|
+
...request,
|
|
3281
|
+
subjectId: delegation.ownerSubjectId,
|
|
3282
|
+
connectionRef: {
|
|
3283
|
+
...request.connectionRef,
|
|
3284
|
+
providerDomain: delegation.providerDomain,
|
|
3285
|
+
connectionId: delegation.connectionId,
|
|
3286
|
+
...delegation.kind ? { kind: delegation.kind } : {}
|
|
3287
|
+
}
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
const result = await input.resolveCredential(effectiveRequest);
|
|
3291
|
+
if (result.status === "ok" || request.connectionRef.subjectScope !== "subject") {
|
|
3292
|
+
return result;
|
|
3293
|
+
}
|
|
3294
|
+
return personalAuthorityUnavailable(request);
|
|
3295
|
+
};
|
|
3296
|
+
}
|
|
3297
|
+
async function freezePersonalConnectionDelegations(input) {
|
|
3298
|
+
const servers = selectedPersonalConnectionServers(input.settings, input.tools);
|
|
3299
|
+
if (servers.length === 0 || input.source.kind === "none") return [];
|
|
3300
|
+
if (input.source.kind === "turn") {
|
|
3301
|
+
return personalConnectionDelegationsFromParent({
|
|
3302
|
+
servers,
|
|
3303
|
+
parentDelegations: await getSessionTurnPersonalConnectionDelegations(
|
|
3304
|
+
input.db,
|
|
3305
|
+
input.workspaceId,
|
|
3306
|
+
input.source.sessionId,
|
|
3307
|
+
input.source.turnId
|
|
3308
|
+
)
|
|
3309
|
+
});
|
|
3310
|
+
}
|
|
3311
|
+
const membership = await getWorkspaceGrant2(input.db, input.source.subjectId, input.workspaceId);
|
|
3312
|
+
if (!membership) return [];
|
|
3313
|
+
return personalConnectionDelegationsFromVisibleConnections({
|
|
3314
|
+
servers,
|
|
3315
|
+
subjectId: input.source.subjectId,
|
|
3316
|
+
connections: await listConnectionsMetadata2(input.db, input.workspaceId, input.source.subjectId)
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3154
3320
|
// src/domain/resources.ts
|
|
3155
3321
|
import {
|
|
3156
3322
|
gitCredentialBindingIdForRepository,
|
|
@@ -3552,6 +3718,7 @@ import {
|
|
|
3552
3718
|
getNestedAgentDepthDeploymentPolicy,
|
|
3553
3719
|
getRig as getRig3,
|
|
3554
3720
|
getScheduledTask,
|
|
3721
|
+
getScheduledTaskPersonalConnectionDelegations,
|
|
3555
3722
|
requireWorkspace as requireWorkspace2,
|
|
3556
3723
|
updateScheduledTask
|
|
3557
3724
|
} from "@opengeni/db";
|
|
@@ -4009,6 +4176,7 @@ async function createAndStartSession(input) {
|
|
|
4009
4176
|
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
4010
4177
|
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
|
|
4011
4178
|
mcpServers: input.mcpServers ?? [],
|
|
4179
|
+
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
4012
4180
|
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
4013
4181
|
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
4014
4182
|
subjectId: input.subjectId ?? null
|
|
@@ -4053,6 +4221,7 @@ async function createAndStartSession(input) {
|
|
|
4053
4221
|
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
4054
4222
|
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
|
|
4055
4223
|
mcpServers: input.mcpServers ?? [],
|
|
4224
|
+
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
4056
4225
|
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
4057
4226
|
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
4058
4227
|
subjectId: input.subjectId ?? null
|
|
@@ -4251,6 +4420,7 @@ async function postUserMessageTurn(input) {
|
|
|
4251
4420
|
reasoningEffortFallback: input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
|
|
4252
4421
|
turnExecutionPolicy: input.turnExecutionPolicy,
|
|
4253
4422
|
source: input.origin === "operator" ? "api" : "user",
|
|
4423
|
+
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
4254
4424
|
mcpCredentialUpdates: input.mcpCredentialUpdates ?? []
|
|
4255
4425
|
})
|
|
4256
4426
|
)
|
|
@@ -4405,6 +4575,13 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
4405
4575
|
toolPolicy = { mode: "workspace_default", inheritedFromSessionId: null };
|
|
4406
4576
|
}
|
|
4407
4577
|
const tools = withFirstPartyTools(selectedTools, runtimeSettings);
|
|
4578
|
+
const personalConnectionDelegations = await freezePersonalConnectionDelegations({
|
|
4579
|
+
db,
|
|
4580
|
+
workspaceId,
|
|
4581
|
+
settings: runtimeSettings,
|
|
4582
|
+
tools,
|
|
4583
|
+
source: personalConnectionDelegationSourceForGrant(grant)
|
|
4584
|
+
});
|
|
4408
4585
|
await validateGitHubRepositorySelection(db, workspaceId, resources);
|
|
4409
4586
|
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
4410
4587
|
throw new HTTPException10(503, { message: "object storage is not configured" });
|
|
@@ -4640,6 +4817,7 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
4640
4817
|
firstPartyMcpTools,
|
|
4641
4818
|
mcpServers: sessionMcpServers.dbServers,
|
|
4642
4819
|
sessionMcpServers: sessionMcpServers.metadata,
|
|
4820
|
+
personalConnectionDelegations,
|
|
4643
4821
|
parentSessionId,
|
|
4644
4822
|
createIdempotencyKey: payload.idempotencyKey ?? null,
|
|
4645
4823
|
maxNestedAgentDepthOverride: payload.maxNestedAgentDepth ?? null,
|
|
@@ -4742,6 +4920,14 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
|
|
|
4742
4920
|
session: existingSession,
|
|
4743
4921
|
updates: input.mcpCredentialUpdates ?? []
|
|
4744
4922
|
});
|
|
4923
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings);
|
|
4924
|
+
const personalConnectionDelegations = await freezePersonalConnectionDelegations({
|
|
4925
|
+
db,
|
|
4926
|
+
workspaceId,
|
|
4927
|
+
settings: runtimeSettings,
|
|
4928
|
+
tools: existingSession.tools,
|
|
4929
|
+
source: personalConnectionDelegationSourceForGrant(grant)
|
|
4930
|
+
});
|
|
4745
4931
|
const delegatedServiceInitiator = serviceInitiatorForGrant(grant);
|
|
4746
4932
|
const { accepted, turn } = await postUserMessageTurn({
|
|
4747
4933
|
db,
|
|
@@ -4760,6 +4946,7 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
|
|
|
4760
4946
|
reasoningEffortFallback: sessionReasoningEffort,
|
|
4761
4947
|
turnExecutionPolicy,
|
|
4762
4948
|
mcpCredentialUpdates,
|
|
4949
|
+
personalConnectionDelegations,
|
|
4763
4950
|
delivery: input.delivery ?? "send",
|
|
4764
4951
|
origin: delegatedServiceInitiator ? "operator" : input.origin ?? "human",
|
|
4765
4952
|
actor: grant.subjectId,
|
|
@@ -5101,6 +5288,19 @@ async function createValidatedScheduledTask(input) {
|
|
|
5101
5288
|
if (input.payload.rigId) {
|
|
5102
5289
|
await requireScheduledTaskRig(input.db, input.grant.workspaceId, input.payload.rigId);
|
|
5103
5290
|
}
|
|
5291
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
5292
|
+
input.db,
|
|
5293
|
+
input.grant.workspaceId,
|
|
5294
|
+
input.settings
|
|
5295
|
+
);
|
|
5296
|
+
const personalConnectionDelegations = await freezePersonalConnectionDelegations({
|
|
5297
|
+
db: input.db,
|
|
5298
|
+
workspaceId: input.grant.workspaceId,
|
|
5299
|
+
settings: runtimeSettings,
|
|
5300
|
+
tools: agentConfig.tools,
|
|
5301
|
+
source: personalConnectionDelegationSourceForGrant(input.grant)
|
|
5302
|
+
});
|
|
5303
|
+
const creationInitiator = creationInitiatorForGrant(input.grant);
|
|
5104
5304
|
return await createScheduledTask(input.db, {
|
|
5105
5305
|
id,
|
|
5106
5306
|
accountId: input.grant.accountId,
|
|
@@ -5112,6 +5312,10 @@ async function createValidatedScheduledTask(input) {
|
|
|
5112
5312
|
runMode: input.payload.runMode,
|
|
5113
5313
|
overlapPolicy: input.payload.overlapPolicy,
|
|
5114
5314
|
agentConfig,
|
|
5315
|
+
...creationInitiator.initiator ? { createdBy: creationInitiator.initiator } : {},
|
|
5316
|
+
...creationInitiator.context ? { createdByContext: creationInitiator.context } : {},
|
|
5317
|
+
createdByActor: creationInitiator.actor ?? null,
|
|
5318
|
+
personalConnectionDelegations,
|
|
5115
5319
|
variableSetId: input.payload.variableSetId ?? null,
|
|
5116
5320
|
rigId: input.payload.rigId ?? null,
|
|
5117
5321
|
metadata: input.payload.metadata
|
|
@@ -5192,6 +5396,31 @@ async function validatedScheduledTaskUpdate(input) {
|
|
|
5192
5396
|
});
|
|
5193
5397
|
}
|
|
5194
5398
|
update.agentConfig = nextAgentConfig;
|
|
5399
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
5400
|
+
input.db,
|
|
5401
|
+
input.existing.workspaceId,
|
|
5402
|
+
input.settings
|
|
5403
|
+
);
|
|
5404
|
+
const personalConnectionDelegations = await freezePersonalConnectionDelegations({
|
|
5405
|
+
db: input.db,
|
|
5406
|
+
workspaceId: input.existing.workspaceId,
|
|
5407
|
+
settings: runtimeSettings,
|
|
5408
|
+
tools: nextAgentConfig.tools,
|
|
5409
|
+
source: personalConnectionDelegationSourceForGrant(input.grant)
|
|
5410
|
+
});
|
|
5411
|
+
if (input.existing.reusableSessionId && input.existing.runMode === "reusable_session") {
|
|
5412
|
+
const existingDelegations = await getScheduledTaskPersonalConnectionDelegations(
|
|
5413
|
+
input.db,
|
|
5414
|
+
input.existing.workspaceId,
|
|
5415
|
+
input.existing.id
|
|
5416
|
+
);
|
|
5417
|
+
if (!personalConnectionDelegationsEqual(existingDelegations, personalConnectionDelegations)) {
|
|
5418
|
+
throw new HTTPException11(409, {
|
|
5419
|
+
message: "cannot change personal MCP connections of a task with a live reusable session; recreate the task"
|
|
5420
|
+
});
|
|
5421
|
+
}
|
|
5422
|
+
}
|
|
5423
|
+
update.personalConnectionDelegations = personalConnectionDelegations;
|
|
5195
5424
|
}
|
|
5196
5425
|
return update;
|
|
5197
5426
|
}
|
|
@@ -5202,7 +5431,18 @@ async function requireScheduledTaskForApi(db, workspaceId, taskId) {
|
|
|
5202
5431
|
}
|
|
5203
5432
|
return task;
|
|
5204
5433
|
}
|
|
5205
|
-
async function
|
|
5434
|
+
async function captureScheduledTaskRestoreState(db, task) {
|
|
5435
|
+
return {
|
|
5436
|
+
task,
|
|
5437
|
+
personalConnectionDelegations: await getScheduledTaskPersonalConnectionDelegations(
|
|
5438
|
+
db,
|
|
5439
|
+
task.workspaceId,
|
|
5440
|
+
task.id
|
|
5441
|
+
)
|
|
5442
|
+
};
|
|
5443
|
+
}
|
|
5444
|
+
async function restoreScheduledTask(db, previous) {
|
|
5445
|
+
const { task } = previous;
|
|
5206
5446
|
return await updateScheduledTask(db, task.workspaceId, task.id, {
|
|
5207
5447
|
name: task.name,
|
|
5208
5448
|
status: task.status,
|
|
@@ -5210,8 +5450,10 @@ async function restoreScheduledTask(db, task) {
|
|
|
5210
5450
|
runMode: task.runMode,
|
|
5211
5451
|
overlapPolicy: task.overlapPolicy,
|
|
5212
5452
|
agentConfig: task.agentConfig,
|
|
5453
|
+
personalConnectionDelegations: previous.personalConnectionDelegations,
|
|
5213
5454
|
reusableSessionId: task.reusableSessionId,
|
|
5214
5455
|
variableSetId: task.variableSetId,
|
|
5456
|
+
rigId: task.rigId,
|
|
5215
5457
|
metadata: task.metadata
|
|
5216
5458
|
});
|
|
5217
5459
|
}
|
|
@@ -5737,6 +5979,236 @@ async function getWorkspaceInsights(db, settings, input) {
|
|
|
5737
5979
|
return { snapshot };
|
|
5738
5980
|
}
|
|
5739
5981
|
|
|
5982
|
+
// src/domain/memory-slack-publication.ts
|
|
5983
|
+
import { createHash } from "crypto";
|
|
5984
|
+
import {
|
|
5985
|
+
redactSensitiveText,
|
|
5986
|
+
stableJson as stableJson3
|
|
5987
|
+
} from "@opengeni/contracts";
|
|
5988
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
5989
|
+
var SELECTOR_SEGMENT_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/;
|
|
5990
|
+
var encoder = new TextEncoder();
|
|
5991
|
+
var decoder = new TextDecoder();
|
|
5992
|
+
var MEMORY_SLACK_PROJECTION_VERSION = 1;
|
|
5993
|
+
var MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES = 512;
|
|
5994
|
+
var MEMORY_SLACK_NAMESPACE_MAX_UTF8_BYTES = 128;
|
|
5995
|
+
var MEMORY_SLACK_LABEL_MAX_UTF8_BYTES = 64;
|
|
5996
|
+
var MEMORY_SLACK_LABEL_MAX_COUNT = 8;
|
|
5997
|
+
var MEMORY_SLACK_OWNER_MAX_UTF8_BYTES = 96;
|
|
5998
|
+
var MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES = 4096;
|
|
5999
|
+
var DEFAULT_MEMORY_SLACK_PUBLICATION_POLICY = {
|
|
6000
|
+
enabled: false,
|
|
6001
|
+
autoImportances: ["major"],
|
|
6002
|
+
reviewImportances: ["normal"]
|
|
6003
|
+
};
|
|
6004
|
+
var IMPORTANCES = /* @__PURE__ */ new Set(["major", "normal", "minor"]);
|
|
6005
|
+
var REQUESTED_MODES = /* @__PURE__ */ new Set(["auto", "review", "never"]);
|
|
6006
|
+
var CHANGE_KINDS = /* @__PURE__ */ new Set(["created", "corrected", "superseded"]);
|
|
6007
|
+
var ORIGINS = /* @__PURE__ */ new Set(["native", "slack_derived"]);
|
|
6008
|
+
var AUDIENCES = /* @__PURE__ */ new Set(["workspace", "restricted"]);
|
|
6009
|
+
var SCOPE_TYPES = /* @__PURE__ */ new Set([
|
|
6010
|
+
"workspace",
|
|
6011
|
+
"user",
|
|
6012
|
+
"role",
|
|
6013
|
+
"session",
|
|
6014
|
+
"ephemeral",
|
|
6015
|
+
"legacy"
|
|
6016
|
+
]);
|
|
6017
|
+
var MEMORY_STATUSES = /* @__PURE__ */ new Set([
|
|
6018
|
+
"proposed",
|
|
6019
|
+
"approved",
|
|
6020
|
+
"rejected",
|
|
6021
|
+
"active",
|
|
6022
|
+
"superseded",
|
|
6023
|
+
"archived"
|
|
6024
|
+
]);
|
|
6025
|
+
var MEMORY_KINDS = /* @__PURE__ */ new Set([
|
|
6026
|
+
"semantic",
|
|
6027
|
+
"episodic",
|
|
6028
|
+
"procedural",
|
|
6029
|
+
"decision",
|
|
6030
|
+
"preference"
|
|
6031
|
+
]);
|
|
6032
|
+
var VISIBLE_STATUSES = /* @__PURE__ */ new Set(["active", "approved"]);
|
|
6033
|
+
function evaluateMemorySlackPublication(input) {
|
|
6034
|
+
const policy = input.policy ?? DEFAULT_MEMORY_SLACK_PUBLICATION_POLICY;
|
|
6035
|
+
if (!validPolicy(policy)) return denied("invalid_input");
|
|
6036
|
+
if (!policy.enabled) return denied("disabled");
|
|
6037
|
+
if (input.memory.sourceType !== "workspace_memory") return denied("unsupported_source");
|
|
6038
|
+
if (!validIdentity(input)) return denied("invalid_input");
|
|
6039
|
+
if (input.memory.accountId !== input.context.accountId || input.memory.workspaceId !== input.context.workspaceId) {
|
|
6040
|
+
return denied("tenant_mismatch");
|
|
6041
|
+
}
|
|
6042
|
+
if (input.memory.kind !== "decision") return denied("not_decision");
|
|
6043
|
+
if (input.memory.scopeType !== "workspace") return denied("restricted_scope");
|
|
6044
|
+
if (input.distribution.audience !== "workspace") return denied("restricted_audience");
|
|
6045
|
+
if (input.change.origin === "slack_derived") return denied("slack_origin_loop");
|
|
6046
|
+
if (input.distribution.slackMode === "never") return denied("mode_never");
|
|
6047
|
+
const timeDecision = evaluateValidity(input);
|
|
6048
|
+
if (timeDecision) return denied(timeDecision);
|
|
6049
|
+
if (!statusEligibleForChange(input)) return denied("status_ineligible");
|
|
6050
|
+
if (!validChangeLineage(input)) return denied("invalid_change_lineage");
|
|
6051
|
+
const deliveryMode = effectiveDeliveryMode(policy, input.distribution);
|
|
6052
|
+
if (!deliveryMode) return denied("below_noise_policy");
|
|
6053
|
+
const collapsedSummary = collapseText(input.distribution.shareSummary);
|
|
6054
|
+
const redactedSummary = redactSensitiveText(collapsedSummary);
|
|
6055
|
+
if (!redactedSummary) return denied("missing_summary");
|
|
6056
|
+
const summary = truncateUtf8(redactedSummary, MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES);
|
|
6057
|
+
const namespace = normalizeNamespace(input.memory.namespace);
|
|
6058
|
+
const labels = normalizeLabels(input.memory.labels);
|
|
6059
|
+
if (!namespace || !labels) return denied("invalid_input");
|
|
6060
|
+
const owner = boundedOptionalText(input.change.ownerLabel, MEMORY_SLACK_OWNER_MAX_UTF8_BYTES);
|
|
6061
|
+
const projection = {
|
|
6062
|
+
version: MEMORY_SLACK_PROJECTION_VERSION,
|
|
6063
|
+
workspaceId: input.context.workspaceId,
|
|
6064
|
+
memoryId: input.memory.id,
|
|
6065
|
+
memoryVersion: input.memory.version,
|
|
6066
|
+
changeKind: input.change.kind,
|
|
6067
|
+
relatedMemoryId: input.change.relatedMemoryId,
|
|
6068
|
+
occurredAt: new Date(input.change.occurredAt).toISOString(),
|
|
6069
|
+
importance: input.distribution.importance,
|
|
6070
|
+
deliveryMode,
|
|
6071
|
+
summary: summary.value,
|
|
6072
|
+
summaryRedacted: redactedSummary !== collapsedSummary,
|
|
6073
|
+
summaryTruncated: summary.truncated,
|
|
6074
|
+
namespace,
|
|
6075
|
+
labels: labels.values,
|
|
6076
|
+
labelsTruncated: labels.truncated,
|
|
6077
|
+
ownerLabel: owner.value,
|
|
6078
|
+
ownerLabelRedacted: owner.redacted,
|
|
6079
|
+
ownerLabelTruncated: owner.truncated,
|
|
6080
|
+
authoritativeRecord: {
|
|
6081
|
+
workspaceId: input.context.workspaceId,
|
|
6082
|
+
memoryId: input.memory.id
|
|
6083
|
+
}
|
|
6084
|
+
};
|
|
6085
|
+
if (utf8Bytes(stableJson3(projection)) > MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES) {
|
|
6086
|
+
return denied("invalid_input");
|
|
6087
|
+
}
|
|
6088
|
+
const digest = createHash("sha256").update(stableJson3(projection), "utf8").digest("hex");
|
|
6089
|
+
return {
|
|
6090
|
+
eligible: true,
|
|
6091
|
+
deliveryMode,
|
|
6092
|
+
idempotencyKey: `memory-slack:v${MEMORY_SLACK_PROJECTION_VERSION}:${digest}`,
|
|
6093
|
+
projection
|
|
6094
|
+
};
|
|
6095
|
+
}
|
|
6096
|
+
function validPolicy(policy) {
|
|
6097
|
+
return typeof policy.enabled === "boolean" && Array.isArray(policy.autoImportances) && Array.isArray(policy.reviewImportances) && policy.autoImportances.every((importance) => IMPORTANCES.has(importance)) && policy.reviewImportances.every((importance) => IMPORTANCES.has(importance));
|
|
6098
|
+
}
|
|
6099
|
+
function validIdentity(input) {
|
|
6100
|
+
const uuids = [
|
|
6101
|
+
input.context.accountId,
|
|
6102
|
+
input.context.workspaceId,
|
|
6103
|
+
input.memory.accountId,
|
|
6104
|
+
input.memory.workspaceId,
|
|
6105
|
+
input.memory.id,
|
|
6106
|
+
input.memory.supersedesId,
|
|
6107
|
+
input.memory.supersededById,
|
|
6108
|
+
input.change.relatedMemoryId
|
|
6109
|
+
].filter((value) => value !== null);
|
|
6110
|
+
return uuids.every((value) => UUID_PATTERN.test(value)) && Number.isSafeInteger(input.memory.version) && input.memory.version > 0 && typeof input.memory.namespace === "string" && Array.isArray(input.memory.labels) && input.memory.labels.every((label) => typeof label === "string") && (input.change.ownerLabel === void 0 || input.change.ownerLabel === null || typeof input.change.ownerLabel === "string") && typeof input.distribution.shareSummary === "string" && IMPORTANCES.has(input.distribution.importance) && REQUESTED_MODES.has(input.distribution.slackMode) && CHANGE_KINDS.has(input.change.kind) && ORIGINS.has(input.change.origin) && AUDIENCES.has(input.distribution.audience) && SCOPE_TYPES.has(input.memory.scopeType) && MEMORY_STATUSES.has(input.memory.status) && MEMORY_KINDS.has(input.memory.kind);
|
|
6111
|
+
}
|
|
6112
|
+
function evaluateValidity(input) {
|
|
6113
|
+
const now = Date.parse(input.context.now);
|
|
6114
|
+
const occurredAt = Date.parse(input.change.occurredAt);
|
|
6115
|
+
const validFrom = Date.parse(input.memory.validFrom);
|
|
6116
|
+
const validUntil = input.memory.validUntil === null ? null : Date.parse(input.memory.validUntil);
|
|
6117
|
+
if (!Number.isFinite(now) || !Number.isFinite(occurredAt) || !Number.isFinite(validFrom) || validUntil !== null && !Number.isFinite(validUntil) || validUntil !== null && validUntil <= validFrom) {
|
|
6118
|
+
return "invalid_input";
|
|
6119
|
+
}
|
|
6120
|
+
if (now < validFrom) return "not_yet_valid";
|
|
6121
|
+
if (validUntil !== null && now >= validUntil) return "expired";
|
|
6122
|
+
return null;
|
|
6123
|
+
}
|
|
6124
|
+
function validChangeLineage(input) {
|
|
6125
|
+
if (input.memory.supersedesId === input.memory.id || input.memory.supersededById === input.memory.id || input.change.relatedMemoryId === input.memory.id) {
|
|
6126
|
+
return false;
|
|
6127
|
+
}
|
|
6128
|
+
switch (input.change.kind) {
|
|
6129
|
+
case "created":
|
|
6130
|
+
return input.change.relatedMemoryId === null && input.memory.supersedesId === null && input.memory.supersededById === null;
|
|
6131
|
+
case "corrected":
|
|
6132
|
+
return input.change.relatedMemoryId !== null && input.change.relatedMemoryId !== input.memory.id && input.memory.supersedesId === input.change.relatedMemoryId && input.memory.supersededById === null;
|
|
6133
|
+
case "superseded":
|
|
6134
|
+
return input.change.relatedMemoryId !== null && input.change.relatedMemoryId !== input.memory.id && input.memory.supersededById === input.change.relatedMemoryId;
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
function statusEligibleForChange(input) {
|
|
6138
|
+
return input.change.kind === "superseded" ? input.memory.status === "superseded" : VISIBLE_STATUSES.has(input.memory.status);
|
|
6139
|
+
}
|
|
6140
|
+
function effectiveDeliveryMode(policy, distribution) {
|
|
6141
|
+
const auto = new Set(policy.autoImportances);
|
|
6142
|
+
const review = new Set(policy.reviewImportances);
|
|
6143
|
+
if (distribution.slackMode === "review") {
|
|
6144
|
+
return auto.has(distribution.importance) || review.has(distribution.importance) ? "review" : null;
|
|
6145
|
+
}
|
|
6146
|
+
if (auto.has(distribution.importance)) return "auto";
|
|
6147
|
+
if (review.has(distribution.importance)) return "review";
|
|
6148
|
+
return null;
|
|
6149
|
+
}
|
|
6150
|
+
function normalizeNamespace(value) {
|
|
6151
|
+
const trimmed = value.trim();
|
|
6152
|
+
if (redactSensitiveText(trimmed) !== trimmed) return null;
|
|
6153
|
+
const namespace = trimmed.toLowerCase();
|
|
6154
|
+
if (!namespace || utf8Bytes(namespace) > MEMORY_SLACK_NAMESPACE_MAX_UTF8_BYTES) return null;
|
|
6155
|
+
if (redactSensitiveText(namespace) !== namespace) return null;
|
|
6156
|
+
const segments = namespace.split("/");
|
|
6157
|
+
if (segments.some((segment) => !SELECTOR_SEGMENT_PATTERN.test(segment))) return null;
|
|
6158
|
+
return segments.join("/");
|
|
6159
|
+
}
|
|
6160
|
+
function normalizeLabels(values) {
|
|
6161
|
+
if (!Array.isArray(values)) return null;
|
|
6162
|
+
const labels = /* @__PURE__ */ new Set();
|
|
6163
|
+
for (const value of values) {
|
|
6164
|
+
if (typeof value !== "string") return null;
|
|
6165
|
+
const trimmed = value.trim();
|
|
6166
|
+
if (redactSensitiveText(trimmed) !== trimmed) return null;
|
|
6167
|
+
const label = trimmed.toLowerCase();
|
|
6168
|
+
if (redactSensitiveText(label) !== label || !SELECTOR_SEGMENT_PATTERN.test(label) || utf8Bytes(label) > MEMORY_SLACK_LABEL_MAX_UTF8_BYTES) {
|
|
6169
|
+
return null;
|
|
6170
|
+
}
|
|
6171
|
+
labels.add(label);
|
|
6172
|
+
}
|
|
6173
|
+
const sorted = [...labels].sort();
|
|
6174
|
+
return {
|
|
6175
|
+
values: sorted.slice(0, MEMORY_SLACK_LABEL_MAX_COUNT),
|
|
6176
|
+
truncated: sorted.length > MEMORY_SLACK_LABEL_MAX_COUNT
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
function boundedOptionalText(value, maxBytes) {
|
|
6180
|
+
const collapsed = collapseText(value ?? "");
|
|
6181
|
+
if (!collapsed) return { value: null, redacted: false, truncated: false };
|
|
6182
|
+
const redacted = redactSensitiveText(collapsed);
|
|
6183
|
+
const bounded = truncateUtf8(redacted, maxBytes);
|
|
6184
|
+
return {
|
|
6185
|
+
value: bounded.value || null,
|
|
6186
|
+
redacted: redacted !== collapsed,
|
|
6187
|
+
truncated: bounded.truncated
|
|
6188
|
+
};
|
|
6189
|
+
}
|
|
6190
|
+
function collapseText(value) {
|
|
6191
|
+
return value.replace(/[\u0000-\u001F\u007F-\u009F]/g, " ").replace(/\s+/g, " ").trim();
|
|
6192
|
+
}
|
|
6193
|
+
function truncateUtf8(value, maxBytes) {
|
|
6194
|
+
const bytes = encoder.encode(value);
|
|
6195
|
+
if (bytes.byteLength <= maxBytes) return { value, truncated: false };
|
|
6196
|
+
const marker = "\u2026";
|
|
6197
|
+
const markerBytes = encoder.encode(marker);
|
|
6198
|
+
let end = Math.max(0, maxBytes - markerBytes.byteLength);
|
|
6199
|
+
while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
|
|
6200
|
+
return {
|
|
6201
|
+
value: `${decoder.decode(bytes.subarray(0, end)).trimEnd()}${marker}`,
|
|
6202
|
+
truncated: true
|
|
6203
|
+
};
|
|
6204
|
+
}
|
|
6205
|
+
function utf8Bytes(value) {
|
|
6206
|
+
return encoder.encode(value).byteLength;
|
|
6207
|
+
}
|
|
6208
|
+
function denied(reason) {
|
|
6209
|
+
return { eligible: false, reason };
|
|
6210
|
+
}
|
|
6211
|
+
|
|
5740
6212
|
// src/domain/workspace-members.ts
|
|
5741
6213
|
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
5742
6214
|
var MEMBER_ADMIN_PERMISSIONS = ["workspace:admin", "members:manage"];
|
|
@@ -6450,6 +6922,7 @@ async function saveHumanComposerDraft(deps, context, input) {
|
|
|
6450
6922
|
export {
|
|
6451
6923
|
CODEX_COMPACTION_V2_PROVIDER_LOCKED,
|
|
6452
6924
|
CodexCompactionV2ProviderLockedError,
|
|
6925
|
+
DEFAULT_MEMORY_SLACK_PUBLICATION_POLICY,
|
|
6453
6926
|
MARKETING_SOCIAL_PACK_ID,
|
|
6454
6927
|
MAX_CHECKS_PER_RIG,
|
|
6455
6928
|
MAX_CREDENTIAL_HOOKS_PER_RIG,
|
|
@@ -6457,6 +6930,13 @@ export {
|
|
|
6457
6930
|
MAX_ENVIRONMENTS_PER_WORKSPACE,
|
|
6458
6931
|
MAX_RIGS_PER_WORKSPACE,
|
|
6459
6932
|
MAX_VARIABLES_PER_ENVIRONMENT,
|
|
6933
|
+
MEMORY_SLACK_LABEL_MAX_COUNT,
|
|
6934
|
+
MEMORY_SLACK_LABEL_MAX_UTF8_BYTES,
|
|
6935
|
+
MEMORY_SLACK_NAMESPACE_MAX_UTF8_BYTES,
|
|
6936
|
+
MEMORY_SLACK_OWNER_MAX_UTF8_BYTES,
|
|
6937
|
+
MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES,
|
|
6938
|
+
MEMORY_SLACK_PROJECTION_VERSION,
|
|
6939
|
+
MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES,
|
|
6460
6940
|
SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
|
|
6461
6941
|
SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS,
|
|
6462
6942
|
SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID,
|
|
@@ -6484,6 +6964,7 @@ export {
|
|
|
6484
6964
|
buildFleetContextForSession,
|
|
6485
6965
|
buildMarketingDailyAnalysisAgentConfig,
|
|
6486
6966
|
canonicalConfiguredModel,
|
|
6967
|
+
captureScheduledTaskRestoreState,
|
|
6487
6968
|
checkLimit,
|
|
6488
6969
|
classifyRigVerificationOutcome,
|
|
6489
6970
|
controlAgentSessionWorkstream,
|
|
@@ -6495,16 +6976,20 @@ export {
|
|
|
6495
6976
|
createRigVersionForApi,
|
|
6496
6977
|
createSessionForRequest,
|
|
6497
6978
|
createValidatedScheduledTask,
|
|
6979
|
+
creationInitiatorForGrant,
|
|
6498
6980
|
defaultSessionMcpServerIds,
|
|
6499
6981
|
deleteHumanQueuePrompt,
|
|
6500
6982
|
deleteRigForApi,
|
|
6983
|
+
directPersonalConnectionSubjectId,
|
|
6501
6984
|
disableCapability,
|
|
6502
6985
|
discoverMcpRegistryCapabilities,
|
|
6503
6986
|
editHumanQueuePrompt,
|
|
6504
6987
|
enableCapability,
|
|
6505
6988
|
enabledCapabilityMcpToolRefs,
|
|
6989
|
+
evaluateMemorySlackPublication,
|
|
6506
6990
|
executeRunOnSelfhostedMachine,
|
|
6507
6991
|
filenameForMimeType,
|
|
6992
|
+
freezePersonalConnectionDelegations,
|
|
6508
6993
|
getActorNewSessionDraft,
|
|
6509
6994
|
getCapabilityPack,
|
|
6510
6995
|
getHumanComposerDraft,
|
|
@@ -6534,6 +7019,11 @@ export {
|
|
|
6534
7019
|
normalizeResources,
|
|
6535
7020
|
officialMcpRegistryUrl,
|
|
6536
7021
|
openGeniSlackBotMetadata,
|
|
7022
|
+
personalConnectionDelegationForServer,
|
|
7023
|
+
personalConnectionDelegationSourceForGrant,
|
|
7024
|
+
personalConnectionDelegationsEqual,
|
|
7025
|
+
personalConnectionDelegationsFromParent,
|
|
7026
|
+
personalConnectionDelegationsFromVisibleConnections,
|
|
6537
7027
|
postUserMessageTurn,
|
|
6538
7028
|
promoteSetupAppendChange,
|
|
6539
7029
|
promoteVerifiedDefinitionEditChangeForApi,
|
|
@@ -6575,6 +7065,7 @@ export {
|
|
|
6575
7065
|
scheduledTaskTemporalScheduleId,
|
|
6576
7066
|
scheduledTaskToolsProvided,
|
|
6577
7067
|
scheduledTaskTriggerToken,
|
|
7068
|
+
selectedPersonalConnectionServers,
|
|
6578
7069
|
sendAgentSessionMessage,
|
|
6579
7070
|
sessionSpawnDenialEnvelope,
|
|
6580
7071
|
sessionWithEffectiveToolPolicy,
|
|
@@ -6604,6 +7095,7 @@ export {
|
|
|
6604
7095
|
validateVariableSetAttachment,
|
|
6605
7096
|
validatedScheduledTaskUpdate,
|
|
6606
7097
|
withDefaultEnabledCapabilityMcpTools,
|
|
7098
|
+
withFrozenPersonalConnectionDelegations,
|
|
6607
7099
|
workflowIdForSession,
|
|
6608
7100
|
workspaceSessionToolPolicyDefaultServerIds,
|
|
6609
7101
|
workspaceSessionToolPolicyServerIds,
|