@opengeni/worker-bundle 0.27.0-canary.0 → 0.27.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.
Files changed (60) hide show
  1. package/dist/activities/agent-turn/agent-build.d.ts +3 -4
  2. package/dist/activities/agent-turn/governance-model.d.ts +2 -3
  3. package/dist/activities/agent-turn/skill-checkout.d.ts +21 -0
  4. package/dist/activities/agent-turn/skill-install.d.ts +12 -0
  5. package/dist/activities/agent-turn/skill-read.d.ts +15 -0
  6. package/dist/activities/agent-turn/skill-save.d.ts +26 -0
  7. package/dist/activities/agent-turn/skill-search.d.ts +16 -0
  8. package/dist/activities/agent-turn/skill-selection.d.ts +17 -0
  9. package/dist/activities/agent-turn/skill-tools.d.ts +20 -0
  10. package/dist/activities/agent-turn/skill-transfer.d.ts +26 -0
  11. package/dist/activities/agent-turn/tool-environment.d.ts +7 -0
  12. package/dist/activities/parent-wake.d.ts +12 -2
  13. package/dist/activities/run-input.d.ts +11 -5
  14. package/dist/activities/sandbox-lease.d.ts +5 -4
  15. package/dist/activities/types.d.ts +4 -2
  16. package/dist/activities/workflow-wake.d.ts +2 -4
  17. package/dist/{activities-control-VMA3XMHE.js → activities-control-WZRFEIJH.js} +103 -26
  18. package/dist/activities-control-WZRFEIJH.js.map +1 -0
  19. package/dist/{activities-turn-57AOUNYN.js → activities-turn-V7J34ZOF.js} +945 -532
  20. package/dist/activities-turn-V7J34ZOF.js.map +1 -0
  21. package/dist/{chunk-D4ZEFOYH.js → chunk-BARTLVBK.js} +1 -1
  22. package/dist/chunk-BARTLVBK.js.map +1 -0
  23. package/dist/{chunk-7LS42PHP.js → chunk-C54VDT6I.js} +590 -34
  24. package/dist/chunk-C54VDT6I.js.map +1 -0
  25. package/dist/index.js +6 -4
  26. package/dist/index.js.map +1 -1
  27. package/dist/model-context-contributions.d.ts +2 -0
  28. package/dist/observability-metrics.d.ts +1 -1
  29. package/dist/workflow-bundle.js +1 -1
  30. package/package.json +17 -17
  31. package/src/activities/agent-turn/agent-build.ts +8 -34
  32. package/src/activities/agent-turn/errors.ts +4 -0
  33. package/src/activities/agent-turn/governance-model.ts +15 -19
  34. package/src/activities/agent-turn/run.ts +4 -6
  35. package/src/activities/agent-turn/sandbox-provision.ts +9 -0
  36. package/src/activities/agent-turn/skill-checkout.ts +124 -0
  37. package/src/activities/agent-turn/skill-install.ts +57 -0
  38. package/src/activities/agent-turn/skill-read.ts +117 -0
  39. package/src/activities/agent-turn/skill-save.ts +124 -0
  40. package/src/activities/agent-turn/skill-search.ts +110 -0
  41. package/src/activities/agent-turn/skill-selection.ts +53 -0
  42. package/src/activities/agent-turn/skill-tools.ts +268 -0
  43. package/src/activities/agent-turn/skill-transfer.ts +137 -0
  44. package/src/activities/agent-turn/tool-environment.ts +93 -1
  45. package/src/activities/automations.ts +3 -1
  46. package/src/activities/context-compaction.ts +26 -2
  47. package/src/activities/parent-wake.ts +66 -5
  48. package/src/activities/run-input.ts +88 -12
  49. package/src/activities/sandbox-lease.ts +77 -20
  50. package/src/activities/scheduled-tasks.ts +2 -0
  51. package/src/activities/types.ts +4 -2
  52. package/src/activities/workflow-wake.ts +42 -5
  53. package/src/index.ts +3 -1
  54. package/src/model-context-contributions.ts +18 -2
  55. package/src/observability-metrics.ts +2 -0
  56. package/src/sandbox-routing.ts +42 -1
  57. package/dist/activities-control-VMA3XMHE.js.map +0 -1
  58. package/dist/activities-turn-57AOUNYN.js.map +0 -1
  59. package/dist/chunk-7LS42PHP.js.map +0 -1
  60. package/dist/chunk-D4ZEFOYH.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/worker-bundle",
