@opengeni/core 2.8.0-canary.0 → 2.8.2-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/dependencies.d.ts +4 -2
- package/dist/domain/github-skill-source.d.ts +5 -0
- package/dist/domain/packs.d.ts +2 -3
- package/dist/domain/pr-review.d.ts +2 -1
- package/dist/domain/remember.d.ts +2 -2
- package/dist/domain/sessions.d.ts +1 -0
- package/dist/domain/skill-search.d.ts +30 -0
- package/dist/domain/skills.d.ts +73 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +523 -30
- package/dist/index.js.map +1 -1
- package/dist/transcription.d.ts +12 -1
- package/package.json +12 -12
- package/src/application/session-tenancy.ts +1 -0
- package/src/dependencies.ts +4 -2
- package/src/domain/github-skill-source.ts +142 -0
- package/src/domain/packs.ts +16 -8
- package/src/domain/product-integration-pack.ts +1 -0
- package/src/domain/remember.ts +7 -0
- package/src/domain/scheduled-tasks.ts +28 -0
- package/src/domain/sessions.ts +17 -0
- package/src/domain/skill-imports.ts +99 -15
- package/src/domain/skill-search.ts +218 -0
- package/src/domain/skills.ts +87 -0
- package/src/index.ts +3 -0
- package/src/sandbox/routing.ts +40 -1
- package/src/sandbox/runtime-settings.ts +9 -2
- package/src/transcription.ts +13 -1
package/dist/index.js
CHANGED
|
@@ -128,6 +128,73 @@ import {
|
|
|
128
128
|
validateEditableArtifactActor
|
|
129
129
|
} from "./chunk-VCZFFEPB.js";
|
|
130
130
|
|
|
131
|
+
// src/domain/skills.ts
|
|
132
|
+
import { validateSkillTextFiles } from "@opengeni/contracts";
|
|
133
|
+
import { buildPortableSkillArtifact } from "@opengeni/runtime/skill-library";
|
|
134
|
+
import {
|
|
135
|
+
applySkillLifecycle,
|
|
136
|
+
listSkillRecords,
|
|
137
|
+
skillFilesContentHash
|
|
138
|
+
} from "@opengeni/db";
|
|
139
|
+
import { replayPortableSkillInstall } from "@opengeni/db";
|
|
140
|
+
function validateSkillFiles(files) {
|
|
141
|
+
validateSkillTextFiles(files);
|
|
142
|
+
const result = files.map(({ path, content }) => ({ path, content }));
|
|
143
|
+
if (!result.some((file) => file.path === "SKILL.md" && file.content.trim()))
|
|
144
|
+
throw new Error("Skill requires nonempty SKILL.md");
|
|
145
|
+
return result.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
146
|
+
}
|
|
147
|
+
function skillBundleHash(files) {
|
|
148
|
+
return skillFilesContentHash(validateSkillFiles(files));
|
|
149
|
+
}
|
|
150
|
+
var listSkills = listSkillRecords;
|
|
151
|
+
async function readSkill(db, context, skillId, revisionId) {
|
|
152
|
+
return (await listSkillRecords(db, context, {
|
|
153
|
+
skillId,
|
|
154
|
+
...revisionId ? { revisionId } : {},
|
|
155
|
+
limit: 1
|
|
156
|
+
}))[0] ?? null;
|
|
157
|
+
}
|
|
158
|
+
async function saveSkill(db, input) {
|
|
159
|
+
const { accountId, workspaceId, actor, ...request } = input;
|
|
160
|
+
const artifact = buildPortableSkillArtifact(validateSkillFiles(input.files));
|
|
161
|
+
return applySkillLifecycle(
|
|
162
|
+
db,
|
|
163
|
+
{ accountId, workspaceId, actor },
|
|
164
|
+
{
|
|
165
|
+
...request,
|
|
166
|
+
operation: "save",
|
|
167
|
+
files: artifact.files,
|
|
168
|
+
title: artifact.name,
|
|
169
|
+
description: artifact.description
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
async function installSkill(db, input) {
|
|
174
|
+
const { accountId, workspaceId, actor, ...request } = input;
|
|
175
|
+
return applySkillLifecycle(
|
|
176
|
+
db,
|
|
177
|
+
{ accountId, workspaceId, actor },
|
|
178
|
+
{ ...request, operation: "install" }
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
async function approveSkill(db, input) {
|
|
182
|
+
const { accountId, workspaceId, actor, ...request } = input;
|
|
183
|
+
return applySkillLifecycle(
|
|
184
|
+
db,
|
|
185
|
+
{ accountId, workspaceId, actor },
|
|
186
|
+
{ ...request, operation: "approve" }
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
async function restoreSkill(db, input) {
|
|
190
|
+
const { accountId, workspaceId, actor, ...request } = input;
|
|
191
|
+
return applySkillLifecycle(
|
|
192
|
+
db,
|
|
193
|
+
{ accountId, workspaceId, actor },
|
|
194
|
+
{ ...request, operation: "restore" }
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
131
198
|
// src/workflow-wake-contract.ts
|
|
132
199
|
var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
|
|
133
200
|
var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
|
|
@@ -140,12 +207,15 @@ var TranscriptionServiceError = class extends Error {
|
|
|
140
207
|
code;
|
|
141
208
|
status;
|
|
142
209
|
retryable;
|
|
210
|
+
/** Explicit rejection before any transcription result; safe to try another provider. */
|
|
211
|
+
fallbackSafe;
|
|
143
212
|
constructor(input) {
|
|
144
213
|
super(input.message);
|
|
145
214
|
this.name = "TranscriptionServiceError";
|
|
146
215
|
this.code = input.code;
|
|
147
216
|
this.status = input.status ?? statusForVoiceInputError(input.code);
|
|
148
217
|
this.retryable = input.retryable ?? false;
|
|
218
|
+
this.fallbackSafe = input.fallbackSafe ?? false;
|
|
149
219
|
}
|
|
150
220
|
};
|
|
151
221
|
function statusForVoiceInputError(code) {
|
|
@@ -718,6 +788,11 @@ import {
|
|
|
718
788
|
|
|
719
789
|
// src/sandbox/routing.ts
|
|
720
790
|
import { sandboxLifecycleTransitionWaitMs } from "@opengeni/config";
|
|
791
|
+
import { appendSessionCommandOutput } from "@opengeni/db/session-command-output";
|
|
792
|
+
import {
|
|
793
|
+
createProviderCommandRetainer,
|
|
794
|
+
retainedProviderCommandPersistence
|
|
795
|
+
} from "@opengeni/db/retained-provider-commands";
|
|
721
796
|
import {
|
|
722
797
|
advanceWorkspaceGenerationForDirectRequest,
|
|
723
798
|
advanceWorkspaceGenerationForRetainedProcess,
|
|
@@ -727,6 +802,7 @@ import {
|
|
|
727
802
|
markWarmLeaseInstanceLost,
|
|
728
803
|
readActiveSandbox,
|
|
729
804
|
retainWorkspaceMutationProcess,
|
|
805
|
+
SandboxRetainedProcessPromotionFencedError,
|
|
730
806
|
retainedProcessSettlementIdentity,
|
|
731
807
|
settleRetainedProcess,
|
|
732
808
|
verifyDirectWorkspaceMutationSettlement,
|
|
@@ -741,6 +817,10 @@ import {
|
|
|
741
817
|
RoutingSandboxSession,
|
|
742
818
|
resolveModalCheckpointProviderBindingForSession
|
|
743
819
|
} from "@opengeni/runtime/sandbox";
|
|
820
|
+
var retainWorkspaceProviderCommand = createProviderCommandRetainer(
|
|
821
|
+
retainWorkspaceMutationProcess,
|
|
822
|
+
(error) => error instanceof SandboxRetainedProcessPromotionFencedError ? error.process : null
|
|
823
|
+
);
|
|
744
824
|
function directRetainedProcessMatchesBackend(durable, process, backend) {
|
|
745
825
|
return durable.providerSessionId === process.providerSessionId && durable.providerBackend === backend.kind && durable.providerInstanceId === backend.providerInstanceId && durable.leaseEpoch === backend.leaseEpoch && durable.routeKind === "active" && durable.routeTargetId === backend.sandboxId && durable.routeEpoch === backend.activeEpoch;
|
|
746
826
|
}
|
|
@@ -860,12 +940,13 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
860
940
|
throw new Error("API-direct workspace mutation settlement lacked its bound admission");
|
|
861
941
|
}
|
|
862
942
|
if (outcome === "resolved" && retainedProcess) {
|
|
863
|
-
await
|
|
943
|
+
await retainWorkspaceProviderCommand(db, {
|
|
864
944
|
accountId: ids.accountId,
|
|
865
945
|
workspaceId: ids.workspaceId,
|
|
866
946
|
sessionId: ids.sessionId,
|
|
867
947
|
processId: retainedProcess.id,
|
|
868
948
|
providerSessionId: retainedProcess.providerSessionId,
|
|
949
|
+
...retainedProcess.providerCommand ? { providerCommand: retainedProcess.providerCommand } : {},
|
|
869
950
|
admissionId: exactAdmission.id,
|
|
870
951
|
admittedWorkspaceGeneration: exactAdmission.workspaceGeneration,
|
|
871
952
|
operation: op,
|
|
@@ -1022,6 +1103,27 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
1022
1103
|
} : {}
|
|
1023
1104
|
});
|
|
1024
1105
|
const proxy = new RoutingSandboxSession({
|
|
1106
|
+
providerCommandHandle: (value) => value && typeof value === "object" ? value.admission?.workspaceGeneration : void 0,
|
|
1107
|
+
providerCommandPersistence: (process) => retainedProviderCommandPersistence(db, {
|
|
1108
|
+
accountId: ids.accountId,
|
|
1109
|
+
workspaceId: ids.workspaceId,
|
|
1110
|
+
sessionId: ids.sessionId,
|
|
1111
|
+
processId: process.id
|
|
1112
|
+
}),
|
|
1113
|
+
captureProcessOutput: async ({ process, chunkId, chunk, stream, streamFidelity }) => {
|
|
1114
|
+
const events = await appendSessionCommandOutput(db, {
|
|
1115
|
+
accountId: ids.accountId,
|
|
1116
|
+
workspaceId: ids.workspaceId,
|
|
1117
|
+
sessionId: ids.sessionId,
|
|
1118
|
+
commandId: process.id,
|
|
1119
|
+
chunkId,
|
|
1120
|
+
chunk,
|
|
1121
|
+
stream,
|
|
1122
|
+
streamFidelity
|
|
1123
|
+
});
|
|
1124
|
+
if (events.length && bus)
|
|
1125
|
+
await bus.publish(ids.workspaceId, ids.sessionId, events).catch(() => void 0);
|
|
1126
|
+
},
|
|
1025
1127
|
bindActiveRouteOnFirstResolve: true,
|
|
1026
1128
|
defaultResolved: {
|
|
1027
1129
|
session: established.session,
|
|
@@ -1079,7 +1181,7 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
1079
1181
|
|
|
1080
1182
|
// src/sandbox/runtime-settings.ts
|
|
1081
1183
|
import {
|
|
1082
|
-
|
|
1184
|
+
StoredCapabilityPack
|
|
1083
1185
|
} from "@opengeni/contracts";
|
|
1084
1186
|
import {
|
|
1085
1187
|
getRigVersion,
|
|
@@ -1157,8 +1259,14 @@ async function resolveWorkspaceLegacyRuntimePacks(db, workspaceId) {
|
|
|
1157
1259
|
continue;
|
|
1158
1260
|
}
|
|
1159
1261
|
const registration = await getWorkspacePack(db, workspaceId, installation.packId);
|
|
1160
|
-
|
|
1161
|
-
|
|
1262
|
+
if (!registration)
|
|
1263
|
+
throw new Error(`Enabled Pack ${installation.packId} has no registered manifest`);
|
|
1264
|
+
const parsed = StoredCapabilityPack.safeParse(registration.pack);
|
|
1265
|
+
if (!parsed.success)
|
|
1266
|
+
throw new Error(
|
|
1267
|
+
`Enabled Pack ${installation.packId} requires repair: ${parsed.error.message}`
|
|
1268
|
+
);
|
|
1269
|
+
packs2.push(parsed.data);
|
|
1162
1270
|
}
|
|
1163
1271
|
return packs2;
|
|
1164
1272
|
}
|
|
@@ -2939,7 +3047,7 @@ import { listSkillLibraryEntries } from "@opengeni/runtime/skill-library";
|
|
|
2939
3047
|
// src/domain/packs.ts
|
|
2940
3048
|
import { createHash as createHash3 } from "crypto";
|
|
2941
3049
|
import {
|
|
2942
|
-
|
|
3050
|
+
StoredCapabilityPack as StoredCapabilityPack2,
|
|
2943
3051
|
OPENGENI_PR_REVIEW_PACK_ID,
|
|
2944
3052
|
OPENGENI_PR_REVIEW_SESSION_ROLE as OPENGENI_PR_REVIEW_SESSION_ROLE2,
|
|
2945
3053
|
stableJson as stableJson2
|
|
@@ -2954,7 +3062,7 @@ import {
|
|
|
2954
3062
|
resolvePackComponentReferences,
|
|
2955
3063
|
resolvePackInlineSkillReferences
|
|
2956
3064
|
} from "@opengeni/db";
|
|
2957
|
-
import { buildPortableSkillArtifact } from "@opengeni/runtime/skill-library";
|
|
3065
|
+
import { buildPortableSkillArtifact as buildPortableSkillArtifact2 } from "@opengeni/runtime/skill-library";
|
|
2958
3066
|
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
2959
3067
|
|
|
2960
3068
|
// src/domain/pr-review.ts
|
|
@@ -3486,6 +3594,7 @@ The desired outcome is a native-feeling product experience backed by a standalon
|
|
|
3486
3594
|
- When an unknown choice is reversible and low-risk, choose the best-fitting default, state the assumption, and continue. When it changes privacy, tenant authority, write access, cost exposure, or an external mutation, resolve it before crossing that boundary.
|
|
3487
3595
|
- Possession of a credential or access to a cloud, repository, or deployment is technical capability, not authorization. Match the user's requested delivery autonomy and the repository's stated workflow.
|
|
3488
3596
|
- Keep alternatives open until evidence eliminates them. Use strict rules only for actual security, privacy, protocol, or authorization invariants.
|
|
3597
|
+
- For packaged React chat, use SessionConversation or compose MessageTimeline and ChatComposer with the normal SDK and authenticated session routes. For a custom or compatible frontend, use the optional backend chat handler.
|
|
3489
3598
|
|
|
3490
3599
|
Read the references selectively:
|
|
3491
3600
|
|
|
@@ -4197,9 +4306,13 @@ function isBuiltInCapabilityPack(packId) {
|
|
|
4197
4306
|
async function listWorkspaceCapabilityPacks(db, workspaceId) {
|
|
4198
4307
|
const registered = await listWorkspacePacks(db, workspaceId);
|
|
4199
4308
|
const builtInIds = new Set(packs.map((pack) => pack.id));
|
|
4200
|
-
const registeredPacks = registered.filter((registration) => !builtInIds.has(registration.pack.id)).
|
|
4201
|
-
const parsed =
|
|
4202
|
-
|
|
4309
|
+
const registeredPacks = registered.filter((registration) => !builtInIds.has(registration.pack.id)).map((registration) => {
|
|
4310
|
+
const parsed = StoredCapabilityPack2.safeParse(registration.pack);
|
|
4311
|
+
if (!parsed.success)
|
|
4312
|
+
throw new HTTPException3(422, {
|
|
4313
|
+
message: `Stored Pack ${registration.pack.id} requires repair before it can be used: ${parsed.error.message}`
|
|
4314
|
+
});
|
|
4315
|
+
return parsed.data;
|
|
4203
4316
|
});
|
|
4204
4317
|
return [...packs, ...registeredPacks];
|
|
4205
4318
|
}
|
|
@@ -4212,8 +4325,12 @@ async function resolveCapabilityPack(db, workspaceId, packId) {
|
|
|
4212
4325
|
if (!registration) {
|
|
4213
4326
|
return null;
|
|
4214
4327
|
}
|
|
4215
|
-
const parsed =
|
|
4216
|
-
|
|
4328
|
+
const parsed = StoredCapabilityPack2.safeParse(registration.pack);
|
|
4329
|
+
if (!parsed.success)
|
|
4330
|
+
throw new HTTPException3(422, {
|
|
4331
|
+
message: `Stored Pack ${packId} requires repair before it can be used: ${parsed.error.message}`
|
|
4332
|
+
});
|
|
4333
|
+
return parsed.data;
|
|
4217
4334
|
}
|
|
4218
4335
|
function capabilityPackManifestDigest(pack) {
|
|
4219
4336
|
return createHash3("sha256").update(stableJson2(pack)).digest("hex");
|
|
@@ -4222,7 +4339,7 @@ function capabilityPackRequiresInstallationPlan(pack) {
|
|
|
4222
4339
|
return pack.components.length > 0 || pack.skills.length > 0 || (pack.automationTemplates?.length ?? 0) > 0 || pack.rig !== void 0 || pack.sandboxImage !== void 0 || pack.sandboxProviderImages !== void 0;
|
|
4223
4340
|
}
|
|
4224
4341
|
function inlinePackSkillInstall(pack, skill) {
|
|
4225
|
-
const artifact =
|
|
4342
|
+
const artifact = buildPortableSkillArtifact2(skill.files);
|
|
4226
4343
|
if (artifact.name.toLowerCase() !== skill.name.toLowerCase()) {
|
|
4227
4344
|
throw new HTTPException3(422, {
|
|
4228
4345
|
message: `Pack Skill ${skill.name} has SKILL.md name ${artifact.name}; the names must match`
|
|
@@ -4919,13 +5036,13 @@ async function probeStreamableHttpMcpServer(input) {
|
|
|
4919
5036
|
function mcpProbeErrorMessage(error, endpointUrl) {
|
|
4920
5037
|
const message = error instanceof Error ? error.message : String(error);
|
|
4921
5038
|
const normalized = message.replace(/\s+/g, " ").trim();
|
|
4922
|
-
const
|
|
5039
|
+
const endpoint2 = safeEndpointLabel(endpointUrl);
|
|
4923
5040
|
if (/404|405|not found|unexpected token|not valid json|invalid json|failed to parse|streamable http error|unable to connect|fetch failed|econnrefused|enotfound|timeout|aborted/i.test(
|
|
4924
5041
|
normalized
|
|
4925
5042
|
)) {
|
|
4926
|
-
return `OpenGeni could not reach a valid Streamable HTTP MCP server at ${
|
|
5043
|
+
return `OpenGeni could not reach a valid Streamable HTTP MCP server at ${endpoint2}. Check the endpoint URL or choose a different catalog entry.`;
|
|
4927
5044
|
}
|
|
4928
|
-
return `OpenGeni could not initialize ${
|
|
5045
|
+
return `OpenGeni could not initialize ${endpoint2}. Check the endpoint configuration or try again.`;
|
|
4929
5046
|
}
|
|
4930
5047
|
function safeEndpointLabel(endpointUrl) {
|
|
4931
5048
|
try {
|
|
@@ -5920,7 +6037,9 @@ function storedConnectionRef(config) {
|
|
|
5920
6037
|
// src/domain/skill-imports.ts
|
|
5921
6038
|
import { createHash as createHash4 } from "crypto";
|
|
5922
6039
|
import {
|
|
5923
|
-
buildPortableSkillArtifact as
|
|
6040
|
+
buildPortableSkillArtifact as buildPortableSkillArtifact3,
|
|
6041
|
+
parsePortableSkillFrontmatter,
|
|
6042
|
+
PORTABLE_SKILL_MAX_FILE_BYTES,
|
|
5924
6043
|
PORTABLE_SKILL_MAX_FILES,
|
|
5925
6044
|
PORTABLE_SKILL_MAX_TOTAL_BYTES
|
|
5926
6045
|
} from "@opengeni/runtime/skill-library";
|
|
@@ -5935,7 +6054,16 @@ async function resolveSkillImport(rawUrl, client) {
|
|
|
5935
6054
|
throw new HTTPException6(422, { message: "GitHub returned an invalid source commit" });
|
|
5936
6055
|
}
|
|
5937
6056
|
const tree = await client.listTree(parsed.owner, parsed.repository, sourceCommit);
|
|
5938
|
-
const
|
|
6057
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
6058
|
+
const readBlob = (sha) => {
|
|
6059
|
+
let pending = blobs.get(sha);
|
|
6060
|
+
if (!pending) {
|
|
6061
|
+
pending = client.readBlob(parsed.owner, parsed.repository, sha);
|
|
6062
|
+
blobs.set(sha, pending);
|
|
6063
|
+
}
|
|
6064
|
+
return pending;
|
|
6065
|
+
};
|
|
6066
|
+
const sourcePath = await selectSkillRoot(parsed, tree, readBlob, sourceCommit);
|
|
5939
6067
|
const entries = skillFilesUnderRoot(tree, sourcePath);
|
|
5940
6068
|
const declaredBytes = entries.reduce((sum, entry) => sum + (entry.size ?? 0), 0);
|
|
5941
6069
|
if (entries.length > PORTABLE_SKILL_MAX_FILES) {
|
|
@@ -5949,11 +6077,10 @@ async function resolveSkillImport(rawUrl, client) {
|
|
|
5949
6077
|
});
|
|
5950
6078
|
}
|
|
5951
6079
|
const files = await mapConcurrent(entries, maxConcurrentBlobReads, async (entry) => {
|
|
6080
|
+
const bytes = await readBlob(entry.sha);
|
|
5952
6081
|
let content;
|
|
5953
6082
|
try {
|
|
5954
|
-
content = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
5955
|
-
await client.readBlob(parsed.owner, parsed.repository, entry.sha)
|
|
5956
|
-
);
|
|
6083
|
+
content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
5957
6084
|
} catch {
|
|
5958
6085
|
throw new HTTPException6(422, {
|
|
5959
6086
|
message: `Skill file is not valid UTF-8 text: ${relativeSkillPath(entry.path, sourcePath)}`
|
|
@@ -5963,7 +6090,7 @@ async function resolveSkillImport(rawUrl, client) {
|
|
|
5963
6090
|
});
|
|
5964
6091
|
let artifact;
|
|
5965
6092
|
try {
|
|
5966
|
-
artifact =
|
|
6093
|
+
artifact = buildPortableSkillArtifact3(files);
|
|
5967
6094
|
} catch (error) {
|
|
5968
6095
|
throw new HTTPException6(422, {
|
|
5969
6096
|
message: error instanceof Error ? error.message : "Skill artifact is invalid"
|
|
@@ -6029,7 +6156,12 @@ function parseSkillSource(rawUrl) {
|
|
|
6029
6156
|
message: "Skill imports require a credential-free HTTPS URL without a fragment"
|
|
6030
6157
|
});
|
|
6031
6158
|
}
|
|
6032
|
-
|
|
6159
|
+
let segments;
|
|
6160
|
+
try {
|
|
6161
|
+
segments = url.pathname.split("/").filter(Boolean).map(decodeURIComponent);
|
|
6162
|
+
} catch {
|
|
6163
|
+
throw new HTTPException6(422, { message: "The Skill URL contains invalid encoding" });
|
|
6164
|
+
}
|
|
6033
6165
|
if (url.hostname === "skills.sh" || url.hostname === "www.skills.sh") {
|
|
6034
6166
|
if (segments.length !== 3) {
|
|
6035
6167
|
throw new HTTPException6(422, {
|
|
@@ -6082,12 +6214,11 @@ function parseSkillSource(rawUrl) {
|
|
|
6082
6214
|
const ref = segments[3];
|
|
6083
6215
|
if (!ref) throw new HTTPException6(422, { message: "The GitHub URL is missing a revision" });
|
|
6084
6216
|
const pathSegments = segments.slice(4);
|
|
6085
|
-
if (pathSegments.length === 0) {
|
|
6217
|
+
if (pathSegments.length === 0 && mode === "blob") {
|
|
6086
6218
|
throw new HTTPException6(422, { message: "The GitHub URL is missing a Skill folder" });
|
|
6087
6219
|
}
|
|
6088
|
-
const
|
|
6089
|
-
|
|
6090
|
-
);
|
|
6220
|
+
const folderSegments = mode === "blob" && pathSegments.at(-1)?.toLowerCase() === "skill.md" ? pathSegments.slice(0, -1) : pathSegments;
|
|
6221
|
+
const requestedPath = folderSegments.length === 0 ? "." : normalizeGitHubPath(folderSegments);
|
|
6091
6222
|
return {
|
|
6092
6223
|
source: "github",
|
|
6093
6224
|
owner,
|
|
@@ -6098,10 +6229,10 @@ function parseSkillSource(rawUrl) {
|
|
|
6098
6229
|
inputUrl: url.toString()
|
|
6099
6230
|
};
|
|
6100
6231
|
}
|
|
6101
|
-
function selectSkillRoot(source, tree) {
|
|
6232
|
+
async function selectSkillRoot(source, tree, readBlob, sourceCommit) {
|
|
6102
6233
|
const skillFiles = tree.filter(
|
|
6103
6234
|
(entry) => entry.type === "blob" && entry.mode !== "120000" && (entry.path === "SKILL.md" || entry.path.endsWith("/SKILL.md"))
|
|
6104
|
-
).map((entry) => entry.path.slice(0, -"/SKILL.md".length)
|
|
6235
|
+
).map((entry) => entry.path === "SKILL.md" ? "." : entry.path.slice(0, -"/SKILL.md".length)).sort();
|
|
6105
6236
|
if (source.requestedPath) {
|
|
6106
6237
|
const root = source.requestedPath;
|
|
6107
6238
|
if (!skillFiles.includes(root)) {
|
|
@@ -6111,7 +6242,48 @@ function selectSkillRoot(source, tree) {
|
|
|
6111
6242
|
}
|
|
6112
6243
|
return root;
|
|
6113
6244
|
}
|
|
6114
|
-
|
|
6245
|
+
let candidates = skillFiles;
|
|
6246
|
+
if (source.skillSlug) {
|
|
6247
|
+
if (skillFiles.length > PORTABLE_SKILL_MAX_FILES) {
|
|
6248
|
+
throw new HTTPException6(422, {
|
|
6249
|
+
message: "Too many Skill candidates; paste the exact GitHub folder URL"
|
|
6250
|
+
});
|
|
6251
|
+
}
|
|
6252
|
+
let metadataBytes = 0;
|
|
6253
|
+
const entriesByPath = new Map(tree.map((entry) => [entry.path, entry]));
|
|
6254
|
+
const matches = await mapConcurrent(skillFiles, maxConcurrentBlobReads, async (root) => {
|
|
6255
|
+
const entry = entriesByPath.get(root === "." ? "SKILL.md" : `${root}/SKILL.md`);
|
|
6256
|
+
if ((entry.size ?? 0) > PORTABLE_SKILL_MAX_FILE_BYTES) {
|
|
6257
|
+
throw new HTTPException6(422, {
|
|
6258
|
+
message: "Skill metadata is too large; paste the exact GitHub folder URL"
|
|
6259
|
+
});
|
|
6260
|
+
}
|
|
6261
|
+
const bytes = await readBlob(entry.sha);
|
|
6262
|
+
metadataBytes += bytes.byteLength;
|
|
6263
|
+
if (bytes.byteLength > PORTABLE_SKILL_MAX_FILE_BYTES || metadataBytes > PORTABLE_SKILL_MAX_TOTAL_BYTES) {
|
|
6264
|
+
throw new HTTPException6(422, {
|
|
6265
|
+
message: "Skill metadata is too large; paste the exact GitHub folder URL"
|
|
6266
|
+
});
|
|
6267
|
+
}
|
|
6268
|
+
let markdown;
|
|
6269
|
+
try {
|
|
6270
|
+
markdown = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
6271
|
+
} catch {
|
|
6272
|
+
throw new HTTPException6(422, {
|
|
6273
|
+
message: "Skill metadata is not valid UTF-8; paste the exact GitHub folder URL"
|
|
6274
|
+
});
|
|
6275
|
+
}
|
|
6276
|
+
return parsePortableSkillFrontmatter(markdown).name?.toLowerCase() === source.skillSlug.toLowerCase();
|
|
6277
|
+
});
|
|
6278
|
+
candidates = skillFiles.filter((_, index) => matches[index]);
|
|
6279
|
+
if (candidates.length === 0) {
|
|
6280
|
+
const folders = skillFiles.filter((path) => path.split("/").at(-1) === source.skillSlug);
|
|
6281
|
+
const exactPath = folders.length === 1 ? encodeGitHubPath(folders[0]) : "<exact-skill-folder-path>";
|
|
6282
|
+
throw new HTTPException6(422, {
|
|
6283
|
+
message: `No Skill frontmatter name matches skills.sh slug "${source.skillSlug}"; the link may be stale. Check the current Skill name, or explicitly select the intended Skill using its exact GitHub folder URL: https://github.com/${source.owner}/${source.repository}/tree/${sourceCommit}/${exactPath}`
|
|
6284
|
+
});
|
|
6285
|
+
}
|
|
6286
|
+
}
|
|
6115
6287
|
if (candidates.length === 0) {
|
|
6116
6288
|
throw new HTTPException6(422, { message: "No Skill folder with SKILL.md was found" });
|
|
6117
6289
|
}
|
|
@@ -6164,13 +6336,20 @@ function assertGitHubRepository(owner, repository) {
|
|
|
6164
6336
|
async function mapConcurrent(values, concurrency, map) {
|
|
6165
6337
|
const output = new Array(values.length);
|
|
6166
6338
|
let next = 0;
|
|
6339
|
+
let failed = false;
|
|
6167
6340
|
await Promise.all(
|
|
6168
6341
|
Array.from({ length: Math.min(concurrency, values.length) }, async () => {
|
|
6169
6342
|
for (; ; ) {
|
|
6343
|
+
if (failed) return;
|
|
6170
6344
|
const index = next;
|
|
6171
6345
|
next += 1;
|
|
6172
6346
|
if (index >= values.length) return;
|
|
6173
|
-
|
|
6347
|
+
try {
|
|
6348
|
+
output[index] = await map(values[index]);
|
|
6349
|
+
} catch (error) {
|
|
6350
|
+
failed = true;
|
|
6351
|
+
throw error;
|
|
6352
|
+
}
|
|
6174
6353
|
}
|
|
6175
6354
|
})
|
|
6176
6355
|
);
|
|
@@ -6180,6 +6359,260 @@ function sha256Hex2(bytes) {
|
|
|
6180
6359
|
return createHash4("sha256").update(bytes).digest("hex");
|
|
6181
6360
|
}
|
|
6182
6361
|
|
|
6362
|
+
// src/domain/skill-search.ts
|
|
6363
|
+
import { pinnedFetch, readResponseJsonBounded } from "@opengeni/network";
|
|
6364
|
+
var PublicSkillSearchError = class extends Error {
|
|
6365
|
+
constructor(code, message, retryAfterSeconds = null) {
|
|
6366
|
+
super(message);
|
|
6367
|
+
this.code = code;
|
|
6368
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
6369
|
+
this.name = "PublicSkillSearchError";
|
|
6370
|
+
}
|
|
6371
|
+
};
|
|
6372
|
+
var endpoint = "https://skills.sh/api/search";
|
|
6373
|
+
var timeoutMs = 1e4;
|
|
6374
|
+
var maxResponseBytes = 256 * 1024;
|
|
6375
|
+
var maxResults = 20;
|
|
6376
|
+
var ownerPattern = /^[a-z0-9](?:[a-z0-9-]{0,38})$/iu;
|
|
6377
|
+
var segmentPattern = /^[a-z0-9](?:[a-z0-9._-]{0,98}[a-z0-9])?$/iu;
|
|
6378
|
+
function createPublicSkillSearchClient(settings, fetcher = pinnedFetch) {
|
|
6379
|
+
return {
|
|
6380
|
+
async search(input) {
|
|
6381
|
+
const query = input.query.trim();
|
|
6382
|
+
const limit = input.limit ?? maxResults;
|
|
6383
|
+
const owner = input.owner?.trim().toLowerCase();
|
|
6384
|
+
if (query.length < 2 || query.length > 200 || /[\u0000-\u001f\u007f]/u.test(query) || !Number.isSafeInteger(limit) || limit < 1 || limit > maxResults || owner !== void 0 && !ownerPattern.test(owner)) {
|
|
6385
|
+
throw new PublicSkillSearchError("invalid_query", "Invalid public Skill search parameters");
|
|
6386
|
+
}
|
|
6387
|
+
const url = new URL(endpoint);
|
|
6388
|
+
url.searchParams.set("q", query);
|
|
6389
|
+
url.searchParams.set("limit", String(limit));
|
|
6390
|
+
if (owner) url.searchParams.set("owner", owner);
|
|
6391
|
+
const controller = new AbortController();
|
|
6392
|
+
let timer;
|
|
6393
|
+
const deadline = new Promise((_, reject) => {
|
|
6394
|
+
timer = setTimeout(() => {
|
|
6395
|
+
controller.abort();
|
|
6396
|
+
reject(new PublicSkillSearchError("timeout", "Public Skill search timed out"));
|
|
6397
|
+
}, timeoutMs);
|
|
6398
|
+
});
|
|
6399
|
+
try {
|
|
6400
|
+
return await Promise.race([
|
|
6401
|
+
deadline,
|
|
6402
|
+
(async () => {
|
|
6403
|
+
const response = await fetcher(
|
|
6404
|
+
url,
|
|
6405
|
+
{
|
|
6406
|
+
method: "GET",
|
|
6407
|
+
headers: { accept: "application/json" },
|
|
6408
|
+
credentials: "omit",
|
|
6409
|
+
redirect: "manual",
|
|
6410
|
+
signal: controller.signal
|
|
6411
|
+
},
|
|
6412
|
+
settings,
|
|
6413
|
+
{ label: "Public Skill search", requireHttpsOutsideLocalTest: true }
|
|
6414
|
+
);
|
|
6415
|
+
if (controller.signal.aborted || !response.ok) {
|
|
6416
|
+
void response.body?.cancel().catch(() => void 0);
|
|
6417
|
+
if (controller.signal.aborted) {
|
|
6418
|
+
throw new PublicSkillSearchError("timeout", "Public Skill search timed out");
|
|
6419
|
+
}
|
|
6420
|
+
if (response.status === 429) {
|
|
6421
|
+
throw new PublicSkillSearchError(
|
|
6422
|
+
"rate_limited",
|
|
6423
|
+
"Public Skill search is rate limited",
|
|
6424
|
+
parseRetryAfter(response.headers.get("retry-after"))
|
|
6425
|
+
);
|
|
6426
|
+
}
|
|
6427
|
+
throw new PublicSkillSearchError("unavailable", "Public Skill search is unavailable");
|
|
6428
|
+
}
|
|
6429
|
+
let payload;
|
|
6430
|
+
try {
|
|
6431
|
+
payload = await readResponseJsonBounded(
|
|
6432
|
+
response,
|
|
6433
|
+
maxResponseBytes,
|
|
6434
|
+
"Public Skill search",
|
|
6435
|
+
{
|
|
6436
|
+
signal: controller.signal
|
|
6437
|
+
}
|
|
6438
|
+
);
|
|
6439
|
+
} catch {
|
|
6440
|
+
throw new PublicSkillSearchError(
|
|
6441
|
+
controller.signal.aborted ? "timeout" : "invalid_response",
|
|
6442
|
+
controller.signal.aborted ? "Public Skill search timed out" : "Public Skill search returned an invalid response"
|
|
6443
|
+
);
|
|
6444
|
+
}
|
|
6445
|
+
return normalizeResponse(payload, query, limit, owner);
|
|
6446
|
+
})()
|
|
6447
|
+
]);
|
|
6448
|
+
} catch (error) {
|
|
6449
|
+
if (error instanceof PublicSkillSearchError) throw error;
|
|
6450
|
+
throw new PublicSkillSearchError(
|
|
6451
|
+
controller.signal.aborted ? "timeout" : "unavailable",
|
|
6452
|
+
controller.signal.aborted ? "Public Skill search timed out" : "Public Skill search is unavailable"
|
|
6453
|
+
);
|
|
6454
|
+
} finally {
|
|
6455
|
+
clearTimeout(timer);
|
|
6456
|
+
}
|
|
6457
|
+
}
|
|
6458
|
+
};
|
|
6459
|
+
}
|
|
6460
|
+
function normalizeResponse(payload, query, limit, owner) {
|
|
6461
|
+
const invalid = () => new PublicSkillSearchError(
|
|
6462
|
+
"invalid_response",
|
|
6463
|
+
"Public Skill search returned an invalid response"
|
|
6464
|
+
);
|
|
6465
|
+
if (!isRecord2(payload) || !Array.isArray(payload.skills) || payload.skills.length > maxResults) {
|
|
6466
|
+
throw invalid();
|
|
6467
|
+
}
|
|
6468
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6469
|
+
const items = [];
|
|
6470
|
+
for (const skill of payload.skills) {
|
|
6471
|
+
if (!isRecord2(skill) || typeof skill.id !== "string" || typeof skill.source !== "string")
|
|
6472
|
+
throw invalid();
|
|
6473
|
+
const parts = skill.id.split("/");
|
|
6474
|
+
if (parts.length !== 3 || !ownerPattern.test(parts[0]) || !parts.every((part) => segmentPattern.test(part)) || skill.source !== parts.slice(0, 2).join("/") || skill.skillId !== void 0 && skill.skillId !== parts[2] || typeof skill.name !== "string" || !skill.name.trim() || skill.name.length > 200 || /[\u0000-\u001f\u007f]/u.test(skill.name) || typeof skill.installs !== "number" || !Number.isSafeInteger(skill.installs) || skill.installs < 0 || owner !== void 0 && parts[0].toLowerCase() !== owner)
|
|
6475
|
+
throw invalid();
|
|
6476
|
+
if (seen.has(skill.id)) continue;
|
|
6477
|
+
seen.add(skill.id);
|
|
6478
|
+
items.push({
|
|
6479
|
+
id: skill.id,
|
|
6480
|
+
name: skill.name.trim(),
|
|
6481
|
+
source: skill.source,
|
|
6482
|
+
skillId: parts[2],
|
|
6483
|
+
url: `https://skills.sh/${parts.map(encodeURIComponent).join("/")}`,
|
|
6484
|
+
installs: skill.installs
|
|
6485
|
+
});
|
|
6486
|
+
}
|
|
6487
|
+
return { provider: "skills_sh", query, items: items.slice(0, limit), nextCursor: null };
|
|
6488
|
+
}
|
|
6489
|
+
function isRecord2(value) {
|
|
6490
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6491
|
+
}
|
|
6492
|
+
function parseRetryAfter(value) {
|
|
6493
|
+
if (value === null) return null;
|
|
6494
|
+
if (/^\d+$/u.test(value)) {
|
|
6495
|
+
const seconds = Number(value);
|
|
6496
|
+
return Number.isSafeInteger(seconds) ? seconds : null;
|
|
6497
|
+
}
|
|
6498
|
+
const date = Date.parse(value);
|
|
6499
|
+
return Number.isFinite(date) ? Math.max(0, Math.ceil((date - Date.now()) / 1e3)) : null;
|
|
6500
|
+
}
|
|
6501
|
+
|
|
6502
|
+
// src/domain/github-skill-source.ts
|
|
6503
|
+
import { pinnedFetch as pinnedFetch2, readResponseJsonBounded as readResponseJsonBounded2, readResponseTextBounded } from "@opengeni/network";
|
|
6504
|
+
var githubApiBase = "https://api.github.com";
|
|
6505
|
+
var githubRequestTimeoutMs = 15e3;
|
|
6506
|
+
var githubMetadataMaxBytes = 4 * 1024 * 1024;
|
|
6507
|
+
var githubBlobResponseMaxBytes = 512 * 1024;
|
|
6508
|
+
function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes, label) => githubJson(settings, path, maxBytes, label)) {
|
|
6509
|
+
return {
|
|
6510
|
+
resolveCommit: async (owner, repository, ref) => {
|
|
6511
|
+
const payload = recordValue(
|
|
6512
|
+
await requestJson(
|
|
6513
|
+
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/commits/${encodeURIComponent(ref)}`,
|
|
6514
|
+
githubMetadataMaxBytes,
|
|
6515
|
+
"GitHub Skill commit"
|
|
6516
|
+
),
|
|
6517
|
+
"GitHub commit"
|
|
6518
|
+
);
|
|
6519
|
+
const sha = stringValue(payload.sha);
|
|
6520
|
+
if (!sha) throw new Error("GitHub commit response omitted sha");
|
|
6521
|
+
return sha.toLowerCase();
|
|
6522
|
+
},
|
|
6523
|
+
listTree: async (owner, repository, commit) => {
|
|
6524
|
+
const payload = recordValue(
|
|
6525
|
+
await requestJson(
|
|
6526
|
+
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/git/trees/${encodeURIComponent(commit)}?recursive=1`,
|
|
6527
|
+
githubMetadataMaxBytes,
|
|
6528
|
+
"GitHub Skill tree"
|
|
6529
|
+
),
|
|
6530
|
+
"GitHub tree"
|
|
6531
|
+
);
|
|
6532
|
+
if (payload.truncated === true) {
|
|
6533
|
+
throw new Error("GitHub repository tree is too large to import safely");
|
|
6534
|
+
}
|
|
6535
|
+
if (!Array.isArray(payload.tree)) throw new Error("GitHub tree response omitted entries");
|
|
6536
|
+
return payload.tree.map((entry, index) => {
|
|
6537
|
+
const record3 = recordValue(entry, `GitHub tree entry ${index}`);
|
|
6538
|
+
const path = stringValue(record3.path);
|
|
6539
|
+
const type = stringValue(record3.type);
|
|
6540
|
+
const mode = stringValue(record3.mode);
|
|
6541
|
+
const sha = stringValue(record3.sha);
|
|
6542
|
+
const size = record3.size;
|
|
6543
|
+
if (!path || type !== "blob" && type !== "tree" && type !== "commit" || !mode || !sha || size !== void 0 && size !== null && (typeof size !== "number" || !Number.isSafeInteger(size) || size < 0)) {
|
|
6544
|
+
throw new Error(`GitHub tree entry ${index} is invalid`);
|
|
6545
|
+
}
|
|
6546
|
+
return { path, type, mode, sha, size: typeof size === "number" ? size : null };
|
|
6547
|
+
});
|
|
6548
|
+
},
|
|
6549
|
+
readBlob: async (owner, repository, sha) => {
|
|
6550
|
+
const payload = recordValue(
|
|
6551
|
+
await requestJson(
|
|
6552
|
+
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/git/blobs/${encodeURIComponent(sha)}`,
|
|
6553
|
+
githubBlobResponseMaxBytes,
|
|
6554
|
+
"GitHub Skill file"
|
|
6555
|
+
),
|
|
6556
|
+
"GitHub blob"
|
|
6557
|
+
);
|
|
6558
|
+
if (payload.encoding !== "base64" || typeof payload.content !== "string") {
|
|
6559
|
+
throw new Error("GitHub Skill file did not use base64 encoding");
|
|
6560
|
+
}
|
|
6561
|
+
const normalized = payload.content.replace(/\s+/gu, "");
|
|
6562
|
+
if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(normalized)) {
|
|
6563
|
+
throw new Error("GitHub Skill file contained invalid base64");
|
|
6564
|
+
}
|
|
6565
|
+
const bytes = Uint8Array.from(Buffer.from(normalized, "base64"));
|
|
6566
|
+
if (typeof payload.size === "number" && Number.isSafeInteger(payload.size) && payload.size !== bytes.byteLength) {
|
|
6567
|
+
throw new Error("GitHub Skill file size did not match its payload");
|
|
6568
|
+
}
|
|
6569
|
+
return bytes;
|
|
6570
|
+
}
|
|
6571
|
+
};
|
|
6572
|
+
}
|
|
6573
|
+
async function githubJson(settings, path, maxBytes, label) {
|
|
6574
|
+
const controller = new AbortController();
|
|
6575
|
+
const timeout = setTimeout(() => controller.abort(), githubRequestTimeoutMs);
|
|
6576
|
+
try {
|
|
6577
|
+
const response = await pinnedFetch2(
|
|
6578
|
+
`${githubApiBase}${path}`,
|
|
6579
|
+
{
|
|
6580
|
+
method: "GET",
|
|
6581
|
+
headers: {
|
|
6582
|
+
accept: "application/vnd.github+json",
|
|
6583
|
+
"user-agent": "OpenGeni-Capabilities",
|
|
6584
|
+
"x-github-api-version": "2022-11-28"
|
|
6585
|
+
},
|
|
6586
|
+
signal: controller.signal
|
|
6587
|
+
},
|
|
6588
|
+
settings,
|
|
6589
|
+
{ label, requireHttpsOutsideLocalTest: true }
|
|
6590
|
+
);
|
|
6591
|
+
if (!response.ok) {
|
|
6592
|
+
await readResponseTextBounded(response, 8192, `${label} error`).catch(() => void 0);
|
|
6593
|
+
if (response.status === 404) throw new Error(`${label} was not found or is not public`);
|
|
6594
|
+
if (response.status === 403 || response.status === 429) {
|
|
6595
|
+
throw new Error(`${label} is temporarily unavailable because GitHub limited the request`);
|
|
6596
|
+
}
|
|
6597
|
+
throw new Error(`${label} failed with HTTP ${response.status}`);
|
|
6598
|
+
}
|
|
6599
|
+
return await readResponseJsonBounded2(response, maxBytes, label, {
|
|
6600
|
+
signal: controller.signal
|
|
6601
|
+
});
|
|
6602
|
+
} finally {
|
|
6603
|
+
clearTimeout(timeout);
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
function recordValue(value, label) {
|
|
6607
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6608
|
+
throw new Error(`${label} response is invalid`);
|
|
6609
|
+
}
|
|
6610
|
+
return value;
|
|
6611
|
+
}
|
|
6612
|
+
function stringValue(value) {
|
|
6613
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
6614
|
+
}
|
|
6615
|
+
|
|
6183
6616
|
// src/domain/environments.ts
|
|
6184
6617
|
import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2 } from "@opengeni/config";
|
|
6185
6618
|
import {
|
|
@@ -8253,6 +8686,7 @@ import {
|
|
|
8253
8686
|
DEFAULT_FIRST_PARTY_MCP_PERMISSIONS as DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2,
|
|
8254
8687
|
OPENGENI_SLACK_BOT_SESSION_METADATA_KEY as OPENGENI_SLACK_BOT_SESSION_METADATA_KEY3,
|
|
8255
8688
|
resolveWorkspaceSessionToolDefaults as resolveWorkspaceSessionToolDefaults3,
|
|
8689
|
+
resolveBundledSkillSelection as resolveBundledSkillSelection2,
|
|
8256
8690
|
SessionAgentAccess,
|
|
8257
8691
|
SessionEndUser,
|
|
8258
8692
|
SessionMemoryScope
|
|
@@ -8320,6 +8754,7 @@ import {
|
|
|
8320
8754
|
FIRST_PARTY_MCP_TOOL_NAMES as FIRST_PARTY_MCP_TOOL_NAMES2,
|
|
8321
8755
|
OPENGENI_SLACK_BOT_SESSION_METADATA_KEY as OPENGENI_SLACK_BOT_SESSION_METADATA_KEY2,
|
|
8322
8756
|
SessionSkills,
|
|
8757
|
+
resolveBundledSkillSelection,
|
|
8323
8758
|
SessionSpawnDenial,
|
|
8324
8759
|
ServiceTurnInitiator,
|
|
8325
8760
|
ServiceTurnInitiatorContext,
|
|
@@ -8790,6 +9225,7 @@ async function forkManagedHumanSession(deps, authorization, workspaceId, sourceS
|
|
|
8790
9225
|
const forkInput = {
|
|
8791
9226
|
sourceWorkspaceId: workspaceId,
|
|
8792
9227
|
sourceSessionId,
|
|
9228
|
+
...request.sourceEventId ? { sourceEventId: request.sourceEventId } : {},
|
|
8793
9229
|
actorSubjectId: authorization.grant.subjectId,
|
|
8794
9230
|
destinationWorkspaceId: workspaceId,
|
|
8795
9231
|
destinationVisibility: request.visibility === "private" ? "user_private" : "workspace_shared",
|
|
@@ -9363,6 +9799,7 @@ async function createAndStartSessionWithOutcome(input) {
|
|
|
9363
9799
|
initialModelContext: input.modelContext ?? null,
|
|
9364
9800
|
resources: input.resources,
|
|
9365
9801
|
skills: input.skills ?? [],
|
|
9802
|
+
bundledSkillIds: input.bundledSkillIds,
|
|
9366
9803
|
tools: input.tools,
|
|
9367
9804
|
toolPolicy: input.toolPolicy,
|
|
9368
9805
|
metadata: sessionMetadata,
|
|
@@ -9447,6 +9884,7 @@ async function createAndStartSessionWithOutcome(input) {
|
|
|
9447
9884
|
initialModelContext: input.modelContext ?? null,
|
|
9448
9885
|
resources: input.resources,
|
|
9449
9886
|
skills: input.skills ?? [],
|
|
9887
|
+
bundledSkillIds: input.bundledSkillIds,
|
|
9450
9888
|
tools: input.tools,
|
|
9451
9889
|
toolPolicy: input.toolPolicy,
|
|
9452
9890
|
metadata: sessionMetadata,
|
|
@@ -10037,6 +10475,17 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
|
|
|
10037
10475
|
message: error instanceof Error ? error.message : "invalid child visibility"
|
|
10038
10476
|
});
|
|
10039
10477
|
}
|
|
10478
|
+
let bundledSkillIds;
|
|
10479
|
+
try {
|
|
10480
|
+
bundledSkillIds = resolveBundledSkillSelection(
|
|
10481
|
+
payload.bundledSkillIds,
|
|
10482
|
+
parentSession?.bundledSkillIds
|
|
10483
|
+
);
|
|
10484
|
+
} catch (error) {
|
|
10485
|
+
throw new HTTPException12(422, {
|
|
10486
|
+
message: error instanceof Error ? error.message : "Invalid bundled Skill selection"
|
|
10487
|
+
});
|
|
10488
|
+
}
|
|
10040
10489
|
const sessionScope = resolveSessionCreateScope({
|
|
10041
10490
|
requested: {
|
|
10042
10491
|
agentAccess: payload.agentAccess,
|
|
@@ -10069,6 +10518,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
|
|
|
10069
10518
|
if (payload.idempotencyKey && (effectiveVisibility !== "user_private" || replayManagedHumanSubjectId !== null)) {
|
|
10070
10519
|
try {
|
|
10071
10520
|
const initializedReplay = await getInitializedSessionCreateReplay(db, {
|
|
10521
|
+
bundledSkillIds,
|
|
10072
10522
|
accountId: grant.accountId,
|
|
10073
10523
|
workspaceId,
|
|
10074
10524
|
subjectId: replayManagedHumanSubjectId ?? grant.subjectId,
|
|
@@ -10618,6 +11068,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
|
|
|
10618
11068
|
modelContext: payload.modelContext ?? null,
|
|
10619
11069
|
resources,
|
|
10620
11070
|
skills,
|
|
11071
|
+
bundledSkillIds,
|
|
10621
11072
|
tools,
|
|
10622
11073
|
toolPolicy,
|
|
10623
11074
|
...payload.clientEventId ? { clientEventId: payload.clientEventId } : {},
|
|
@@ -11660,6 +12111,11 @@ async function validateScheduledTaskTarget(input) {
|
|
|
11660
12111
|
if (!session || session.accountId !== input.grant.accountId) {
|
|
11661
12112
|
throw new HTTPException13(404, { message: "target session not found" });
|
|
11662
12113
|
}
|
|
12114
|
+
if (input.agentConfig.bundledSkillIds !== void 0 && !isDeepStrictEqual(input.agentConfig.bundledSkillIds, session.bundledSkillIds)) {
|
|
12115
|
+
throw new HTTPException13(422, {
|
|
12116
|
+
message: "An existing-session schedule cannot change that session's bundled Skill selection"
|
|
12117
|
+
});
|
|
12118
|
+
}
|
|
11663
12119
|
if (session.status === "cancelled") {
|
|
11664
12120
|
throw new HTTPException13(409, {
|
|
11665
12121
|
message: "target session is cancelled; choose a revivable session"
|
|
@@ -12200,6 +12656,24 @@ function manualScheduledTaskTriggerUsageKey(workspaceId, taskId, triggerToken) {
|
|
|
12200
12656
|
return `agent_run.created:scheduled-trigger:${workspaceId}:${taskId}:${triggerToken}`;
|
|
12201
12657
|
}
|
|
12202
12658
|
async function validateScheduledTaskAgentConfig(input) {
|
|
12659
|
+
const actor = creationInitiatorForGrant(input.grant).actor;
|
|
12660
|
+
const parent = actor ? await getSession3(input.db, input.workspaceId, actor.sessionId) : null;
|
|
12661
|
+
if (actor && (!parent || parent.accountId !== input.grant.accountId)) {
|
|
12662
|
+
throw new HTTPException13(403, {
|
|
12663
|
+
message: "Scheduled Skill selection requires the creating agent's session"
|
|
12664
|
+
});
|
|
12665
|
+
}
|
|
12666
|
+
let bundledSkillIds;
|
|
12667
|
+
try {
|
|
12668
|
+
bundledSkillIds = resolveBundledSkillSelection2(
|
|
12669
|
+
input.payload.agentConfig.bundledSkillIds,
|
|
12670
|
+
parent?.bundledSkillIds
|
|
12671
|
+
);
|
|
12672
|
+
} catch (error) {
|
|
12673
|
+
throw new HTTPException13(422, {
|
|
12674
|
+
message: error instanceof Error ? error.message : "Invalid bundled Skill selection"
|
|
12675
|
+
});
|
|
12676
|
+
}
|
|
12203
12677
|
const model = canonicalConfiguredModel(input.settings, input.payload.agentConfig.model);
|
|
12204
12678
|
await assertWorkspaceModelPolicyAllows(input.db, input.settings, input.workspaceId, model);
|
|
12205
12679
|
const resources = normalizeResources(input.payload.agentConfig.resources ?? []);
|
|
@@ -12259,6 +12733,7 @@ async function validateScheduledTaskAgentConfig(input) {
|
|
|
12259
12733
|
}
|
|
12260
12734
|
const validated = {
|
|
12261
12735
|
...input.payload.agentConfig,
|
|
12736
|
+
...bundledSkillIds !== void 0 ? { bundledSkillIds } : {},
|
|
12262
12737
|
...model === void 0 || model === null ? {} : { model },
|
|
12263
12738
|
prompt,
|
|
12264
12739
|
resources,
|
|
@@ -13953,6 +14428,12 @@ function createRememberRouter(options) {
|
|
|
13953
14428
|
async remember(input) {
|
|
13954
14429
|
const attempt = await attemptOf(input.attempt);
|
|
13955
14430
|
const request = RememberRequest.parse(input.request);
|
|
14431
|
+
if (request.lane === "preference") {
|
|
14432
|
+
throw new RememberError(
|
|
14433
|
+
"preference_retired",
|
|
14434
|
+
"The Remember preference lane is retired; use skill_save for shared Skill files. No note or proposal was created."
|
|
14435
|
+
);
|
|
14436
|
+
}
|
|
13956
14437
|
const note = await createNote(options.db, {
|
|
13957
14438
|
...attempt,
|
|
13958
14439
|
operationId: derivedRememberOperationId(request.operationId, "note"),
|
|
@@ -16619,6 +17100,7 @@ export {
|
|
|
16619
17100
|
PR_REVIEW_AUTOMATION_ADAPTER_ID,
|
|
16620
17101
|
PR_REVIEW_AUTOMATION_TEMPLATE_ID,
|
|
16621
17102
|
ProductionEditableArtifactSnapshotVerifier,
|
|
17103
|
+
PublicSkillSearchError,
|
|
16622
17104
|
RIG_DEFAULT_VARIABLE_SET_LOAD_CONCURRENCY,
|
|
16623
17105
|
RememberError,
|
|
16624
17106
|
SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
|
|
@@ -16656,6 +17138,7 @@ export {
|
|
|
16656
17138
|
appendRigSetupCommand,
|
|
16657
17139
|
applyCapabilityEnablement,
|
|
16658
17140
|
applyGitHubRepositoryBindings,
|
|
17141
|
+
approveSkill,
|
|
16659
17142
|
assertAllowedEnvironmentVariableName,
|
|
16660
17143
|
assertAllowedVariableSetVariableName,
|
|
16661
17144
|
assertBoundedArtifactTitle,
|
|
@@ -16720,7 +17203,9 @@ export {
|
|
|
16720
17203
|
createCompanyBrainLearningPolicyRouter,
|
|
16721
17204
|
createCompanyProfileAgentAdminRouter,
|
|
16722
17205
|
createCompanyProfileDurableLearningAdapter,
|
|
17206
|
+
createGitHubSkillSourceClient,
|
|
16723
17207
|
createGovernedLearningActivationController,
|
|
17208
|
+
createPublicSkillSearchClient,
|
|
16724
17209
|
createRememberRouter,
|
|
16725
17210
|
createRigForApi,
|
|
16726
17211
|
createRigVersionForApi,
|
|
@@ -16813,6 +17298,7 @@ export {
|
|
|
16813
17298
|
inlinePackSkillInstall,
|
|
16814
17299
|
insightsSessionLabel,
|
|
16815
17300
|
inspectEditableArtifactLiveWireEnvelope,
|
|
17301
|
+
installSkill,
|
|
16816
17302
|
isAcceptedMimeType,
|
|
16817
17303
|
isAuthoritativeGitHubRepositorySelectionError,
|
|
16818
17304
|
isBuiltInCapabilityPack,
|
|
@@ -16833,6 +17319,7 @@ export {
|
|
|
16833
17319
|
listManagedHumanUserResourceAuthorities,
|
|
16834
17320
|
listRigChangesForApi,
|
|
16835
17321
|
listRigVersionsForApi,
|
|
17322
|
+
listSkills,
|
|
16836
17323
|
listWorkspaceCapabilityPacks,
|
|
16837
17324
|
loadRigDefaultVariableSetEnvironment,
|
|
16838
17325
|
lockActiveCustomModelForAdmission,
|
|
@@ -16899,6 +17386,7 @@ export {
|
|
|
16899
17386
|
provisionSandbox,
|
|
16900
17387
|
publishGovernedLearningEventToSlack,
|
|
16901
17388
|
readSessionLineage,
|
|
17389
|
+
readSkill,
|
|
16902
17390
|
recordRigAuditEvent,
|
|
16903
17391
|
recordVariableSetAuditEvent,
|
|
16904
17392
|
recordWorkspaceUsage,
|
|
@@ -16907,6 +17395,7 @@ export {
|
|
|
16907
17395
|
relayDialBaseFromSettings,
|
|
16908
17396
|
releaseManagedAuthRequestActorLease,
|
|
16909
17397
|
rememberConfirmationLabel,
|
|
17398
|
+
replayPortableSkillInstall,
|
|
16910
17399
|
reportSessionUsageRecordingFailure,
|
|
16911
17400
|
requireAccessContext,
|
|
16912
17401
|
requireAccessGrant,
|
|
@@ -16952,6 +17441,7 @@ export {
|
|
|
16952
17441
|
resolveWorkspaceLegacyRuntimePacks,
|
|
16953
17442
|
resolveWorkspaceModelSelection,
|
|
16954
17443
|
restoreScheduledTask,
|
|
17444
|
+
restoreSkill,
|
|
16955
17445
|
retainedProcessBackgroundSettlement,
|
|
16956
17446
|
revokeManagedHumanUserResourceGrant,
|
|
16957
17447
|
rigActorForGrant,
|
|
@@ -16967,6 +17457,7 @@ export {
|
|
|
16967
17457
|
sanitizeSlackPublicationText,
|
|
16968
17458
|
saveActorNewSessionDraft,
|
|
16969
17459
|
saveHumanComposerDraft,
|
|
17460
|
+
saveSkill,
|
|
16970
17461
|
saveWorkspaceMemoryWithSlackPublication,
|
|
16971
17462
|
scheduledConnectionSurfaceEligibility,
|
|
16972
17463
|
scheduledSlackBotConnectionId,
|
|
@@ -16991,6 +17482,7 @@ export {
|
|
|
16991
17482
|
settingsWithRigProviderImage,
|
|
16992
17483
|
settingsWithSessionMcpServerMetadata,
|
|
16993
17484
|
signedJsonAutomationAdapter,
|
|
17485
|
+
skillBundleHash,
|
|
16994
17486
|
stableJson3 as stableJson,
|
|
16995
17487
|
statusForVoiceInputError,
|
|
16996
17488
|
steerAgentSession,
|
|
@@ -17020,6 +17512,7 @@ export {
|
|
|
17020
17512
|
validateOpenGeniSlackBotConnectionSelection,
|
|
17021
17513
|
validateScheduledTaskMachineTarget,
|
|
17022
17514
|
validateScheduledTaskTarget,
|
|
17515
|
+
validateSkillFiles,
|
|
17023
17516
|
validateToolRefs,
|
|
17024
17517
|
validateToolRefsForSessionPolicy,
|
|
17025
17518
|
validateVariableSetAttachment,
|