@opengeni/core 0.21.10 → 0.28.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/access/index.d.ts +1 -1
- package/dist/application/new-session-drafts.d.ts +1 -1
- package/dist/application/session-commands.d.ts +2 -2
- package/dist/billing/limits.d.ts +1 -1
- package/dist/canonical-human-identities.d.ts +12 -0
- package/dist/canonical-human-identities.js +23 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/chunk-6WKPWE5S.js +2380 -0
- package/dist/chunk-6WKPWE5S.js.map +1 -0
- package/dist/chunk-IBOEYG6N.js +34 -0
- package/dist/chunk-IBOEYG6N.js.map +1 -0
- package/dist/chunk-JJU6I5XD.js +2713 -0
- package/dist/chunk-JJU6I5XD.js.map +1 -0
- package/dist/chunk-NYPMQ7NO.js +2270 -0
- package/dist/chunk-NYPMQ7NO.js.map +1 -0
- package/dist/dependencies.d.ts +30 -6
- package/dist/domain/capabilities.d.ts +28 -3
- package/dist/domain/company-profile-durable-learning-adapter.d.ts +39 -0
- package/dist/domain/conversation-integrations.d.ts +225 -0
- package/dist/domain/durable-learning-slack-publication.d.ts +72 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +192 -0
- package/dist/domain/editable-artifacts/durable-export-postgres.d.ts +4 -0
- package/dist/domain/editable-artifacts/durable-export.d.ts +212 -0
- package/dist/domain/editable-artifacts/errors.d.ts +47 -0
- package/dist/domain/editable-artifacts/hash.d.ts +11 -0
- package/dist/domain/editable-artifacts/in-memory.d.ts +99 -0
- package/dist/domain/editable-artifacts/index.d.ts +14 -0
- package/dist/domain/editable-artifacts/ports.d.ts +341 -0
- package/dist/domain/editable-artifacts/postgres-adapters.d.ts +13 -0
- package/dist/domain/editable-artifacts/service.d.ts +105 -0
- package/dist/domain/editable-artifacts/snapshot-compaction.d.ts +44 -0
- package/dist/domain/editable-artifacts/snapshot-verifier-genesis.d.ts +50 -0
- package/dist/domain/editable-artifacts/snapshot-verifier-materialization.d.ts +21 -0
- package/dist/domain/editable-artifacts/snapshot-verifier.d.ts +118 -0
- package/dist/domain/editable-artifacts/types.d.ts +410 -0
- package/dist/domain/fiken.d.ts +35 -0
- package/dist/domain/memory-slack-delivery.d.ts +19 -0
- package/dist/domain/packs.d.ts +29 -1
- package/dist/domain/personal-connection-delegations.d.ts +9 -1
- package/dist/domain/scheduled-tasks.d.ts +2 -2
- package/dist/domain/session-tool-policy.d.ts +2 -2
- package/dist/domain/sessions.d.ts +24 -8
- package/dist/domain/skill-imports.d.ts +32 -0
- package/dist/domain/slack-publication-secret-safety.d.ts +8 -0
- package/dist/domain/timeline-annotations.d.ts +5 -0
- package/dist/domain/video-generation-capabilities.d.ts +10 -0
- package/dist/domain/video-generation.d.ts +47 -0
- package/dist/editable-artifact-live/application.d.ts +69 -0
- package/dist/editable-artifact-live/errors.d.ts +1 -0
- package/dist/editable-artifact-live/index.d.ts +8 -0
- package/dist/editable-artifact-live/outbox-dispatcher.d.ts +162 -0
- package/dist/editable-artifact-live/ports.d.ts +100 -0
- package/dist/editable-artifact-live/server.d.ts +71 -0
- package/dist/editable-artifact-live/ticket.d.ts +40 -0
- package/dist/editable-artifact-live/types.d.ts +213 -0
- package/dist/editable-artifact-live/wire.d.ts +34 -0
- package/dist/editable-artifact-live.d.ts +2 -0
- package/dist/editable-artifact-live.js +58 -0
- package/dist/editable-artifact-live.js.map +1 -0
- package/dist/editable-artifacts.d.ts +2 -0
- package/dist/editable-artifacts.js +179 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/index.d.ts +37 -26
- package/dist/index.js +4145 -959
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +6 -2
- package/dist/rigs/index.d.ts +1 -1
- package/dist/rigs/provider-images.d.ts +31 -0
- package/dist/sandbox/fleet.d.ts +8 -5
- package/dist/sandbox/routing.d.ts +1 -0
- package/dist/session-authorization.d.ts +4 -2
- package/dist/transcription.d.ts +5 -0
- package/dist/workflow-wake-contract.d.ts +7 -0
- package/package.json +22 -10
- package/src/access/index.ts +14 -2
- package/src/application/new-session-drafts.ts +2 -0
- package/src/application/session-commands.ts +188 -107
- package/src/canonical-human-identities.ts +27 -0
- package/src/dependencies.ts +34 -2
- package/src/domain/capabilities.ts +729 -408
- package/src/domain/company-profile-durable-learning-adapter.ts +221 -0
- package/src/domain/conversation-integrations.ts +1238 -0
- package/src/domain/durable-learning-slack-publication.ts +202 -0
- package/src/domain/editable-artifacts/agent-application.ts +687 -0
- package/src/domain/editable-artifacts/durable-export-postgres.ts +139 -0
- package/src/domain/editable-artifacts/durable-export.ts +734 -0
- package/src/domain/editable-artifacts/errors.ts +115 -0
- package/src/domain/editable-artifacts/hash.ts +131 -0
- package/src/domain/editable-artifacts/in-memory.ts +1662 -0
- package/src/domain/editable-artifacts/index.ts +14 -0
- package/src/domain/editable-artifacts/ports.ts +462 -0
- package/src/domain/editable-artifacts/postgres-adapters.ts +49 -0
- package/src/domain/editable-artifacts/service.ts +2402 -0
- package/src/domain/editable-artifacts/snapshot-compaction.ts +246 -0
- package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +235 -0
- package/src/domain/editable-artifacts/snapshot-verifier-materialization.ts +74 -0
- package/src/domain/editable-artifacts/snapshot-verifier.ts +687 -0
- package/src/domain/editable-artifacts/types.ts +835 -0
- package/src/domain/fiken.ts +88 -0
- package/src/domain/insights.ts +163 -50
- package/src/domain/memory-slack-delivery.ts +204 -0
- package/src/domain/memory-slack-publication.ts +5 -2
- package/src/domain/packs.ts +266 -5
- package/src/domain/personal-connection-delegations.ts +97 -6
- package/src/domain/scheduled-tasks.ts +171 -29
- package/src/domain/session-tool-policy.ts +8 -2
- package/src/domain/sessions.ts +395 -148
- package/src/domain/skill-imports.ts +350 -0
- package/src/domain/slack-publication-secret-safety.ts +42 -0
- package/src/domain/timeline-annotations.ts +204 -0
- package/src/domain/video-generation-capabilities.ts +104 -0
- package/src/domain/video-generation.ts +249 -0
- package/src/editable-artifact-live/application.ts +182 -0
- package/src/editable-artifact-live/errors.ts +4 -0
- package/src/editable-artifact-live/index.ts +8 -0
- package/src/editable-artifact-live/outbox-dispatcher.ts +916 -0
- package/src/editable-artifact-live/ports.ts +139 -0
- package/src/editable-artifact-live/server.ts +1707 -0
- package/src/editable-artifact-live/ticket.ts +217 -0
- package/src/editable-artifact-live/types.ts +286 -0
- package/src/editable-artifact-live/wire.ts +95 -0
- package/src/editable-artifact-live.ts +2 -0
- package/src/editable-artifacts.ts +2 -0
- package/src/index.ts +15 -0
- package/src/managed-session.ts +19 -2
- package/src/rigs/index.ts +26 -19
- package/src/rigs/provider-images.ts +110 -0
- package/src/sandbox/fleet.ts +72 -66
- package/src/sandbox/routing.ts +6 -3
- package/src/session-authorization.ts +187 -52
- package/src/transcription.ts +5 -0
- package/src/workflow-wake-contract.ts +8 -0
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type KnowledgeMemoryKind,
|
|
5
5
|
type KnowledgeMemoryStatus,
|
|
6
6
|
} from "@opengeni/contracts";
|
|
7
|
+
import { sanitizeSlackPublicationText } from "./slack-publication-secret-safety";
|
|
7
8
|
|
|
8
9
|
const 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}$/;
|
|
9
10
|
const SELECTOR_SEGMENT_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/;
|
|
@@ -197,7 +198,9 @@ export function evaluateMemorySlackPublication(
|
|
|
197
198
|
const deliveryMode = effectiveDeliveryMode(policy, input.distribution);
|
|
198
199
|
if (!deliveryMode) return denied("below_noise_policy");
|
|
199
200
|
|
|
200
|
-
const collapsedSummary =
|
|
201
|
+
const collapsedSummary = sanitizeSlackPublicationText(
|
|
202
|
+
collapseText(input.distribution.shareSummary),
|
|
203
|
+
);
|
|
201
204
|
if (!collapsedSummary) return denied("missing_summary");
|
|
202
205
|
const summary = truncateUtf8(collapsedSummary, MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES);
|
|
203
206
|
|
|
@@ -397,7 +400,7 @@ function boundedOptionalText(
|
|
|
397
400
|
value: string | null | undefined,
|
|
398
401
|
maxBytes: number,
|
|
399
402
|
): { value: string | null; truncated: boolean } {
|
|
400
|
-
const collapsed = collapseText(value ?? "");
|
|
403
|
+
const collapsed = sanitizeSlackPublicationText(collapseText(value ?? ""));
|
|
401
404
|
if (!collapsed) return { value: null, truncated: false };
|
|
402
405
|
const bounded = truncateUtf8(collapsed, maxBytes);
|
|
403
406
|
return {
|
package/src/domain/packs.ts
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
CapabilityPack,
|
|
5
|
+
stableJson,
|
|
6
|
+
type CapabilityPackSkill,
|
|
7
|
+
type PackComponentResolution,
|
|
8
|
+
type PackInstallationPreview,
|
|
9
|
+
type PackRigResolution,
|
|
3
10
|
type ScheduledTaskAgentConfig,
|
|
4
11
|
type SocialConnection,
|
|
5
12
|
} from "@opengeni/contracts";
|
|
6
13
|
import {
|
|
14
|
+
getPackInstallation,
|
|
15
|
+
getRig,
|
|
16
|
+
getVariableSet,
|
|
7
17
|
getWorkspacePack,
|
|
8
18
|
listPackInstallations,
|
|
9
19
|
listWorkspacePacks,
|
|
20
|
+
resolvePackComponentReferences,
|
|
21
|
+
resolvePackInlineSkillReferences,
|
|
10
22
|
type Database,
|
|
11
23
|
} from "@opengeni/db";
|
|
24
|
+
import { buildPortableSkillArtifact } from "@opengeni/runtime/skill-library";
|
|
12
25
|
import { HTTPException } from "hono/http-exception";
|
|
13
26
|
|
|
14
27
|
export const MARKETING_SOCIAL_PACK_ID = "marketing-social-daily-analysis";
|
|
@@ -25,6 +38,7 @@ const marketingSocialPack: CapabilityPack = {
|
|
|
25
38
|
// worker's pack-runtime resolution only reads manifest-registered packs
|
|
26
39
|
// (see apps/worker/src/activities/packs.ts), and a test enforces this.
|
|
27
40
|
skills: [],
|
|
41
|
+
components: [],
|
|
28
42
|
tools: [
|
|
29
43
|
{ kind: "mcp", id: "opengeni" },
|
|
30
44
|
{ kind: "mcp", id: "docs" },
|
|
@@ -146,11 +160,18 @@ const marketingSocialPack: CapabilityPack = {
|
|
|
146
160
|
"social_connections_list",
|
|
147
161
|
"social_posts_recent",
|
|
148
162
|
"social_daily_analysis_context",
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
163
|
+
"x_accounts_list",
|
|
164
|
+
"x_search_live",
|
|
165
|
+
"x_mentions_live",
|
|
166
|
+
"x_thread_fetch",
|
|
167
|
+
"x_posts_sync",
|
|
168
|
+
"x_post_reply",
|
|
169
|
+
"reddit_accounts_list",
|
|
170
|
+
"reddit_search_live",
|
|
171
|
+
"reddit_mentions_live",
|
|
172
|
+
"reddit_thread_fetch",
|
|
173
|
+
"reddit_posts_sync",
|
|
174
|
+
"reddit_post_reply",
|
|
154
175
|
],
|
|
155
176
|
},
|
|
156
177
|
};
|
|
@@ -207,6 +228,246 @@ export async function resolveCapabilityPack(
|
|
|
207
228
|
return parsed.success ? parsed.data : null;
|
|
208
229
|
}
|
|
209
230
|
|
|
231
|
+
export function capabilityPackManifestDigest(pack: CapabilityPack): string {
|
|
232
|
+
return createHash("sha256").update(stableJson(pack)).digest("hex");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** True when the Pack must use the owner-aware V2 preview/install lifecycle. */
|
|
236
|
+
export function capabilityPackRequiresInstallationPlan(pack: CapabilityPack): boolean {
|
|
237
|
+
return (
|
|
238
|
+
pack.components.length > 0 ||
|
|
239
|
+
pack.skills.length > 0 ||
|
|
240
|
+
pack.rig !== undefined ||
|
|
241
|
+
pack.sandboxImage !== undefined ||
|
|
242
|
+
pack.sandboxProviderImages !== undefined
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export type InlinePackSkillInstall = {
|
|
247
|
+
componentKey: string;
|
|
248
|
+
capabilityId: string;
|
|
249
|
+
pluginKey: string;
|
|
250
|
+
sourceUrl: string;
|
|
251
|
+
repositoryUrl: string;
|
|
252
|
+
sourceCommit: string;
|
|
253
|
+
sourcePath: string;
|
|
254
|
+
name: string;
|
|
255
|
+
description: string;
|
|
256
|
+
contentSha256: string;
|
|
257
|
+
totalBytes: number;
|
|
258
|
+
files: Array<{ path: string; content: string; byteSize: number; contentSha256: string }>;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/** Convert a legacy inline Pack Skill into the ordinary immutable Skill model. */
|
|
262
|
+
export function inlinePackSkillInstall(
|
|
263
|
+
pack: CapabilityPack,
|
|
264
|
+
skill: CapabilityPackSkill,
|
|
265
|
+
): InlinePackSkillInstall {
|
|
266
|
+
const artifact = buildPortableSkillArtifact(skill.files);
|
|
267
|
+
if (artifact.name.toLowerCase() !== skill.name.toLowerCase()) {
|
|
268
|
+
throw new HTTPException(422, {
|
|
269
|
+
message: `Pack Skill ${skill.name} has SKILL.md name ${artifact.name}; the names must match`,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
const normalizedName = skill.name.toLowerCase();
|
|
273
|
+
const encodedSkill = encodeURIComponent(normalizedName);
|
|
274
|
+
const sourceUrl = `https://opengeni.invalid/pack-inline-skills/${encodedSkill}/${artifact.contentSha256}`;
|
|
275
|
+
const capabilityId = `skill:pack-inline/${normalizedName}@${artifact.contentSha256}`;
|
|
276
|
+
return {
|
|
277
|
+
componentKey: `inline-skill/${normalizedName}`,
|
|
278
|
+
capabilityId,
|
|
279
|
+
pluginKey: `pack-skill/${normalizedName}/${artifact.contentSha256}`,
|
|
280
|
+
sourceUrl,
|
|
281
|
+
repositoryUrl: "https://opengeni.invalid/pack-inline-skills",
|
|
282
|
+
sourceCommit: artifact.contentSha256,
|
|
283
|
+
sourcePath: normalizedName,
|
|
284
|
+
name: artifact.name,
|
|
285
|
+
description: artifact.description,
|
|
286
|
+
contentSha256: artifact.contentSha256,
|
|
287
|
+
totalBytes: artifact.totalBytes,
|
|
288
|
+
files: artifact.files.map((file) => ({
|
|
289
|
+
path: file.path,
|
|
290
|
+
content: file.content,
|
|
291
|
+
byteSize: new TextEncoder().encode(file.content).byteLength,
|
|
292
|
+
contentSha256: createHash("sha256").update(file.content).digest("hex"),
|
|
293
|
+
})),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function previewCapabilityPackInstallation(
|
|
298
|
+
db: Database,
|
|
299
|
+
workspaceId: string,
|
|
300
|
+
pack: CapabilityPack,
|
|
301
|
+
options: { rigId?: string; variableSetId?: string } = {},
|
|
302
|
+
): Promise<PackInstallationPreview> {
|
|
303
|
+
const installation = await getPackInstallation(db, workspaceId, pack.id);
|
|
304
|
+
const inlineInstalls = pack.skills.map((skill) => inlinePackSkillInstall(pack, skill));
|
|
305
|
+
const [referencedComponents, inlineComponents] = await Promise.all([
|
|
306
|
+
resolvePackComponentReferences(db, workspaceId, pack.components),
|
|
307
|
+
resolvePackInlineSkillReferences(
|
|
308
|
+
db,
|
|
309
|
+
workspaceId,
|
|
310
|
+
inlineInstalls.map((inline) => ({
|
|
311
|
+
key: inline.componentKey,
|
|
312
|
+
capabilityId: inline.capabilityId,
|
|
313
|
+
name: inline.name,
|
|
314
|
+
contentSha256: inline.contentSha256,
|
|
315
|
+
})),
|
|
316
|
+
installation?.id,
|
|
317
|
+
),
|
|
318
|
+
]);
|
|
319
|
+
const manifestDigest = capabilityPackManifestDigest(pack);
|
|
320
|
+
const components: PackComponentResolution[] = [...referencedComponents, ...inlineComponents];
|
|
321
|
+
const blockers = components
|
|
322
|
+
.filter((component) => component.required && component.status !== "ready")
|
|
323
|
+
.map((component) =>
|
|
324
|
+
component.status === "missing"
|
|
325
|
+
? `${component.label} is not installed in this workspace`
|
|
326
|
+
: `${component.label} does not match the Pack's pinned version`,
|
|
327
|
+
);
|
|
328
|
+
const rig = await resolvePackRig(
|
|
329
|
+
db,
|
|
330
|
+
workspaceId,
|
|
331
|
+
pack,
|
|
332
|
+
options.rigId,
|
|
333
|
+
installation?.selectedRigId,
|
|
334
|
+
);
|
|
335
|
+
if (
|
|
336
|
+
rig.status !== "ready" &&
|
|
337
|
+
rig.status !== "not_required" &&
|
|
338
|
+
(rig.required || rig.requestedRigId !== null || rig.rigId !== null)
|
|
339
|
+
) {
|
|
340
|
+
blockers.push(packRigBlocker(rig, pack.sandboxImage ?? null));
|
|
341
|
+
}
|
|
342
|
+
const variableSetId =
|
|
343
|
+
options.variableSetId ?? storedVariableSetId(installation?.metadata) ?? null;
|
|
344
|
+
if (pack.variableSet?.required && !variableSetId) {
|
|
345
|
+
blockers.push("Choose saved configuration before installing this Pack");
|
|
346
|
+
} else if (variableSetId) {
|
|
347
|
+
const variableSet = await getVariableSet(db, workspaceId, variableSetId);
|
|
348
|
+
if (!variableSet) {
|
|
349
|
+
blockers.push("The selected configuration no longer exists in this workspace");
|
|
350
|
+
} else {
|
|
351
|
+
const missing = (pack.variableSet?.requiredVariables ?? []).filter(
|
|
352
|
+
(name) => !variableSet.variables.some((variable) => variable.name === name),
|
|
353
|
+
);
|
|
354
|
+
if (missing.length > 0) {
|
|
355
|
+
blockers.push(`Configuration is missing required value(s): ${missing.join(", ")}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const action =
|
|
360
|
+
!installation || installation.status === "disabled"
|
|
361
|
+
? "install"
|
|
362
|
+
: installation.manifestDigest === manifestDigest
|
|
363
|
+
? "repair"
|
|
364
|
+
: "update";
|
|
365
|
+
return {
|
|
366
|
+
packId: pack.id,
|
|
367
|
+
packVersion: pack.version,
|
|
368
|
+
manifestDigest,
|
|
369
|
+
installationVersion: installation?.version ?? null,
|
|
370
|
+
action,
|
|
371
|
+
ready: blockers.length === 0,
|
|
372
|
+
blockers,
|
|
373
|
+
components,
|
|
374
|
+
rig,
|
|
375
|
+
variableSetId,
|
|
376
|
+
legacyInlineSkillCount: pack.skills.length,
|
|
377
|
+
legacySandboxImage: pack.sandboxImage ?? null,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function resolvePackRig(
|
|
382
|
+
db: Database,
|
|
383
|
+
workspaceId: string,
|
|
384
|
+
pack: CapabilityPack,
|
|
385
|
+
requestedRigId: string | undefined,
|
|
386
|
+
storedRigId: string | null | undefined,
|
|
387
|
+
): Promise<PackRigResolution> {
|
|
388
|
+
const required = pack.rig?.required === true || Boolean(pack.sandboxImage);
|
|
389
|
+
const selectedRigId = pack.rig?.rigId ?? requestedRigId ?? storedRigId ?? null;
|
|
390
|
+
if (!required && !selectedRigId) {
|
|
391
|
+
return {
|
|
392
|
+
required: false,
|
|
393
|
+
status: "not_required",
|
|
394
|
+
requestedRigId: requestedRigId ?? null,
|
|
395
|
+
rigId: null,
|
|
396
|
+
rigVersionId: null,
|
|
397
|
+
name: null,
|
|
398
|
+
image: null,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (pack.rig?.rigId && requestedRigId && pack.rig.rigId !== requestedRigId) {
|
|
402
|
+
return {
|
|
403
|
+
required,
|
|
404
|
+
status: "mismatch",
|
|
405
|
+
requestedRigId,
|
|
406
|
+
rigId: pack.rig.rigId,
|
|
407
|
+
rigVersionId: null,
|
|
408
|
+
name: null,
|
|
409
|
+
image: null,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (!selectedRigId) {
|
|
413
|
+
return {
|
|
414
|
+
required,
|
|
415
|
+
status: "missing",
|
|
416
|
+
requestedRigId: requestedRigId ?? null,
|
|
417
|
+
rigId: null,
|
|
418
|
+
rigVersionId: null,
|
|
419
|
+
name: null,
|
|
420
|
+
image: null,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const rig = await getRig(db, workspaceId, selectedRigId);
|
|
424
|
+
if (!rig?.activeVersion) {
|
|
425
|
+
return {
|
|
426
|
+
required,
|
|
427
|
+
status: "missing",
|
|
428
|
+
requestedRigId: requestedRigId ?? null,
|
|
429
|
+
rigId: selectedRigId,
|
|
430
|
+
rigVersionId: null,
|
|
431
|
+
name: rig?.name ?? null,
|
|
432
|
+
image: null,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const image = rig.activeVersion.image ?? null;
|
|
436
|
+
const status =
|
|
437
|
+
pack.sandboxImage && image !== pack.sandboxImage
|
|
438
|
+
? "mismatch"
|
|
439
|
+
: pack.rig?.requireVerified && rig.activeVersionHealth?.checkHealth !== "passing"
|
|
440
|
+
? "unverified"
|
|
441
|
+
: "ready";
|
|
442
|
+
return {
|
|
443
|
+
required,
|
|
444
|
+
status,
|
|
445
|
+
requestedRigId: requestedRigId ?? null,
|
|
446
|
+
rigId: rig.id,
|
|
447
|
+
rigVersionId: rig.activeVersion.id,
|
|
448
|
+
name: rig.name,
|
|
449
|
+
image,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function packRigBlocker(rig: PackRigResolution, legacyImage: string | null): string {
|
|
454
|
+
if (rig.status === "missing") {
|
|
455
|
+
return "Choose an available compute environment before installing this Pack";
|
|
456
|
+
}
|
|
457
|
+
if (rig.status === "unverified") {
|
|
458
|
+
return "Verify the selected compute environment before installing this Pack";
|
|
459
|
+
}
|
|
460
|
+
if (legacyImage) {
|
|
461
|
+
return `The selected compute environment must use the Pack's previous image ${legacyImage}; the Pack will not replace workspace compute`;
|
|
462
|
+
}
|
|
463
|
+
return "The selected compute environment does not satisfy this Pack's requirement";
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function storedVariableSetId(metadata: Record<string, unknown> | undefined): string | null {
|
|
467
|
+
const value = metadata?.variableSetId ?? metadata?.environmentId;
|
|
468
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
469
|
+
}
|
|
470
|
+
|
|
210
471
|
/**
|
|
211
472
|
* v1 pack-scoped runtime rule: at most one enabled pack per workspace may
|
|
212
473
|
* declare a `sandboxImage` — there is deliberately no image composition or
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from "@opengeni/contracts";
|
|
10
10
|
import {
|
|
11
11
|
getSessionTurnPersonalConnectionDelegations,
|
|
12
|
+
getConnectionMetadata,
|
|
12
13
|
getSocialConnection,
|
|
13
14
|
getWorkspaceGrant,
|
|
14
15
|
listConnectionsMetadata,
|
|
@@ -23,6 +24,10 @@ export type PersonalConnectionDelegationSource =
|
|
|
23
24
|
| { kind: "turn"; sessionId: string; turnId: string }
|
|
24
25
|
| { kind: "none" };
|
|
25
26
|
export type AuthorizedSocialConnection = { connection: SocialConnection; subjectId: string | null };
|
|
27
|
+
export type AuthorizedAtlassianConnection = {
|
|
28
|
+
connection: ConnectionMetadata;
|
|
29
|
+
subjectId: string | null;
|
|
30
|
+
};
|
|
26
31
|
|
|
27
32
|
export function directPersonalConnectionSubjectId(
|
|
28
33
|
turn: Pick<SessionTurn, "source" | "initiator" | "initiatorContext">,
|
|
@@ -107,6 +112,70 @@ export async function authorizedSocialConnectionsForGrant(input: {
|
|
|
107
112
|
return [...workspace.map((connection) => ({ connection, subjectId: null })), ...personal];
|
|
108
113
|
}
|
|
109
114
|
|
|
115
|
+
export async function authorizedAtlassianConnectionsForGrant(input: {
|
|
116
|
+
db: Database;
|
|
117
|
+
grant: AccessGrant;
|
|
118
|
+
}): Promise<AuthorizedAtlassianConnection[]> {
|
|
119
|
+
const workspace = (await listConnectionsMetadata(input.db, input.grant.workspaceId, null)).filter(
|
|
120
|
+
(connection) =>
|
|
121
|
+
connection.subjectId === null &&
|
|
122
|
+
connection.status === "active" &&
|
|
123
|
+
sameProviderDomain(connection.providerDomain, "api.atlassian.com"),
|
|
124
|
+
);
|
|
125
|
+
const source = personalConnectionDelegationSourceForGrant(input.grant);
|
|
126
|
+
if (source.kind === "none") {
|
|
127
|
+
return workspace.map((connection) => ({ connection, subjectId: null }));
|
|
128
|
+
}
|
|
129
|
+
if (source.kind === "subject") {
|
|
130
|
+
const visible = await listConnectionsMetadata(
|
|
131
|
+
input.db,
|
|
132
|
+
input.grant.workspaceId,
|
|
133
|
+
source.subjectId,
|
|
134
|
+
);
|
|
135
|
+
return visible
|
|
136
|
+
.filter(
|
|
137
|
+
(connection) =>
|
|
138
|
+
connection.status === "active" &&
|
|
139
|
+
sameProviderDomain(connection.providerDomain, "api.atlassian.com"),
|
|
140
|
+
)
|
|
141
|
+
.map((connection) => ({
|
|
142
|
+
connection,
|
|
143
|
+
subjectId: connection.subjectId === null ? null : source.subjectId,
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
const delegations = (
|
|
147
|
+
await getSessionTurnPersonalConnectionDelegations(
|
|
148
|
+
input.db,
|
|
149
|
+
input.grant.workspaceId,
|
|
150
|
+
source.sessionId,
|
|
151
|
+
source.turnId,
|
|
152
|
+
)
|
|
153
|
+
).filter((item) => item.connectionType === "atlassian");
|
|
154
|
+
const personal: AuthorizedAtlassianConnection[] = [];
|
|
155
|
+
for (const delegation of delegations) {
|
|
156
|
+
if (!(await getWorkspaceGrant(input.db, delegation.ownerSubjectId, input.grant.workspaceId))) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const connection = await getConnectionMetadata(
|
|
160
|
+
input.db,
|
|
161
|
+
input.grant.workspaceId,
|
|
162
|
+
delegation.connectionId,
|
|
163
|
+
delegation.ownerSubjectId,
|
|
164
|
+
);
|
|
165
|
+
if (
|
|
166
|
+
!connection ||
|
|
167
|
+
connection.subjectId !== delegation.ownerSubjectId ||
|
|
168
|
+
connection.status !== "active" ||
|
|
169
|
+
!sameProviderDomain(connection.providerDomain, delegation.providerDomain) ||
|
|
170
|
+
!sameProviderDomain(connection.providerDomain, "api.atlassian.com")
|
|
171
|
+
) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
personal.push({ connection, subjectId: delegation.ownerSubjectId });
|
|
175
|
+
}
|
|
176
|
+
return [...workspace.map((connection) => ({ connection, subjectId: null })), ...personal];
|
|
177
|
+
}
|
|
178
|
+
|
|
110
179
|
export function selectedPersonalConnectionServers(
|
|
111
180
|
settings: Pick<Settings, "mcpServers">,
|
|
112
181
|
tools: ToolRef[],
|
|
@@ -181,7 +250,7 @@ export function personalConnectionDelegationsFromParent(input: {
|
|
|
181
250
|
return [
|
|
182
251
|
...mcp,
|
|
183
252
|
...input.parentDelegations
|
|
184
|
-
.filter((item) => item.connectionType === "social")
|
|
253
|
+
.filter((item) => item.connectionType === "social" || item.connectionType === "atlassian")
|
|
185
254
|
.map((item) => ({ ...item })),
|
|
186
255
|
];
|
|
187
256
|
}
|
|
@@ -241,7 +310,7 @@ function personalAuthorityUnavailable(
|
|
|
241
310
|
|
|
242
311
|
/**
|
|
243
312
|
* Resolves subject-owned MCP credentials only through the exact authority
|
|
244
|
-
* frozen on the causal turn. A direct human subject, worker
|
|
313
|
+
* frozen on the causal turn. A direct human subject, worker Codemode caller,
|
|
245
314
|
* retry, or recovery can identify the caller, but none may widen or replace
|
|
246
315
|
* the persisted connection UUID.
|
|
247
316
|
*/
|
|
@@ -288,8 +357,10 @@ export async function freezePersonalConnectionDelegations(input: {
|
|
|
288
357
|
source: PersonalConnectionDelegationSource;
|
|
289
358
|
}): Promise<McpPersonalConnectionDelegation[]> {
|
|
290
359
|
const servers = selectedPersonalConnectionServers(input.settings, input.tools);
|
|
291
|
-
const
|
|
292
|
-
if ((servers.length === 0 && !
|
|
360
|
+
const includeFirstPartyConnections = input.tools.some((tool) => tool.id === "opengeni");
|
|
361
|
+
if ((servers.length === 0 && !includeFirstPartyConnections) || input.source.kind === "none") {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
293
364
|
if (input.source.kind === "turn") {
|
|
294
365
|
const inherited = personalConnectionDelegationsFromParent({
|
|
295
366
|
servers,
|
|
@@ -300,7 +371,11 @@ export async function freezePersonalConnectionDelegations(input: {
|
|
|
300
371
|
input.source.turnId,
|
|
301
372
|
),
|
|
302
373
|
});
|
|
303
|
-
return
|
|
374
|
+
return includeFirstPartyConnections
|
|
375
|
+
? inherited
|
|
376
|
+
: inherited.filter(
|
|
377
|
+
(item) => item.connectionType !== "social" && item.connectionType !== "atlassian",
|
|
378
|
+
);
|
|
304
379
|
}
|
|
305
380
|
const membership = await getWorkspaceGrant(input.db, input.source.subjectId, input.workspaceId);
|
|
306
381
|
if (!membership) return [];
|
|
@@ -309,7 +384,7 @@ export async function freezePersonalConnectionDelegations(input: {
|
|
|
309
384
|
subjectId: input.source.subjectId,
|
|
310
385
|
connections: await listConnectionsMetadata(input.db, input.workspaceId, input.source.subjectId),
|
|
311
386
|
});
|
|
312
|
-
if (!
|
|
387
|
+
if (!includeFirstPartyConnections) return mcp;
|
|
313
388
|
const ownerSubjectId = input.source.subjectId;
|
|
314
389
|
const visible = await listSocialConnections(input.db, input.workspaceId, 500, ownerSubjectId);
|
|
315
390
|
const latest = new Map<"x" | "reddit", (typeof visible)[number]>();
|
|
@@ -324,6 +399,14 @@ export async function freezePersonalConnectionDelegations(input: {
|
|
|
324
399
|
if (!prior || connection.updatedAt > prior.updatedAt)
|
|
325
400
|
latest.set(connection.provider, connection);
|
|
326
401
|
}
|
|
402
|
+
const personalAtlassian = (
|
|
403
|
+
await listConnectionsMetadata(input.db, input.workspaceId, ownerSubjectId)
|
|
404
|
+
).filter(
|
|
405
|
+
(connection) =>
|
|
406
|
+
connection.subjectId === ownerSubjectId &&
|
|
407
|
+
connection.status === "active" &&
|
|
408
|
+
sameProviderDomain(connection.providerDomain, "api.atlassian.com"),
|
|
409
|
+
);
|
|
327
410
|
return [
|
|
328
411
|
...mcp,
|
|
329
412
|
...[...latest.values()].map((connection) => ({
|
|
@@ -334,5 +417,13 @@ export async function freezePersonalConnectionDelegations(input: {
|
|
|
334
417
|
kind: "oauth2" as const,
|
|
335
418
|
connectionType: "social" as const,
|
|
336
419
|
})),
|
|
420
|
+
...personalAtlassian.map((connection) => ({
|
|
421
|
+
serverId: `atlassian:${connection.id}`,
|
|
422
|
+
connectionId: connection.id,
|
|
423
|
+
ownerSubjectId,
|
|
424
|
+
providerDomain: connection.providerDomain,
|
|
425
|
+
kind: connection.kind,
|
|
426
|
+
connectionType: "atlassian" as const,
|
|
427
|
+
})),
|
|
337
428
|
];
|
|
338
429
|
}
|