3
- "version": "0.27.0-canary.0",
3
+ "version": "0.27.2-canary.0",
4
4
  "description": "OpenGeni worker entry and reusable embedded lifecycle, shipped with a release-coherent pre-bundled Temporal workflow artifact.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -54,22 +54,22 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@llamaindex/liteparse": "2.14.2",
57
- "@opengeni/agent-proto": "^0.5.1-canary.18",
58
- "@opengeni/capabilities": "^0.3.3-canary.0",
59
- "@opengeni/codemode": "^0.5.0-canary.0",
60
- "@opengeni/codex": "^0.2.22-canary.0",
61
- "@opengeni/config": "^1.0.1-canary.0",
62
- "@opengeni/contracts": "^2.14.0-canary.0",
63
- "@opengeni/core": "^2.8.0-canary.0",
64
- "@opengeni/db": "^4.1.0-canary.0",
65
- "@opengeni/documents": "^0.8.20-canary.0",
66
- "@opengeni/events": "^0.4.18-canary.0",
67
- "@opengeni/github": "^0.7.4-canary.0",
68
- "@opengeni/network": "^0.3.1-canary.0",
69
- "@opengeni/observability": "^0.8.20-canary.0",
70
- "@opengeni/runtime": "^2.4.0-canary.0",
71
- "@opengeni/storage": "^0.2.121-canary.0",
72
- "@opengeni/xai-subscription": "^0.1.4-canary.0",
57
+ "@opengeni/agent-proto": "^0.5.1-canary.20",
58
+ "@opengeni/capabilities": "^0.3.3-canary.2",
59
+ "@opengeni/codemode": "^0.5.2-canary.0",
60
+ "@opengeni/codex": "^0.2.22-canary.2",
61
+ "@opengeni/config": "^1.0.3-canary.0",
62
+ "@opengeni/contracts": "^2.15.1-canary.0",
63
+ "@opengeni/core": "^2.8.2-canary.0",
64
+ "@opengeni/db": "^4.2.1-canary.0",
65
+ "@opengeni/documents": "^0.8.22-canary.0",
66
+ "@opengeni/events": "^0.4.20-canary.0",
67
+ "@opengeni/github": "^0.7.6-canary.0",
68
+ "@opengeni/network": "^0.3.1-canary.2",
69
+ "@opengeni/observability": "^0.8.22-canary.0",
70
+ "@opengeni/runtime": "^2.4.2-canary.0",
71
+ "@opengeni/storage": "^0.2.123-canary.0",
72
+ "@opengeni/xai-subscription": "^0.1.4-canary.2",
73
73
  "@temporalio/activity": "^1.17.0",
74
74
  "@temporalio/client": "^1.17.0",
75
75
  "@temporalio/worker": "^1.17.0",
@@ -8,6 +8,7 @@ import {
8
8
  loadWorkspaceVercelAiGatewayCredentialLease,
9
9
  } from "@opengeni/db";
10
10
  import {
11
+ formatSkillCatalog,
11
12
  type AttemptConnectorActionBinding,
12
13
  type BuildAgentOptions,
13
14
  type ConnectorActionPolicyHooks,
@@ -34,10 +35,7 @@ import { VideoReferenceInputError } from "../video-reference-staging";
34
35
  import { rigProviderImageSourceImage } from "../packs";
35
36
  import type { TurnActivityServices as ActivityServices, RunAgentTurnInput } from "../types";
36
37
  import { recordTurnStartupPhase } from "../../observability-metrics";
37
- import {
38
- modelVisibleCompanyBrainSkillActivations,
39
- summarizeCompanyBrainContributions,
40
- } from "../../model-context-contributions";
38
+ import { summarizeCompanyBrainContributions } from "../../model-context-contributions";
41
39
  import { createTurnCredentialLeases } from "./credential-leases";
42
40
  import { createTurnMediaArtifacts } from "./media-artifacts";
43
41
  import { executeGatewayImageGeneration } from "../gateway-image-generation";
@@ -75,6 +73,7 @@ import { resolveTurnSandboxAccess } from "./turn-sandbox-access";
75
73
  import { resolveVideoReferenceSandboxAccess } from "./video-reference-sandbox";
76
74
 
77
75
  export type BuildTurnAgentDeps = {
76
+ skillCatalog: NonNullable<BuildAgentOptions["skillCatalog"]>;
78
77
  input: RunAgentTurnInput;
79
78
  db: ActivityServices["db"];
80
79
  runtime: ActivityServices["runtime"];
@@ -109,8 +108,6 @@ export type BuildTurnAgentDeps = {
109
108
  workspaceMemory: GovernanceModelOk["workspaceMemory"];
110
109
  rigVersion: GovernanceModelOk["rigVersion"];
111
110
  rigName: GovernanceModelOk["rigName"];
112
- packRuntime: GovernanceModelOk["packRuntime"];
113
- installedSkillRuntime: GovernanceModelOk["installedSkillRuntime"];
114
111
  buildCompanyBrainContributionReceiptFor: GovernanceModelOk["buildCompanyBrainContributionReceiptFor"];
115
112
  promptCacheKey: CompactionPrepOk["promptCacheKey"];
116
113
  workspaceVariableSet: Awaited<ReturnType<typeof loadWorkspaceEnvironmentForRunWithCredentials>>;
@@ -170,8 +167,6 @@ export async function buildTurnAgent(deps: BuildTurnAgentDeps) {
170
167
  workspaceMemory,
171
168
  rigVersion,
172
169
  rigName,
173
- packRuntime,
174
- installedSkillRuntime,
175
170
  buildCompanyBrainContributionReceiptFor,
176
171
  promptCacheKey,
177
172
  workspaceVariableSet,
@@ -559,30 +554,10 @@ export async function buildTurnAgent(deps: BuildTurnAgentDeps) {
559
554
  turnExecutionPolicy.latencyMode,
560
555
  );
561
556
  const approvedToolCallId = approvedConnectorActionCallId(trigger);
562
- const runtimeSkillActivations = [
563
- ...installedSkillRuntime.activations,
564
- ...packRuntime.skillActivations,
565
- ...session.skills.map((skill) => ({
566
- source: "session" as const,
567
- id: `session:${session.id}:${skill.name}`,
568
- artifact: {
569
- name: skill.name,
570
- description: skill.description ?? null,
571
- files: skill.files.map((file) => ({
572
- path: file.path,
573
- content: file.content,
574
- })),
575
- },
576
- reason: "attached to session",
577
- })),
578
- ];
579
- const modelVisibleRuntimeSkillActivations = modelVisibleCompanyBrainSkillActivations(
580
- eventing.modelRunSettings.sandboxBackend,
581
- runtimeSkillActivations,
582
- );
557
+ const modelVisibleSkillCatalogText = formatSkillCatalog(deps.skillCatalog);
583
558
  try {
584
559
  eventing.companyBrainContextContributions = summarizeCompanyBrainContributions(
585
- buildCompanyBrainContributionReceiptFor(modelVisibleRuntimeSkillActivations),
560
+ buildCompanyBrainContributionReceiptFor(modelVisibleSkillCatalogText),
586
561
  );
587
562
  } catch {
588
563
  // Contribution telemetry must never change model execution semantics.
@@ -701,9 +676,8 @@ export async function buildTurnAgent(deps: BuildTurnAgentDeps) {
701
676
  promptCacheKey: input.sessionId,
702
677
  }),
703
678
  onRetainableSessionImageOutput: media.retainSessionImageAtToolBoundary,
704
- ...(runtimeSkillActivations.length > 0
705
- ? { skillActivations: runtimeSkillActivations }
706
- : {}),
679
+ skillCatalog: deps.skillCatalog,
680
+ serverSkillReading: true,
707
681
  ...(!structuredWorkspacePolicyActive && workspaceAgentInstructions
708
682
  ? { instructionsTemplate: workspaceAgentInstructions }
709
683
  : {}),
@@ -772,7 +746,7 @@ export async function buildTurnAgent(deps: BuildTurnAgentDeps) {
772
746
  }
773
747
  return {
774
748
  agent,
775
- modelVisibleRuntimeSkillActivations,
749
+ modelVisibleSkillCatalogText,
776
750
  postAgentPreparationStartedAt,
777
751
  };
778
752
  }
@@ -24,6 +24,7 @@ import {
24
24
  } from "@opengeni/runtime/mcp-network";
25
25
  import { ApplicationFailure, CancelledFailure } from "@temporalio/activity";
26
26
  import { CODEX_USAGE_EXHAUSTED_PCT } from "../codex-rotation";
27
+ import { RetainedAttachmentTransportLimitError } from "../run-input";
27
28
  import type { CodexAccountStatus } from "@opengeni/db";
28
29
  import {
29
30
  CodexReloginRequired,
@@ -870,6 +871,9 @@ export function agentRunFailurePayload(
870
871
  historyPersistenceStage?: MandatoryHistoryPersistenceStage;
871
872
  mcpTransportDiagnostic?: McpTransportRequestFailureDiagnostic;
872
873
  } {
874
+ if (error instanceof RetainedAttachmentTransportLimitError) {
875
+ return { error: error.message, code: "retained_attachment_transport_limit", retryable: false };
876
+ }
873
877
  if (error instanceof MandatoryHistoryPersistenceError) {
874
878
  const underlying = isSessionEventPersistenceError(error.cause)
875
879
  ? agentRunFailurePayload(error.cause, options)
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  materializeRigVersionForAttempt,
3
3
  getWorkspaceModelPolicy,
4
+ getFilesForSubject,
4
5
  getWorkspace,
5
6
  resolveCompanyBrainContextSelection,
6
7
  getGeneratedVideoArtifact,
@@ -22,11 +23,7 @@ import {
22
23
  import { settingsWithResolvedModelContext, type Settings } from "@opengeni/config";
23
24
  import { settingsWithSessionMcpServersForRun } from "../capabilities";
24
25
  import { resolveRigProviderImageForRun } from "@opengeni/core";
25
- import {
26
- resolveWorkspacePackRuntime,
27
- resolveWorkspaceInstalledSkillRuntime,
28
- settingsWithPackSandboxImage,
29
- } from "../packs";
26
+ import { resolveWorkspacePackRuntime, settingsWithPackSandboxImage } from "../packs";
30
27
  import { createModelHistoryAttachmentProjector } from "../run-input";
31
28
  import type {
32
29
  TurnActivityServices as ActivityServices,
@@ -88,7 +85,6 @@ export type GovernanceModelDeps = {
88
85
  export type GovernanceModelOk = {
89
86
  runtimePreparationStartedAt: number;
90
87
  packRuntime: Awaited<ReturnType<typeof resolveWorkspacePackRuntime>>;
91
- installedSkillRuntime: Awaited<ReturnType<typeof resolveWorkspaceInstalledSkillRuntime>>;
92
88
  rigVersion:
93
89
  | NonNullable<Awaited<ReturnType<typeof materializeRigVersionForAttempt>>>["version"]
94
90
  | null;
@@ -99,9 +95,7 @@ export type GovernanceModelOk = {
99
95
  structuredWorkspacePolicyActive: boolean;
100
96
  workspaceMemory: string | null | undefined;
101
97
  buildCompanyBrainContributionReceiptFor: (
102
- skillActivations: Parameters<
103
- typeof buildCompanyBrainContributionReceipt
104
- >[0]["skillActivations"],
98
+ skillCatalogText: string,
105
99
  ) => ReturnType<typeof buildCompanyBrainContributionReceipt>;
106
100
  logicalSandboxSettings: Settings;
107
101
  verifiedRigProviderImageId: string | undefined;
@@ -201,13 +195,11 @@ export async function prepareGovernanceAndModel(
201
195
  // snapshots below so its receipt stays exact.
202
196
  const [
203
197
  packRuntime,
204
- installedSkillRuntime,
205
198
  rigMaterialization,
206
199
  [workspace, companyProfileSnapshot, instructionPolicySnapshot, preferenceSnapshot],
207
200
  workspaceModelPolicy,
208
201
  ] = await Promise.all([
209
202
  resolveWorkspacePackRuntime(db, input.workspaceId),
210
- resolveWorkspaceInstalledSkillRuntime(db, input.workspaceId),
211
203
  session.rigId && session.rigVersionId
212
204
  ? (async () =>
213
205
  await materializeRigVersionForAttempt(db, {
@@ -254,16 +246,13 @@ export async function prepareGovernanceAndModel(
254
246
  },
255
247
  {
256
248
  includeCompanyProfile: companyProfileIncluded,
249
+ sharedSkillReader: true,
257
250
  },
258
251
  );
259
252
  const structuredWorkspacePolicyActive =
260
253
  hasActiveWorkspaceInstructionPolicy(instructionPolicySnapshot);
261
254
  const workspaceMemory = contextSelection.workspaceMemory;
262
- const buildCompanyBrainContributionReceiptFor = (
263
- skillActivations: Parameters<
264
- typeof buildCompanyBrainContributionReceipt
265
- >[0]["skillActivations"],
266
- ) =>
255
+ const buildCompanyBrainContributionReceiptFor = (skillCatalogText: string) =>
267
256
  buildCompanyBrainContributionReceipt({
268
257
  contextSelectionReceiptId: contextSelection.receipt.id,
269
258
  attemptId: input.attemptId,
@@ -276,7 +265,8 @@ export async function prepareGovernanceAndModel(
276
265
  companyProfile: companyProfileSnapshot,
277
266
  companyProfileIncluded,
278
267
  workspaceMemory,
279
- skillActivations,
268
+ skillActivations: [],
269
+ skillCatalogText,
280
270
  });
281
271
  try {
282
272
  // Portable operator compaction runs before tool/skill preparation, so its
@@ -284,7 +274,7 @@ export async function prepareGovernanceAndModel(
284
274
  // no runtime skill catalog. Later compaction paths replace this summary
285
275
  // after the complete skill activation set is resolved.
286
276
  eventing.companyBrainContextContributions = summarizeCompanyBrainContributions(
287
- buildCompanyBrainContributionReceiptFor([]),
277
+ buildCompanyBrainContributionReceiptFor(""),
288
278
  );
289
279
  } catch {
290
280
  // Contribution telemetry must never change model execution semantics.
@@ -367,6 +357,13 @@ export async function prepareGovernanceAndModel(
367
357
  return object.bytes;
368
358
  }
369
359
  : undefined,
360
+ (fileIds) =>
361
+ getFilesForSubject(db, {
362
+ accountId: input.accountId,
363
+ workspaceId: input.workspaceId,
364
+ subjectId: fileAuthoritySubjectId,
365
+ fileIds,
366
+ }),
370
367
  );
371
368
  const modelHistoryProjector = async (
372
369
  items: Array<Record<string, unknown>>,
@@ -464,7 +461,6 @@ export async function prepareGovernanceAndModel(
464
461
  ok: {
465
462
  runtimePreparationStartedAt,
466
463
  packRuntime,
467
- installedSkillRuntime,
468
464
  rigVersion,
469
465
  rigName,
470
466
  agentHumanInputEnabled,
@@ -479,7 +479,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
479
479
  const {
480
480
  runtimePreparationStartedAt,
481
481
  packRuntime,
482
- installedSkillRuntime,
483
482
  rigVersion,
484
483
  rigName,
485
484
  agentHumanInputEnabled,
@@ -1152,6 +1151,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1152
1151
  ).values(),
1153
1152
  ];
1154
1153
  const toolRuntime = await prepareTurnToolRuntime({
1154
+ selectedSkillActivations: packRuntime.skillActivations,
1155
1155
  input,
1156
1156
  catalogSourceSettings,
1157
1157
  db,
@@ -1199,6 +1199,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1199
1199
  } = toolRuntime;
1200
1200
 
1201
1201
  const builtAgent = await buildTurnAgent({
1202
+ skillCatalog: toolRuntime.skillCatalog,
1202
1203
  input,
1203
1204
  db,
1204
1205
  runtime,
@@ -1233,8 +1234,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1233
1234
  workspaceMemory,
1234
1235
  rigVersion,
1235
1236
  rigName,
1236
- packRuntime,
1237
- installedSkillRuntime,
1238
1237
  buildCompanyBrainContributionReceiptFor,
1239
1238
  promptCacheKey,
1240
1239
  workspaceVariableSet,
@@ -1256,8 +1255,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1256
1255
  postToolPreparationStartedAt,
1257
1256
  codexContext,
1258
1257
  });
1259
- const { agent, modelVisibleRuntimeSkillActivations, postAgentPreparationStartedAt } =
1260
- builtAgent;
1258
+ const { agent, modelVisibleSkillCatalogText, postAgentPreparationStartedAt } = builtAgent;
1261
1259
 
1262
1260
  await bindLazySandboxProvisioner({
1263
1261
  ...sandboxRoute,
@@ -1313,7 +1311,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1313
1311
  companyBrainContributionReceiptRecorded = true;
1314
1312
  try {
1315
1313
  const companyBrainContributionReceipt = buildCompanyBrainContributionReceiptFor(
1316
- modelVisibleRuntimeSkillActivations,
1314
+ modelVisibleSkillCatalogText,
1317
1315
  );
1318
1316
  eventing.companyBrainContextContributions = summarizeCompanyBrainContributions(
1319
1317
  companyBrainContributionReceipt,
@@ -361,6 +361,15 @@ export function classifySandboxLogicalProvisionFailure(
361
361
 
362
362
  export function isLazySandboxProvisionRetryable(error: unknown): boolean {
363
363
  const chain = sandboxProvisionErrorChain(error);
364
+ // Rotation resumes through the durable lifecycle wake. Retrying here holds
365
+ // a turn worker and can repeat the entire lease wait for every skill root.
366
+ if (
367
+ chain.some(
368
+ (value) =>
369
+ value instanceof SandboxLeaseTransitionError && value.reason === "rotation_in_progress",
370
+ )
371
+ )
372
+ return false;
364
373
  if (
365
374
  chain.some(
366
375
  (value) =>
@@ -0,0 +1,124 @@
1
+ import type { AttemptToolDefinition } from "@opengeni/codemode";
2
+ import type { SandboxChannelAService } from "@opengeni/runtime/sandbox";
3
+ import type { SkillTextFile } from "@opengeni/runtime/skill-library";
4
+ import { checkoutSkillDirectory, readSkillDirectory } from "./skill-transfer";
5
+ import type { SkillSaveReceipt, SkillSaveRequest } from "./skill-save";
6
+
7
+ type SkillFileSystem = Pick<SandboxChannelAService, "fsList" | "fsRead" | "fsWrite" | "fsMkdir">;
8
+
9
+ export function createSkillCheckoutAttemptToolDefinition(input: {
10
+ authorize: () => Promise<void>;
11
+ load: (skill: string) => Promise<{
12
+ skillId: string;
13
+ revisionId: string | null;
14
+ scopeVersion: number | null;
15
+ files: readonly SkillTextFile[];
16
+ }>;
17
+ filesystem: () => Promise<SkillFileSystem>;
18
+ }): AttemptToolDefinition {
19
+ return {
20
+ identity: { serverId: "opengeni", toolName: "skill_checkout" },
21
+ modelName: "skill_checkout",
22
+ codemodePath: ["opengeni", "skill_checkout"],
23
+ title: "Check out Skill files",
24
+ description:
25
+ "Copy a Skill to a fresh sandbox-relative directory for execution or larger edits. Starts the sandbox if needed. Never overwrites an existing directory and does not publish changes. Retain the returned revision and scope version for skill_publish. Use skill_read for ordinary reading without a sandbox.",
26
+ inputSchema: {
27
+ type: "object",
28
+ properties: {
29
+ skill: { type: "string", minLength: 1, maxLength: 512 },
30
+ directory: { type: "string", minLength: 1, maxLength: 1024 },
31
+ },
32
+ required: ["skill", "directory"],
33
+ additionalProperties: false,
34
+ },
35
+ annotations: {
36
+ readOnlyHint: false,
37
+ destructiveHint: false,
38
+ idempotentHint: false,
39
+ openWorldHint: false,
40
+ },
41
+ source: "opengeni",
42
+ approval: "none",
43
+ execute: async (args) => {
44
+ await input.authorize();
45
+ const skill = await input.load(args.skill as string);
46
+ const copied = await checkoutSkillDirectory(
47
+ await input.filesystem(),
48
+ args.directory as string,
49
+ skill.files,
50
+ );
51
+ const output = {
52
+ ...copied,
53
+ skillId: skill.skillId,
54
+ revisionId: skill.revisionId,
55
+ scopeVersion: skill.scopeVersion,
56
+ };
57
+ return {
58
+ isError: false,
59
+ content: [{ type: "text", text: JSON.stringify(output) }],
60
+ structuredContent: output,
61
+ };
62
+ },
63
+ };
64
+ }
65
+
66
+ export function createSkillPublishAttemptToolDefinition(input: {
67
+ authorize: () => Promise<void>;
68
+ filesystem: () => Promise<SkillFileSystem>;
69
+ save: (request: SkillSaveRequest) => Promise<SkillSaveReceipt>;
70
+ }): AttemptToolDefinition {
71
+ return {
72
+ identity: { serverId: "opengeni", toolName: "skill_publish" },
73
+ modelName: "skill_publish",
74
+ codemodePath: ["opengeni", "skill_publish"],
75
+ title: "Publish Skill directory",
76
+ description:
77
+ "Save a complete sandbox Skill directory through the same Learning-controlled service as skill_save. Reads UTF-8 text files directly; do not serialize the directory into arguments. Files missing from the directory are removed from the new revision. Supply the checkout revision and scope version; stale edits are refused. The result reports whether the change is live or pending approval.",
78
+ inputSchema: {
79
+ type: "object",
80
+ properties: {
81
+ operationId: { type: "string", format: "uuid" },
82
+ skillId: { type: "string", format: "uuid" },
83
+ expectedRevisionId: { type: ["string", "null"], format: "uuid" },
84
+ expectedScopeVersion: { type: "integer", minimum: 0 },
85
+ directory: { type: "string", minLength: 1, maxLength: 1024 },
86
+ reason: { type: "string", minLength: 1, maxLength: 2000 },
87
+ },
88
+ required: [
89
+ "operationId",
90
+ "skillId",
91
+ "expectedRevisionId",
92
+ "expectedScopeVersion",
93
+ "directory",
94
+ "reason",
95
+ ],
96
+ additionalProperties: false,
97
+ },
98
+ annotations: {
99
+ readOnlyHint: false,
100
+ destructiveHint: false,
101
+ idempotentHint: true,
102
+ openWorldHint: false,
103
+ },
104
+ source: "opengeni",
105
+ approval: "none",
106
+ execute: async (args) => {
107
+ await input.authorize();
108
+ const artifact = await readSkillDirectory(await input.filesystem(), args.directory as string);
109
+ const output = await input.save({
110
+ operationId: args.operationId as string,
111
+ skillId: args.skillId as string,
112
+ expectedRevisionId: args.expectedRevisionId as string | null,
113
+ expectedScopeVersion: args.expectedScopeVersion as number,
114
+ files: artifact.files,
115
+ reason: args.reason as string,
116
+ });
117
+ return {
118
+ isError: false,
119
+ content: [{ type: "text", text: JSON.stringify(output) }],
120
+ structuredContent: output,
121
+ };
122
+ },
123
+ };
124
+ }
@@ -0,0 +1,57 @@
1
+ import type { AttemptToolDefinition } from "@opengeni/codemode";
2
+ import type { SkillSaveReceipt } from "./skill-save";
3
+
4
+ /** The installer resolves/pins bytes and persists them through the shared lifecycle. */
5
+ export function createSkillInstallAttemptToolDefinition(input: {
6
+ authorize: () => Promise<void>;
7
+ install: (request: {
8
+ operationId: string;
9
+ source: string;
10
+ expectedInstallationVersion?: number;
11
+ reason: string;
12
+ }) => Promise<SkillSaveReceipt>;
13
+ }): AttemptToolDefinition {
14
+ return {
15
+ identity: { serverId: "opengeni", toolName: "skill_install" },
16
+ modelName: "skill_install",
17
+ codemodePath: ["opengeni", "skill_install"],
18
+ title: "Install a Skill",
19
+ description:
20
+ "Install a Skill from a public skills.sh or GitHub URL, or library:<id> returned by skill_search. The server resolves and pins the source; no prior preview or invented hash is required. Learning mode controls activation. Replacing an existing installation requires its current installation version, and preserves workspace customizations.",
21
+ inputSchema: {
22
+ type: "object",
23
+ properties: {
24
+ operationId: { type: "string", format: "uuid" },
25
+ source: { type: "string", minLength: 1, maxLength: 2048 },
26
+ expectedInstallationVersion: { type: "integer", minimum: 0 },
27
+ reason: { type: "string", minLength: 1, maxLength: 2000 },
28
+ },
29
+ required: ["operationId", "source", "reason"],
30
+ additionalProperties: false,
31
+ },
32
+ annotations: {
33
+ readOnlyHint: false,
34
+ destructiveHint: false,
35
+ idempotentHint: true,
36
+ openWorldHint: true,
37
+ },
38
+ source: "opengeni",
39
+ approval: "none",
40
+ execute: async (args) => {
41
+ await input.authorize();
42
+ const output = await input.install({
43
+ operationId: args.operationId as string,
44
+ source: args.source as string,
45
+ ...(args.expectedInstallationVersion !== undefined
46
+ ? { expectedInstallationVersion: args.expectedInstallationVersion as number }
47
+ : {}),
48
+ reason: args.reason as string,
49
+ });
50
+ return {
51
+ isError: false,
52
+ content: [{ type: "text", text: JSON.stringify(output) }],
53
+ structuredContent: output,
54
+ };
55
+ },
56
+ };
57
+ }
@@ -0,0 +1,117 @@
1
+ import type { AttemptToolDefinition } from "@opengeni/codemode";
2
+ import {
3
+ assertSkillRelativePath,
4
+ PORTABLE_SKILL_MAX_FILES,
5
+ readSkillFiles,
6
+ SKILL_READ_MAX_OUTPUT_BYTES,
7
+ SKILL_READ_MAX_PATHS,
8
+ type SkillTextFile,
9
+ } from "@opengeni/runtime/skill-library";
10
+
11
+ export const SKILL_READ_TOOL_NAME = "skill_read";
12
+
13
+ export type SkillReadContent = Readonly<{
14
+ files: readonly SkillTextFile[];
15
+ skillId: string;
16
+ revisionId: string;
17
+ scopeVersion: number;
18
+ installationVersion?: number;
19
+ }>;
20
+
21
+ /** A first-party gateway definition, not a sandbox capability or second backend. */
22
+ export function createSkillReadAttemptToolDefinition(input: {
23
+ authorize: () => Promise<void>;
24
+ load: (skill: string) => Promise<readonly SkillTextFile[] | SkillReadContent>;
25
+ }): AttemptToolDefinition {
26
+ return {
27
+ identity: { serverId: "opengeni", toolName: SKILL_READ_TOOL_NAME },
28
+ modelName: SKILL_READ_TOOL_NAME,
29
+ codemodePath: ["opengeni", SKILL_READ_TOOL_NAME],
30
+ title: "Read Skill files",
31
+ description:
32
+ "Read Skill text without starting a sandbox. Omit paths to read SKILL.md; provide relative paths to read exactly those files, never implicitly adding SKILL.md. Set listFiles:true without paths to list relative paths and available revision identity only, with no file bodies. Use an id or name from the Skill index or skill_search. Management tools are lazy; when listed, opengeni-skills explains how to use them.",
33
+ inputSchema: {
34
+ type: "object",
35
+ properties: {
36
+ skill: { type: "string", minLength: 1, maxLength: 512 },
37
+ listFiles: { type: "boolean" },
38
+ paths: {
39
+ type: "array",
40
+ minItems: 1,
41
+ maxItems: SKILL_READ_MAX_PATHS,
42
+ uniqueItems: true,
43
+ items: { type: "string", minLength: 1, maxLength: 1024 },
44
+ },
45
+ },
46
+ required: ["skill"],
47
+ additionalProperties: false,
48
+ },
49
+ annotations: {
50
+ title: "Read Skill files",
51
+ readOnlyHint: true,
52
+ destructiveHint: false,
53
+ idempotentHint: true,
54
+ openWorldHint: false,
55
+ },
56
+ source: "opengeni",
57
+ approval: "none",
58
+ execute: async (args) => {
59
+ if (
60
+ typeof args.skill !== "string" ||
61
+ !args.skill ||
62
+ (args.listFiles !== undefined && typeof args.listFiles !== "boolean") ||
63
+ (args.paths !== undefined &&
64
+ (!Array.isArray(args.paths) || args.paths.some((path) => typeof path !== "string")))
65
+ ) {
66
+ throw new Error("skill_read requires a Skill identifier and optional relative paths.");
67
+ }
68
+ if (args.listFiles === true && args.paths !== undefined) {
69
+ throw new Error("skill_read listFiles:true cannot be combined with paths.");
70
+ }
71
+ await input.authorize();
72
+ const loaded = await input.load(args.skill);
73
+ const metadata = "files" in loaded ? loaded : null;
74
+ const files = metadata ? metadata.files : (loaded as readonly SkillTextFile[]);
75
+ const selected =
76
+ args.listFiles === true
77
+ ? listSkillPaths(files)
78
+ : readSkillFiles(files, args.paths as string[] | undefined);
79
+ const output = {
80
+ ...(metadata
81
+ ? {
82
+ skillId: metadata.skillId,
83
+ revisionId: metadata.revisionId,
84
+ scopeVersion: metadata.scopeVersion,
85
+ ...(metadata.installationVersion !== undefined
86
+ ? { installationVersion: metadata.installationVersion }
87
+ : {}),
88
+ }
89
+ : {}),
90
+ ...selected,
91
+ };
92
+ return {
93
+ isError: false,
94
+ content: [{ type: "text", text: JSON.stringify(output) }],
95
+ structuredContent: output,
96
+ };
97
+ },
98
+ };
99
+ }
100
+
101
+ /** Inventory never reads or serializes file bodies, including the entry point. */
102
+ function listSkillPaths(files: readonly SkillTextFile[]): { paths: string[] } {
103
+ if (files.length > PORTABLE_SKILL_MAX_FILES) {
104
+ throw new Error(`Skill inventory exceeds ${PORTABLE_SKILL_MAX_FILES} files.`);
105
+ }
106
+ const seen = new Set<string>();
107
+ for (const { path } of files) {
108
+ assertSkillRelativePath(path);
109
+ if (seen.has(path)) throw new Error(`Duplicate stored Skill path: ${path}`);
110
+ seen.add(path);
111
+ }
112
+ const result = { paths: [...seen].sort() };
113
+ if (Buffer.byteLength(JSON.stringify(result), "utf8") > SKILL_READ_MAX_OUTPUT_BYTES) {
114
+ throw new Error("Skill inventory exceeds the read output limit.");
115
+ }
116
+ return result;
117
+ }