@opengeni/core 2.9.3 → 2.9.4-canary.1

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/index.js CHANGED
@@ -4110,7 +4110,7 @@ var productIntegrationSkillFiles = [
4110
4110
  },
4111
4111
  {
4112
4112
  "path": "references/product-shapes-and-ui.md",
4113
- "content": "# Product shapes and UI\n\n## Choose the smallest suitable surface\n\nOpenGeni supports several product shapes. Select from the product experience and host stack rather than assuming every integration needs a custom chat:\n\n| Need | Likely surface | Product owns |\n| --- | --- | --- |\n| The complete OpenGeni experience is acceptable | Link or deep-link to stock OpenGeni | Entry point and product navigation |\n| Custom UI in any framework, mobile app, CLI, or automation | OpenGeni SDK or public API behind product backend | All user-facing presentation |\n| React product wants canonical session state without packaged visuals | Headless React session hooks and projections | Components, layout, and styling |\n| React product wants packaged chat/session controls | Focused styled React subpaths | Shell, domain UI, and theming |\n| Product exposes files, changes, terminal, or desktop compute | Optional workbench surfaces | Product shell and selected tabs |\n\nStart with the narrowest surface that preserves the desired experience. Do not mount the full workbench for an ordinary analytics chat. Do not rebuild session streaming, replay, queueing, approval, or timeline projection when a compatible package already supplies the needed behavior.\n\n## Evaluate reuse before writing chat UI\n\nFor React hosts, inspect the installed OpenGeni React package before creating replacement components. Its subpaths are composable, and the styled surfaces use scoped compiled CSS plus runtime theme and density tokens. Compare:\n\n- packaged components with customer theme tokens;\n- headless hooks with customer-native components; and\n- a fully custom SDK-driven UI.\n\nChoose based on UX requirements and dependency compatibility, then record why. Styling differences alone are not a reason to skip reusable components if their structure fits. Conversely, do not force a packaged component when the product needs a materially different interaction model.\n\nFor Svelte, SvelteKit, Vue, native mobile, or another non-React frontend, use the product's native component system. Keep the privileged OpenGeni client on a compatible backend boundary. A SvelteKit server route may use the TypeScript SDK directly; a non-JavaScript backend may use the public HTTP contract or a small compatible adapter. The browser still speaks to authenticated product routes.\n\n## Browser/backend split\n\nThe product browser normally sends product-shaped requests to its own same-origin backend. The backend authenticates, resolves the allowed mapping, and calls OpenGeni. Never bundle an organization key into frontend code.\n\nFor live sessions, preserve event sequence, reconnect, replay, and duplicate suppression. The SDK's stream and proxy helpers are preferred where compatible. Treat unknown additive event types as forward-compatible data rather than crashing the UI.\n\nUploads may send bytes directly to a short-lived signed storage URL returned by the trusted flow. That URL is narrow transfer authority, not the OpenGeni API key. Verify storage CORS for every intended browser origin.\n\n## Decide what the user sees\n\nOpenGeni's durable event stream can support different product projections:\n\n- final answer only;\n- assistant messages plus progress and status;\n- selected tool-call summaries;\n- approvals and structured human-input cards; or\n- a detailed operational timeline.\n\nThe customer frontend chooses which event types and fields to render. Hiding an event from the chat view does not remove it from OpenGeni's durable history or from authorized audit readers. Do not promise data erasure or secrecy from presentation filtering.\n\nEven a final-answer-only UI should surface states the user must act on: failure, cancellation, credit or policy denial, approval requests, human-input requests, reconnect status, and a way to retry safely. Avoid presenting tool failures as ordinary assistant prose when product state can represent them more clearly.\n\n## Fit the host product\n\nFollow existing navigation, accessibility, responsive, loading, error, observability, localization, and design-system conventions. Keep OpenGeni IDs behind product-native identifiers. Make the smallest dependency addition that improves correctness.\n\nThe integration should feel native to the customer product while retaining OpenGeni's session semantics. Framework adaptation is expected; protocol reimplementation is not a goal.\n"
4113
+ "content": "# Opening host-owned workbench tabs\n\nUse `SandboxWorkspace.openTabRequest={{ tab, requestId }}` to open a built-in or\nhost-injected tab from the host's UI. Increment `requestId` for each intentional\nopen, including repeated clicks on the same item. Keep artifact selection and\ninternal-link recognition in the host; the workbench only selects an available\ntab and expands the dock. Preserve modified clicks and external navigation.\nWhen handing off to a full-page artifact route, retain an explicit originating\nsession return path rather than relying on browser history.\n\n# Product shapes and UI\n\n## Choose the smallest suitable surface\n\nOpenGeni supports several product shapes. Select from the product experience and host stack rather than assuming every integration needs a custom chat:\n\n| Need | Likely surface | Product owns |\n| --- | --- | --- |\n| The complete OpenGeni experience is acceptable | Link or deep-link to stock OpenGeni | Entry point and product navigation |\n| Custom UI in any framework, mobile app, CLI, or automation | OpenGeni SDK or public API behind product backend | All user-facing presentation |\n| React product wants canonical session state without packaged visuals | Headless React session hooks and projections | Components, layout, and styling |\n| React product wants packaged chat/session controls | Focused styled React subpaths | Shell, domain UI, and theming |\n| Product exposes files, changes, terminal, or desktop compute | Optional workbench surfaces | Product shell and selected tabs |\n\nStart with the narrowest surface that preserves the desired experience. Do not mount the full workbench for an ordinary analytics chat. Do not rebuild session streaming, replay, queueing, approval, or timeline projection when a compatible package already supplies the needed behavior.\n\n## Evaluate reuse before writing chat UI\n\nFor React hosts, inspect the installed OpenGeni React package before creating replacement components. Its subpaths are composable, and the styled surfaces use scoped compiled CSS plus runtime theme and density tokens. Compare:\n\n- packaged components with customer theme tokens;\n- headless hooks with customer-native components; and\n- a fully custom SDK-driven UI.\n\nChoose based on UX requirements and dependency compatibility, then record why. Styling differences alone are not a reason to skip reusable components if their structure fits. Conversely, do not force a packaged component when the product needs a materially different interaction model.\n\nFor Svelte, SvelteKit, Vue, native mobile, or another non-React frontend, use the product's native component system. Keep the privileged OpenGeni client on a compatible backend boundary. A SvelteKit server route may use the TypeScript SDK directly; a non-JavaScript backend may use the public HTTP contract or a small compatible adapter. The browser still speaks to authenticated product routes.\n\n## Browser/backend split\n\nThe product browser normally sends product-shaped requests to its own same-origin backend. The backend authenticates, resolves the allowed mapping, and calls OpenGeni. Never bundle an organization key into frontend code.\n\nFor live sessions, preserve event sequence, reconnect, replay, and duplicate suppression. The SDK's stream and proxy helpers are preferred where compatible. Treat unknown additive event types as forward-compatible data rather than crashing the UI.\n\nUploads may send bytes directly to a short-lived signed storage URL returned by the trusted flow. That URL is narrow transfer authority, not the OpenGeni API key. Verify storage CORS for every intended browser origin.\n\n## Decide what the user sees\n\nOpenGeni's durable event stream can support different product projections:\n\n- final answer only;\n- assistant messages plus progress and status;\n- selected tool-call summaries;\n- approvals and structured human-input cards; or\n- a detailed operational timeline.\n\nThe customer frontend chooses which event types and fields to render. Hiding an event from the chat view does not remove it from OpenGeni's durable history or from authorized audit readers. Do not promise data erasure or secrecy from presentation filtering.\n\nEven a final-answer-only UI should surface states the user must act on: failure, cancellation, credit or policy denial, approval requests, human-input requests, reconnect status, and a way to retry safely. Avoid presenting tool failures as ordinary assistant prose when product state can represent them more clearly.\n\n## Fit the host product\n\nFollow existing navigation, accessibility, responsive, loading, error, observability, localization, and design-system conventions. Keep OpenGeni IDs behind product-native identifiers. Make the smallest dependency addition that improves correctness.\n\nThe integration should feel native to the customer product while retaining OpenGeni's session semantics. Framework adaptation is expected; protocol reimplementation is not a goal.\n"
4114
4114
  },
4115
4115
  {
4116
4116
  "path": "references/runtime-profile-and-verification.md",
@@ -4747,7 +4747,7 @@ async function buildCapabilityCatalog(input) {
4747
4747
  ...configuredMcpCatalogItems(input.settings),
4748
4748
  ...providerIntegrationCatalogItems(socialConnections),
4749
4749
  fikenCatalogItem(workspaceConnections.filter(isFikenConnection)),
4750
- ...curatedLibrarySkills,
4750
+ ...curatedLibrarySkills.filter((item) => installedSkillById.has(item.id)),
4751
4751
  ...installedSkills.filter(
4752
4752
  (skill) => skill.source !== "library" && skill.owners.some((owner) => owner.kind === "direct")
4753
4753
  ).map(installedSkillCatalogItem)
@@ -5369,9 +5369,6 @@ async function discoverMcpRegistryCapabilities(input) {
5369
5369
  if (!item || seen.has(item.id)) {
5370
5370
  continue;
5371
5371
  }
5372
- if (query && !catalogSearchText(item).includes(query)) {
5373
- continue;
5374
- }
5375
5372
  seen.add(item.id);
5376
5373
  items.push(item);
5377
5374
  if (items.length >= limit) {
@@ -5379,7 +5376,7 @@ async function discoverMcpRegistryCapabilities(input) {
5379
5376
  }
5380
5377
  }
5381
5378
  cursor = typeof page.metadata?.nextCursor === "string" ? page.metadata.nextCursor : void 0;
5382
- if (!cursor) {
5379
+ if (items.length > 0 || !cursor) {
5383
5380
  break;
5384
5381
  }
5385
5382
  }
@@ -6102,18 +6099,6 @@ function validUrl(value) {
6102
6099
  return null;
6103
6100
  }
6104
6101
  }
6105
- function catalogSearchText(item) {
6106
- return [
6107
- item.name,
6108
- item.description,
6109
- item.category,
6110
- ...item.tags,
6111
- item.endpointUrl,
6112
- item.homepageUrl,
6113
- item.installUrl,
6114
- JSON.stringify(item.metadata)
6115
- ].filter(Boolean).join(" ").toLowerCase();
6116
- }
6117
6102
  function capabilityInstallationRuntimeReady(item, installation) {
6118
6103
  if (!installation || item.kind !== "mcp") {
6119
6104
  return !!installation;
@@ -6160,6 +6145,10 @@ var gitCommit = /^[0-9a-f]{40,64}$/u;
6160
6145
  var maxConcurrentBlobReads = 8;
6161
6146
  async function resolveSkillImport(rawUrl, client) {
6162
6147
  const parsed = parseSkillSource(rawUrl);
6148
+ if (parsed.source === "skills_sh" && client.downloadSnapshot) {
6149
+ const files2 = await client.downloadSnapshot(parsed.owner, parsed.repository, parsed.skillSlug);
6150
+ return buildResolvedImport(parsed, files2, parsed.skillSlug, null);
6151
+ }
6163
6152
  const sourceCommit = await client.resolveCommit(parsed.owner, parsed.repository, parsed.ref);
6164
6153
  if (!gitCommit.test(sourceCommit)) {
6165
6154
  throw new HTTPException11(422, { message: "GitHub returned an invalid source commit" });
@@ -6199,6 +6188,9 @@ async function resolveSkillImport(rawUrl, client) {
6199
6188
  }
6200
6189
  return { path: relativeSkillPath(entry.path, sourcePath), content };
6201
6190
  });
6191
+ return buildResolvedImport(parsed, files, sourcePath, sourceCommit);
6192
+ }
6193
+ function buildResolvedImport(parsed, files, sourcePath, commit) {
6202
6194
  let artifact;
6203
6195
  try {
6204
6196
  artifact = buildPortableSkillArtifact3(files);
@@ -6208,7 +6200,13 @@ async function resolveSkillImport(rawUrl, client) {
6208
6200
  });
6209
6201
  }
6210
6202
  const repositoryUrl = `https://github.com/${parsed.owner}/${parsed.repository}`;
6211
- const sourceUrl = sourcePath === "." ? `${repositoryUrl}/tree/${sourceCommit}` : `${repositoryUrl}/tree/${sourceCommit}/${encodeGitHubPath(sourcePath)}`;
6203
+ const sourceCommit = commit ?? artifact.contentSha256;
6204
+ if (!commit && artifact.name.toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "") !== parsed.skillSlug.toLowerCase()) {
6205
+ throw new HTTPException11(422, {
6206
+ message: "The downloaded skill does not match the requested name"
6207
+ });
6208
+ }
6209
+ const sourceUrl = !commit ? `https://skills.sh/${parsed.owner}/${parsed.repository}/${parsed.skillSlug}` : sourcePath === "." ? `${repositoryUrl}/tree/${sourceCommit}` : `${repositoryUrl}/tree/${sourceCommit}/${encodeGitHubPath(sourcePath)}`;
6212
6210
  const warnings = [];
6213
6211
  if (!artifact.files.some((file) => /(^|\/)licen[cs]e(?:\.|$)/iu.test(file.path))) {
6214
6212
  warnings.push("No license file was found inside the selected Skill folder.");
@@ -6221,6 +6219,7 @@ async function resolveSkillImport(rawUrl, client) {
6221
6219
  }
6222
6220
  return {
6223
6221
  preview: {
6222
+ markdown: artifact.files.find((file) => file.path === "SKILL.md")?.content,
6224
6223
  source: parsed.source,
6225
6224
  sourceUrl,
6226
6225
  repositoryUrl,
@@ -6582,6 +6581,7 @@ function normalizeResponse(payload, query, limit, owner) {
6582
6581
  if (!isRecord2(skill) || typeof skill.id !== "string" || typeof skill.source !== "string")
6583
6582
  throw invalid();
6584
6583
  const parts = skill.id.split("/");
6584
+ if (parts.length === 2 && skill.source === parts[0]) continue;
6585
6585
  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)
6586
6586
  throw invalid();
6587
6587
  if (seen.has(skill.id)) continue;
@@ -6611,13 +6611,42 @@ function parseRetryAfter(value) {
6611
6611
  }
6612
6612
 
6613
6613
  // src/domain/github-skill-source.ts
6614
+ import { HTTPException as HTTPException12 } from "hono/http-exception";
6614
6615
  import { pinnedFetch as pinnedFetch2, readResponseJsonBounded as readResponseJsonBounded2, readResponseTextBounded } from "@opengeni/network";
6616
+ import {
6617
+ PORTABLE_SKILL_MAX_FILES as PORTABLE_SKILL_MAX_FILES2,
6618
+ PORTABLE_SKILL_MAX_TOTAL_BYTES as PORTABLE_SKILL_MAX_TOTAL_BYTES2
6619
+ } from "@opengeni/runtime/skill-library";
6615
6620
  var githubApiBase = "https://api.github.com";
6616
6621
  var githubRequestTimeoutMs = 15e3;
6617
6622
  var githubMetadataMaxBytes = 4 * 1024 * 1024;
6618
6623
  var githubBlobResponseMaxBytes = 512 * 1024;
6619
6624
  function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes, label) => githubJson(settings, path, maxBytes, label)) {
6625
+ const readJson = requestJson;
6626
+ const cache = /* @__PURE__ */ new Map();
6627
+ const pending = /* @__PURE__ */ new Map();
6628
+ requestJson = async (path, maxBytes, label) => {
6629
+ const cached = cache.get(path);
6630
+ if (cached && cached.expires > Date.now()) return cached.payload;
6631
+ const existing = pending.get(path);
6632
+ if (existing) return existing;
6633
+ const task = readJson(path, maxBytes, label).then((payload) => {
6634
+ if (cache.size >= 128) cache.delete(cache.keys().next().value);
6635
+ cache.set(path, {
6636
+ payload,
6637
+ expires: Date.now() + (path.includes("/commits/") ? 6e4 : 36e5)
6638
+ });
6639
+ return payload;
6640
+ });
6641
+ pending.set(path, task);
6642
+ try {
6643
+ return await task;
6644
+ } finally {
6645
+ pending.delete(path);
6646
+ }
6647
+ };
6620
6648
  return {
6649
+ downloadSnapshot: (owner, repository, slug) => downloadSkillSnapshot(settings, owner, repository, slug),
6621
6650
  resolveCommit: async (owner, repository, ref) => {
6622
6651
  const payload = recordValue(
6623
6652
  await requestJson(
@@ -6681,6 +6710,39 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
6681
6710
  }
6682
6711
  };
6683
6712
  }
6713
+ async function downloadSkillSnapshot(settings, owner, repository, slug) {
6714
+ const signal = AbortSignal.timeout(githubRequestTimeoutMs);
6715
+ const response = await pinnedFetch2(
6716
+ `https://skills.sh/api/download/${[owner, repository, slug].map(encodeURIComponent).join("/")}`,
6717
+ { headers: { accept: "application/json" }, credentials: "omit", redirect: "manual", signal },
6718
+ settings,
6719
+ { label: "Skill download", requireHttpsOutsideLocalTest: true }
6720
+ );
6721
+ if (!response.ok) {
6722
+ await response.body?.cancel();
6723
+ throw new HTTPException12(response.status === 429 ? 429 : 422, {
6724
+ message: response.status === 429 ? "skills.sh is busy. Try again shortly." : response.status === 404 ? "This skill has no downloadable snapshot. Import its GitHub folder URL instead." : "Could not download this skill from skills.sh. Try again shortly."
6725
+ });
6726
+ }
6727
+ const payload = recordValue(
6728
+ await readResponseJsonBounded2(
6729
+ response,
6730
+ PORTABLE_SKILL_MAX_TOTAL_BYTES2 * 6 + 128e3,
6731
+ "Skill download",
6732
+ { signal }
6733
+ ),
6734
+ "Skill download"
6735
+ );
6736
+ if (!Array.isArray(payload.files) || payload.files.length === 0 || payload.files.length > PORTABLE_SKILL_MAX_FILES2) {
6737
+ throw new HTTPException12(422, { message: "The downloaded skill has an invalid file list" });
6738
+ }
6739
+ return payload.files.map((file) => {
6740
+ if (!file || typeof file !== "object" || typeof file.path !== "string" || typeof file.contents !== "string") {
6741
+ throw new HTTPException12(422, { message: "The downloaded skill contains an invalid file" });
6742
+ }
6743
+ return { path: file.path, content: file.contents };
6744
+ });
6745
+ }
6684
6746
  async function githubJson(settings, path, maxBytes, label) {
6685
6747
  const controller = new AbortController();
6686
6748
  const timeout = setTimeout(() => controller.abort(), githubRequestTimeoutMs);
@@ -6703,7 +6765,9 @@ async function githubJson(settings, path, maxBytes, label) {
6703
6765
  await readResponseTextBounded(response, 8192, `${label} error`).catch(() => void 0);
6704
6766
  if (response.status === 404) throw new Error(`${label} was not found or is not public`);
6705
6767
  if (response.status === 403 || response.status === 429) {
6706
- throw new Error(`${label} is temporarily unavailable because GitHub limited the request`);
6768
+ throw new HTTPException12(429, {
6769
+ message: "GitHub's public request limit has been reached. Skill search is still available; try previewing again after the limit resets."
6770
+ });
6707
6771
  }
6708
6772
  throw new Error(`${label} failed with HTTP ${response.status}`);
6709
6773
  }
@@ -6730,12 +6794,12 @@ import {
6730
6794
  variableSetVariableNameReservation
6731
6795
  } from "@opengeni/contracts";
6732
6796
  import { getVariableSet as getVariableSet2, recordAuditEvent } from "@opengeni/db";
6733
- import { HTTPException as HTTPException12 } from "hono/http-exception";
6797
+ import { HTTPException as HTTPException13 } from "hono/http-exception";
6734
6798
  var MAX_ENVIRONMENTS_PER_WORKSPACE = 25;
6735
6799
  var MAX_VARIABLES_PER_ENVIRONMENT = 100;
6736
6800
  function assertAllowedVariableSetVariableName(name) {
6737
6801
  if (variableSetVariableNameReservation(name)) {
6738
- throw new HTTPException12(422, {
6802
+ throw new HTTPException13(422, {
6739
6803
  message: `reserved variable set variable name / reserved environment variable name: ${name}`
6740
6804
  });
6741
6805
  }
@@ -6744,7 +6808,7 @@ var assertAllowedEnvironmentVariableName = assertAllowedVariableSetVariableName;
6744
6808
  function requireVariableSetEncryption(settings) {
6745
6809
  const key = environmentsEncryptionKeyBytes2(settings);
6746
6810
  if (!key) {
6747
- throw new HTTPException12(503, {
6811
+ throw new HTTPException13(503, {
6748
6812
  message: "variable sets require OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"
6749
6813
  });
6750
6814
  }
@@ -6762,7 +6826,7 @@ async function requireVariableSetForApi(db, grant, variableSetId) {
6762
6826
  variableSetId
6763
6827
  );
6764
6828
  if (!variableSet) {
6765
- throw new HTTPException12(404, { message: "variableSet not found" });
6829
+ throw new HTTPException13(404, { message: "variableSet not found" });
6766
6830
  }
6767
6831
  return variableSet;
6768
6832
  }
@@ -6778,7 +6842,7 @@ async function validateVariableSetAttachment(deps, grant, workspaceId, variableS
6778
6842
  variableSetId
6779
6843
  );
6780
6844
  if (!variableSet) {
6781
- throw new HTTPException12(422, { message: "unknown variableSetId" });
6845
+ throw new HTTPException13(422, { message: "unknown variableSetId" });
6782
6846
  }
6783
6847
  return variableSet;
6784
6848
  }
@@ -6818,7 +6882,7 @@ import {
6818
6882
  RigChangeTransitionError,
6819
6883
  updateScopedRig
6820
6884
  } from "@opengeni/db";
6821
- import { HTTPException as HTTPException13 } from "hono/http-exception";
6885
+ import { HTTPException as HTTPException14 } from "hono/http-exception";
6822
6886
  import { boundedParallelMap } from "@opengeni/runtime/mcp-network";
6823
6887
  var MAX_RIGS_PER_WORKSPACE = 50;
6824
6888
  var MAX_CHECKS_PER_RIG = 100;
@@ -6857,21 +6921,21 @@ function rigActorForGrant(grant) {
6857
6921
  async function requireRigForApi(db, grant, rigId) {
6858
6922
  const rig = await getRig2(db, grant, rigId);
6859
6923
  if (!rig) {
6860
- throw new HTTPException13(404, { message: "rig not found" });
6924
+ throw new HTTPException14(404, { message: "rig not found" });
6861
6925
  }
6862
6926
  return rig;
6863
6927
  }
6864
6928
  async function requireRigChangeForApi(db, workspaceId, rigId, changeId) {
6865
6929
  const change = await getRigChange(db, workspaceId, changeId);
6866
6930
  if (!change || change.rigId !== rigId) {
6867
- throw new HTTPException13(404, { message: "rig change not found" });
6931
+ throw new HTTPException14(404, { message: "rig change not found" });
6868
6932
  }
6869
6933
  return change;
6870
6934
  }
6871
6935
  function trimmedRigName(name) {
6872
6936
  const trimmed = name.trim();
6873
6937
  if (!trimmed) {
6874
- throw new HTTPException13(422, { message: "rig name is required" });
6938
+ throw new HTTPException14(422, { message: "rig name is required" });
6875
6939
  }
6876
6940
  return trimmed;
6877
6941
  }
@@ -6882,7 +6946,7 @@ function assertUniqueCheckNames(checks) {
6882
6946
  const seen = /* @__PURE__ */ new Set();
6883
6947
  for (const check of checks) {
6884
6948
  if (seen.has(check.name)) {
6885
- throw new HTTPException13(422, {
6949
+ throw new HTTPException14(422, {
6886
6950
  message: `duplicate rig check name: ${check.name}`
6887
6951
  });
6888
6952
  }
@@ -6902,12 +6966,12 @@ async function assertVariableSetsExist(db, access, rigScope, ids) {
6902
6966
  for (const id of unique) {
6903
6967
  const variableSet = await getVariableSet3(db, access, id);
6904
6968
  if (!variableSet) {
6905
- throw new HTTPException13(422, {
6969
+ throw new HTTPException14(422, {
6906
6970
  message: `unknown defaultVariableSetId: ${id}`
6907
6971
  });
6908
6972
  }
6909
6973
  if (!variableSetScopeAllowedForRig(rigScope, variableSet.scope)) {
6910
- throw new HTTPException13(422, {
6974
+ throw new HTTPException14(422, {
6911
6975
  message: `${rigScope}-scoped rigs cannot use ${variableSet.scope}-scoped variable sets`
6912
6976
  });
6913
6977
  }
@@ -6926,12 +6990,12 @@ async function createRigForApi(deps, grant, payload, options = {}) {
6926
6990
  payload.defaultVariableSetIds.length > 0 ? payload.defaultVariableSetIds : void 0
6927
6991
  );
6928
6992
  if (await countRigs(deps.db, grant, payload.scope) >= MAX_RIGS_PER_WORKSPACE) {
6929
- throw new HTTPException13(422, {
6993
+ throw new HTTPException14(422, {
6930
6994
  message: `a workspace supports at most ${MAX_RIGS_PER_WORKSPACE} rigs`
6931
6995
  });
6932
6996
  }
6933
6997
  if (await getRigByName(deps.db, grant, name, payload.scope)) {
6934
- throw new HTTPException13(409, {
6998
+ throw new HTTPException14(409, {
6935
6999
  message: `rig name is already in use: ${name}`
6936
7000
  });
6937
7001
  }
@@ -6967,7 +7031,7 @@ async function updateRigForApi(deps, grant, rig, payload, options = {}) {
6967
7031
  if (name !== void 0 && name !== rig.name) {
6968
7032
  const existing = await getRigByName(deps.db, grant, name, rig.scope);
6969
7033
  if (existing && existing.id !== rig.id) {
6970
- throw new HTTPException13(409, {
7034
+ throw new HTTPException14(409, {
6971
7035
  message: `rig name is already in use: ${name}`
6972
7036
  });
6973
7037
  }
@@ -6993,7 +7057,7 @@ async function deleteRigForApi(deps, grant, rig, options = {}) {
6993
7057
  let current = error;
6994
7058
  while (current instanceof Error) {
6995
7059
  if (current.message.includes("active sessions")) {
6996
- throw new HTTPException13(409, { message: current.message });
7060
+ throw new HTTPException14(409, { message: current.message });
6997
7061
  }
6998
7062
  current = current.cause;
6999
7063
  }
@@ -7008,7 +7072,7 @@ async function deleteRigForApi(deps, grant, rig, options = {}) {
7008
7072
  async function proposeRigChangeForApi(deps, grant, rig, request, options = {}) {
7009
7073
  const workspaceId = rig.workspaceId;
7010
7074
  if (!rig.activeVersion) {
7011
- throw new HTTPException13(422, {
7075
+ throw new HTTPException14(422, {
7012
7076
  message: "rig has no active version to base a change on"
7013
7077
  });
7014
7078
  }
@@ -7060,37 +7124,37 @@ async function promoteChangeWithActiveCas(deps, workspaceId, rigId, changeId, in
7060
7124
  return await createRigVersionForChangePromotion(deps.db, workspaceId, rigId, changeId, input);
7061
7125
  } catch (error) {
7062
7126
  if (error instanceof RigActiveVersionChangedError) {
7063
- throw new HTTPException13(409, {
7127
+ throw new HTTPException14(409, {
7064
7128
  message: `rig moved since this change was verified (base ${error.expectedVersionId}, now ${error.actualVersionId ?? "none"}); re-verify before promoting`
7065
7129
  });
7066
7130
  }
7067
7131
  if (error instanceof RigChangeTransitionError) {
7068
- throw new HTTPException13(409, { message: error.message });
7132
+ throw new HTTPException14(409, { message: error.message });
7069
7133
  }
7070
7134
  throw error;
7071
7135
  }
7072
7136
  }
7073
7137
  async function promoteSetupAppendChange(deps, grant, rig, change) {
7074
7138
  if (change.kind !== "setup_append") {
7075
- throw new HTTPException13(422, {
7139
+ throw new HTTPException14(422, {
7076
7140
  message: "only setup_append changes auto-promote through this path"
7077
7141
  });
7078
7142
  }
7079
7143
  if (change.status !== "proposed" && change.status !== "verifying") {
7080
- throw new HTTPException13(409, {
7144
+ throw new HTTPException14(409, {
7081
7145
  message: `rig change is ${change.status}; cannot promote`
7082
7146
  });
7083
7147
  }
7084
7148
  if (!change.baseVersionId) {
7085
- throw new HTTPException13(422, { message: "rig change has no base version" });
7149
+ throw new HTTPException14(422, { message: "rig change has no base version" });
7086
7150
  }
7087
7151
  const base = await getRigVersion2(deps.db, rig.workspaceId, rig.id, change.baseVersionId);
7088
7152
  if (!base) {
7089
- throw new HTTPException13(404, { message: "base rig version not found" });
7153
+ throw new HTTPException14(404, { message: "base rig version not found" });
7090
7154
  }
7091
7155
  const payload = change.payload;
7092
7156
  if (typeof payload.command !== "string" || !payload.command.trim()) {
7093
- throw new HTTPException13(422, {
7157
+ throw new HTTPException14(422, {
7094
7158
  message: "setup_append change is missing command"
7095
7159
  });
7096
7160
  }
@@ -7138,26 +7202,26 @@ async function promoteSetupAppendChange(deps, grant, rig, change) {
7138
7202
  }
7139
7203
  async function promoteVerifiedDefinitionEditChangeForApi(deps, grant, rig, change) {
7140
7204
  if (change.kind !== "definition_edit") {
7141
- throw new HTTPException13(422, {
7205
+ throw new HTTPException14(422, {
7142
7206
  message: "only definition_edit changes use explicit promote"
7143
7207
  });
7144
7208
  }
7145
7209
  if (change.status !== "proposed") {
7146
- throw new HTTPException13(409, {
7210
+ throw new HTTPException14(409, {
7147
7211
  message: `rig change is ${change.status}; cannot promote`
7148
7212
  });
7149
7213
  }
7150
7214
  if (change.verification?.passed !== true) {
7151
- throw new HTTPException13(422, {
7215
+ throw new HTTPException14(422, {
7152
7216
  message: "definition_edit change must pass verification before promote"
7153
7217
  });
7154
7218
  }
7155
7219
  if (!change.baseVersionId) {
7156
- throw new HTTPException13(422, { message: "rig change has no base version" });
7220
+ throw new HTTPException14(422, { message: "rig change has no base version" });
7157
7221
  }
7158
7222
  const base = await getRigVersion2(deps.db, rig.workspaceId, rig.id, change.baseVersionId);
7159
7223
  if (!base) {
7160
- throw new HTTPException13(404, { message: "base rig version not found" });
7224
+ throw new HTTPException14(404, { message: "base rig version not found" });
7161
7225
  }
7162
7226
  const payload = change.payload;
7163
7227
  const nextDefinition = {
@@ -7204,7 +7268,7 @@ async function promoteVerifiedDefinitionEditChangeForApi(deps, grant, rig, chang
7204
7268
  }
7205
7269
  async function createRigVersionForApi(deps, grant, rig, payload) {
7206
7270
  if (!rig.activeVersion) {
7207
- throw new HTTPException13(422, { message: "rig has no active version" });
7271
+ throw new HTTPException14(422, { message: "rig has no active version" });
7208
7272
  }
7209
7273
  assertUniqueCheckNames(payload.checks);
7210
7274
  await assertVariableSetsExist(
@@ -7441,13 +7505,13 @@ import {
7441
7505
  areGitHubRepositoriesAllowedForWorkspace,
7442
7506
  requireFileForSubject
7443
7507
  } from "@opengeni/db";
7444
- import { HTTPException as HTTPException14 } from "hono/http-exception";
7508
+ import { HTTPException as HTTPException15 } from "hono/http-exception";
7445
7509
  function validateToolRefs(tools, settings) {
7446
7510
  const mcpServerIds = new Set(settings.mcpServers.map((server) => server.id));
7447
7511
  const out = [];
7448
7512
  for (const tool of tools) {
7449
7513
  if (tool.kind !== "mcp") {
7450
- throw new HTTPException14(422, {
7514
+ throw new HTTPException15(422, {
7451
7515
  message: `unsupported tool kind: ${tool.kind}`
7452
7516
  });
7453
7517
  }
@@ -7456,7 +7520,7 @@ function validateToolRefs(tools, settings) {
7456
7520
  if (optional) {
7457
7521
  continue;
7458
7522
  }
7459
- throw new HTTPException14(422, { message: `unknown MCP server id: ${tool.id}` });
7523
+ throw new HTTPException15(422, { message: `unknown MCP server id: ${tool.id}` });
7460
7524
  }
7461
7525
  out.push({
7462
7526
  kind: "mcp",
@@ -7494,7 +7558,7 @@ function assertToolRefsSubset(requested, allowed, message = "requested tools exc
7494
7558
  const allowedIds = new Set(allowed.map((tool) => `${tool.kind}:${tool.id}`));
7495
7559
  const widened = requested.find((tool) => !allowedIds.has(`${tool.kind}:${tool.id}`));
7496
7560
  if (widened) {
7497
- throw new HTTPException14(403, { message: `${message}: ${widened.id}` });
7561
+ throw new HTTPException15(403, { message: `${message}: ${widened.id}` });
7498
7562
  }
7499
7563
  }
7500
7564
  function validateToolRefsForSessionPolicy(input) {
@@ -7523,7 +7587,7 @@ function normalizeResources(resources) {
7523
7587
  try {
7524
7588
  normalizedUri = normalizeRepositoryTransportUri(resource.uri);
7525
7589
  } catch (error) {
7526
- throw new HTTPException14(422, {
7590
+ throw new HTTPException15(422, {
7527
7591
  message: error instanceof Error ? error.message : "invalid repository URI"
7528
7592
  });
7529
7593
  }
@@ -7532,14 +7596,14 @@ function normalizeResources(resources) {
7532
7596
  );
7533
7597
  const credentialBindingId = gitCredentialBindingIdForRepository(resource, credentialProvider);
7534
7598
  if ((resource.credentialBindingId || resource.connectionId || resource.access) && !credentialProvider) {
7535
- throw new HTTPException14(422, {
7599
+ throw new HTTPException15(422, {
7536
7600
  message: "repository credential bindings and access intent require a Git provider"
7537
7601
  });
7538
7602
  }
7539
7603
  if (credentialProvider && credentialBindingId) {
7540
7604
  const boundProvider = credentialBindingProviders.get(credentialBindingId);
7541
7605
  if (boundProvider && boundProvider !== credentialProvider) {
7542
- throw new HTTPException14(422, {
7606
+ throw new HTTPException15(422, {
7543
7607
  message: `credential binding ${credentialBindingId} is assigned to multiple Git providers`
7544
7608
  });
7545
7609
  }
@@ -7567,7 +7631,7 @@ function normalizeResources(resources) {
7567
7631
  const mountCollisionKey = normalized.mountPath ? resourceMountPathCollisionKey(normalized.mountPath) : void 0;
7568
7632
  const mounted = mountCollisionKey ? mountPaths.get(mountCollisionKey) : void 0;
7569
7633
  if (mounted && mounted !== key) {
7570
- throw new HTTPException14(422, {
7634
+ throw new HTTPException15(422, {
7571
7635
  message: `duplicate resource mount path: ${normalized.mountPath}`
7572
7636
  });
7573
7637
  }
@@ -7577,7 +7641,7 @@ function normalizeResources(resources) {
7577
7641
  const identity = resourceIdentityKey(normalized);
7578
7642
  const seenIdentity = identities.get(identity);
7579
7643
  if (seenIdentity && seenIdentity !== key) {
7580
- throw new HTTPException14(422, {
7644
+ throw new HTTPException15(422, {
7581
7645
  message: `duplicate resource with different settings: ${identity}`
7582
7646
  });
7583
7647
  }
@@ -7594,7 +7658,7 @@ function mergeResourceRefs(existing, additions) {
7594
7658
  return mergeContractResourceRefs(existing, additions, { rejectConflicts: true });
7595
7659
  } catch (error) {
7596
7660
  if (error instanceof ResourceRefConflictError) {
7597
- throw new HTTPException14(422, { message: error.message });
7661
+ throw new HTTPException15(422, { message: error.message });
7598
7662
  }
7599
7663
  throw error;
7600
7664
  }
@@ -7618,14 +7682,14 @@ function personalGitHubRepositoryResources(resources) {
7618
7682
  for (const resource of resources) {
7619
7683
  if (!isPersonalGitHubRepositoryCandidate(resource)) {
7620
7684
  if (resource.kind === "repository" && resource.connectionType === "github_personal") {
7621
- throw new HTTPException14(422, {
7685
+ throw new HTTPException15(422, {
7622
7686
  message: "personal GitHub repository resources require the dedicated GitHub provider"
7623
7687
  });
7624
7688
  }
7625
7689
  continue;
7626
7690
  }
7627
7691
  if (!PERSONAL_GITHUB_BINDING_ID_PATTERN.test(resource.credentialBindingId) || typeof resource.repositoryId !== "string" || !/^[1-9]\d*$/u.test(resource.repositoryId) || resource.access !== "read" && resource.access !== "write" || resource.installationId !== void 0 || resource.githubInstallationId !== void 0 || resource.githubRepositoryId !== void 0 || resource.connectionId !== void 0 || resource.projectId !== void 0) {
7628
- throw new HTTPException14(422, {
7692
+ throw new HTTPException15(422, {
7629
7693
  message: "personal GitHub repository resources require one opaque binding, provider repository id, and explicit read or write access"
7630
7694
  });
7631
7695
  }
@@ -7633,10 +7697,10 @@ function personalGitHubRepositoryResources(resources) {
7633
7697
  try {
7634
7698
  uri = new URL(resource.uri);
7635
7699
  } catch {
7636
- throw new HTTPException14(422, { message: "personal GitHub repository URI is invalid" });
7700
+ throw new HTTPException15(422, { message: "personal GitHub repository URI is invalid" });
7637
7701
  }
7638
7702
  if (uri.protocol !== "https:" || uri.hostname !== "github.com" || uri.port !== "" || uri.username !== "" || uri.password !== "" || uri.search !== "" || uri.hash !== "" || uri.pathname.endsWith(".git") || uri.pathname.split("/").filter(Boolean).length !== 2 || resource.uri !== `${uri.origin}${uri.pathname.replace(/\/$/u, "")}`) {
7639
- throw new HTTPException14(422, {
7703
+ throw new HTTPException15(422, {
7640
7704
  message: "personal GitHub repository resources require a canonical GitHub HTTPS URI"
7641
7705
  });
7642
7706
  }
@@ -7646,7 +7710,7 @@ function personalGitHubRepositoryResources(resources) {
7646
7710
  for (const resource of selected) {
7647
7711
  const key = `${resource.credentialBindingId}\0${resource.repositoryId}`;
7648
7712
  if (identities.has(key)) {
7649
- throw new HTTPException14(422, {
7713
+ throw new HTTPException15(422, {
7650
7714
  message: "personal GitHub repository resources must not contain duplicates"
7651
7715
  });
7652
7716
  }
@@ -7657,7 +7721,7 @@ function personalGitHubRepositoryResources(resources) {
7657
7721
  function validateGitHubRepositorySelectionShape(resources) {
7658
7722
  const installationIds = validateGitHubRepositorySelectionShapes(resources);
7659
7723
  if (installationIds.length > 1) {
7660
- throw new HTTPException14(422, {
7724
+ throw new HTTPException15(422, {
7661
7725
  message: "GitHub App repository resources must belong to one installation"
7662
7726
  });
7663
7727
  }
@@ -7682,7 +7746,7 @@ function gitHubRepositorySelections(resources) {
7682
7746
  const installationId = positiveInteger2(installationRaw);
7683
7747
  const repositoryId = positiveInteger2(repositoryRaw);
7684
7748
  if (!installationId || !repositoryId) {
7685
- throw new HTTPException14(422, {
7749
+ throw new HTTPException15(422, {
7686
7750
  message: "GitHub App repository resources require positive github_installation_id and github_repository_id"
7687
7751
  });
7688
7752
  }
@@ -7704,14 +7768,14 @@ async function validateGitHubRepositorySelection(db, workspaceId, resources) {
7704
7768
  installationId,
7705
7769
  repositoryIds
7706
7770
  )) {
7707
- throw new HTTPException14(422, {
7771
+ throw new HTTPException15(422, {
7708
7772
  message: "GitHub App repository resources must be authorized for a GitHub App installation linked to this workspace"
7709
7773
  });
7710
7774
  }
7711
7775
  }
7712
7776
  }
7713
7777
  function isAuthoritativeGitHubRepositorySelectionError(error) {
7714
- return error instanceof HTTPException14 && error.status === 422;
7778
+ return error instanceof HTTPException15 && error.status === 422;
7715
7779
  }
7716
7780
  async function validateFileResources(db, accountId, workspaceId, subjectId, resources) {
7717
7781
  const fileIds = /* @__PURE__ */ new Set();
@@ -7720,7 +7784,7 @@ async function validateFileResources(db, accountId, workspaceId, subjectId, reso
7720
7784
  continue;
7721
7785
  }
7722
7786
  if (fileIds.has(resource.fileId)) {
7723
- throw new HTTPException14(422, { message: `duplicate file resource: ${resource.fileId}` });
7787
+ throw new HTTPException15(422, { message: `duplicate file resource: ${resource.fileId}` });
7724
7788
  }
7725
7789
  fileIds.add(resource.fileId);
7726
7790
  const file = await requireFileForSubject(db, {
@@ -7730,10 +7794,10 @@ async function validateFileResources(db, accountId, workspaceId, subjectId, reso
7730
7794
  fileId: resource.fileId
7731
7795
  }).catch(() => null);
7732
7796
  if (!file) {
7733
- throw new HTTPException14(422, { message: `unknown file resource: ${resource.fileId}` });
7797
+ throw new HTTPException15(422, { message: `unknown file resource: ${resource.fileId}` });
7734
7798
  }
7735
7799
  if (file.status !== "ready") {
7736
- throw new HTTPException14(422, {
7800
+ throw new HTTPException15(422, {
7737
7801
  message: `file resource ${resource.fileId} is ${file.status}`
7738
7802
  });
7739
7803
  }
@@ -7744,7 +7808,7 @@ function normalizeMountPath(path) {
7744
7808
  return normalizeResourceMountPath(path);
7745
7809
  } catch (error) {
7746
7810
  if (!(error instanceof ResourceMountPathError)) throw error;
7747
- throw new HTTPException14(422, { message: `invalid resource mount path: ${path}` });
7811
+ throw new HTTPException15(422, { message: `invalid resource mount path: ${path}` });
7748
7812
  }
7749
7813
  }
7750
7814
  function positiveInteger2(value) {
@@ -8825,11 +8889,11 @@ import {
8825
8889
  withWorkspaceSubjectRls as withWorkspaceSubjectRls5,
8826
8890
  resolveXaiProviderAccountAuthoritySnapshotForAcceptance
8827
8891
  } from "@opengeni/db";
8828
- import { HTTPException as HTTPException20 } from "hono/http-exception";
8892
+ import { HTTPException as HTTPException21 } from "hono/http-exception";
8829
8893
  import { isDeepStrictEqual } from "util";
8830
8894
 
8831
8895
  // src/domain/host-mcp-task-admission.ts
8832
- import { HTTPException as HTTPException15 } from "hono/http-exception";
8896
+ import { HTTPException as HTTPException16 } from "hono/http-exception";
8833
8897
  import {
8834
8898
  HostMcpBindingDefinition,
8835
8899
  HostMcpCreateSelections
@@ -8842,7 +8906,7 @@ import {
8842
8906
  function prepareHostMcpTaskAdmission(input) {
8843
8907
  const selections = HostMcpCreateSelections.parse(input.selections);
8844
8908
  if (!input.authorization || input.authorization.grant.accountId !== input.grant.accountId || input.authorization.grant.subjectId !== input.grant.subjectId || input.authorization?.grant.workspaceId !== input.grant.workspaceId || input.grant.metadata?.["sessionId"] || !hasPermission(input.authorization?.grant.permissions ?? [], "connections:read") || !hasPermission(input.grant.permissions, "connections:read"))
8845
- throw new HTTPException15(403, {
8909
+ throw new HTTPException16(403, {
8846
8910
  message: "Host task selection requires a verified owner"
8847
8911
  });
8848
8912
  const reauthorize = prepareHostMcpOwnerAuthorization(
@@ -8855,7 +8919,7 @@ function prepareHostMcpTaskAdmission(input) {
8855
8919
  (candidate) => candidate.id === selection.serverId
8856
8920
  );
8857
8921
  if (!server?.url || server.connectionRef?.authoritySource !== "host" || !server.connectionRef.hostBinding || !input.tools.some((tool) => tool.kind === "mcp" && tool.id === selection.serverId))
8858
- throw new HTTPException15(422, {
8922
+ throw new HTTPException16(422, {
8859
8923
  message: "Host task selection must match a selected configured server"
8860
8924
  });
8861
8925
  assertHostMcpAuthoritySourceAdmissionEnabled(input.settings, server.connectionRef);
@@ -8878,7 +8942,7 @@ function prepareHostMcpTaskAdmission(input) {
8878
8942
  if (prepared.length) await captureHostMcpTaskAuthorities(tx, owner, task, prepared);
8879
8943
  } catch (error) {
8880
8944
  if (error instanceof HostMcpDelegationAuthorityError)
8881
- throw new HTTPException15(403, { message: error.message });
8945
+ throw new HTTPException16(403, { message: error.message });
8882
8946
  throw error;
8883
8947
  }
8884
8948
  };
@@ -9019,14 +9083,14 @@ import {
9019
9083
  publishDurableSessionEvents as publishDurableSessionEvents2,
9020
9084
  publishDurableWorkspaceControlEvent
9021
9085
  } from "@opengeni/events";
9022
- import { HTTPException as HTTPException19 } from "hono/http-exception";
9086
+ import { HTTPException as HTTPException20 } from "hono/http-exception";
9023
9087
 
9024
9088
  // src/domain/external-creation-attribution.ts
9025
- import { HTTPException as HTTPException16 } from "hono/http-exception";
9089
+ import { HTTPException as HTTPException17 } from "hono/http-exception";
9026
9090
  var EXTERNAL_CREATION_ATTRIBUTION_KEY = "opengeniExternalCreationAttribution";
9027
9091
  function externalCreationMetadata(metadata, authorization, grant) {
9028
9092
  if (metadata && Object.hasOwn(metadata, EXTERNAL_CREATION_ATTRIBUTION_KEY)) {
9029
- throw new HTTPException16(422, {
9093
+ throw new HTTPException17(422, {
9030
9094
  message: `${EXTERNAL_CREATION_ATTRIBUTION_KEY} is server-owned`
9031
9095
  });
9032
9096
  }
@@ -9042,11 +9106,11 @@ import {
9042
9106
  numberTimelineAnnotations
9043
9107
  } from "@opengeni/contracts";
9044
9108
  import { getSessionEvent } from "@opengeni/db";
9045
- import { HTTPException as HTTPException17 } from "hono/http-exception";
9109
+ import { HTTPException as HTTPException18 } from "hono/http-exception";
9046
9110
  var SOURCE_CONTEXT_BYTES = 160;
9047
9111
  var ANSI_SEQUENCE = new RegExp("\\u001B\\[[0-?]*[ -/]*[@-~]", "g");
9048
9112
  function invalidAnnotationSource() {
9049
- throw new HTTPException17(422, { message: "Invalid timeline annotation source" });
9113
+ throw new HTTPException18(422, { message: "Invalid timeline annotation source" });
9050
9114
  }
9051
9115
  function safeJson(value) {
9052
9116
  try {
@@ -9196,7 +9260,7 @@ import {
9196
9260
  import {
9197
9261
  getConnectionMetadata as getConnectionMetadata3
9198
9262
  } from "@opengeni/db";
9199
- import { HTTPException as HTTPException18 } from "hono/http-exception";
9263
+ import { HTTPException as HTTPException19 } from "hono/http-exception";
9200
9264
  function openGeniSlackBotMetadata(metadata) {
9201
9265
  const parsed = OpenGeniSlackBotConnectionMetadata.safeParse(metadata);
9202
9266
  return parsed.success ? parsed.data : null;
@@ -9215,12 +9279,12 @@ function hasReservedOpenGeniSlackBotSessionMetadata(metadata) {
9215
9279
  async function requireOpenGeniSlackBotConnection(db, workspaceId, connectionId) {
9216
9280
  const connection = await getConnectionMetadata3(db, workspaceId, connectionId, null);
9217
9281
  if (!connection || !isOpenGeniSlackBotConnection(connection)) {
9218
- throw new HTTPException18(422, {
9282
+ throw new HTTPException19(422, {
9219
9283
  message: "slackBotConnectionId must reference an OpenGeni Slack bot connection"
9220
9284
  });
9221
9285
  }
9222
9286
  if (connection.status !== "active") {
9223
- throw new HTTPException18(422, {
9287
+ throw new HTTPException19(422, {
9224
9288
  message: `OpenGeni Slack bot connection is not active (${connection.status})`
9225
9289
  });
9226
9290
  }
@@ -9564,25 +9628,25 @@ var sessionMcpCredentialHeaderName = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
9564
9628
  async function requireAtomicPersonalResourceAttachment(deps, authorization, workspaceId, intent, existingSession) {
9565
9629
  if (!intent) return;
9566
9630
  if (!authorization) {
9567
- throw new HTTPException19(403, {
9631
+ throw new HTTPException20(403, {
9568
9632
  message: "Personal resources require the owning managed-human session."
9569
9633
  });
9570
9634
  }
9571
9635
  try {
9572
9636
  requireVerifiedOwningUser(authorization, workspaceId);
9573
9637
  } catch (error) {
9574
- throw new HTTPException19(403, {
9638
+ throw new HTTPException20(403, {
9575
9639
  message: "Personal resources require the owning managed-human session.",
9576
9640
  cause: error
9577
9641
  });
9578
9642
  }
9579
9643
  if (!await sessionTenancyProductActivated2(deps.db, workspaceId)) {
9580
- throw new HTTPException19(409, {
9644
+ throw new HTTPException20(409, {
9581
9645
  message: "Session tenancy is not activated for this organization."
9582
9646
  });
9583
9647
  }
9584
9648
  if (existingSession && intent.expectedAuthorityEpoch === void 0) {
9585
- throw new HTTPException19(422, {
9649
+ throw new HTTPException20(422, {
9586
9650
  message: "Personal-resource attachment requires expectedAuthorityEpoch."
9587
9651
  });
9588
9652
  }
@@ -9610,7 +9674,7 @@ function resolveFirstPartyMcpToolsForCreate(requested, parentStored, policy = {
9610
9674
  const parentCeiling = effectiveFirstPartyMcpToolCeiling(parentStored, policy);
9611
9675
  const widened = requested.find((tool) => !parentCeiling.has(tool));
9612
9676
  if (widened) {
9613
- throw new HTTPException19(403, {
9677
+ throw new HTTPException20(403, {
9614
9678
  message: `child first-party MCP tools may only narrow the parent session selection: ${widened}`
9615
9679
  });
9616
9680
  }
@@ -9639,7 +9703,7 @@ function sessionSpawnDenialEnvelope(error) {
9639
9703
  function serviceInitiatorForGrant(grant) {
9640
9704
  if (!grant.serviceInitiator) {
9641
9705
  if (grant.serviceInitiatorContext) {
9642
- throw new HTTPException19(403, {
9706
+ throw new HTTPException20(403, {
9643
9707
  message: "service initiator context requires a signed service initiator"
9644
9708
  });
9645
9709
  }
@@ -9647,13 +9711,13 @@ function serviceInitiatorForGrant(grant) {
9647
9711
  }
9648
9712
  const initiator = ServiceTurnInitiator.safeParse(grant.serviceInitiator);
9649
9713
  if (!initiator.success) {
9650
- throw new HTTPException19(403, {
9714
+ throw new HTTPException20(403, {
9651
9715
  message: "a delegated command initiator must be a bounded service principal"
9652
9716
  });
9653
9717
  }
9654
9718
  const context = ServiceTurnInitiatorContext.safeParse(grant.serviceInitiatorContext ?? {});
9655
9719
  if (!context.success) {
9656
- throw new HTTPException19(403, {
9720
+ throw new HTTPException20(403, {
9657
9721
  message: "delegated service initiator context is invalid or reserved"
9658
9722
  });
9659
9723
  }
@@ -9661,7 +9725,7 @@ function serviceInitiatorForGrant(grant) {
9661
9725
  const callerAttemptId = grant.metadata?.["attemptId"];
9662
9726
  const callerExecutionGeneration = grant.metadata?.["executionGeneration"];
9663
9727
  if (callerTurnId !== void 0 || callerAttemptId !== void 0 || callerExecutionGeneration !== void 0) {
9664
- throw new HTTPException19(403, {
9728
+ throw new HTTPException20(403, {
9665
9729
  message: "a service initiator cannot replace an exact agent-attempt initiator"
9666
9730
  });
9667
9731
  }
@@ -9679,7 +9743,7 @@ function creationInitiatorForGrant(grant) {
9679
9743
  const hasCallerTurnClaim = callerTurnId !== void 0 || callerAttemptId !== void 0 || callerExecutionGeneration !== void 0;
9680
9744
  if (hasCallerTurnClaim) {
9681
9745
  if (typeof callerSessionId !== "string" || typeof callerTurnId !== "string" || typeof callerAttemptId !== "string" || typeof callerExecutionGeneration !== "number" || !Number.isSafeInteger(callerExecutionGeneration) || callerExecutionGeneration < 1) {
9682
- throw new HTTPException19(403, {
9746
+ throw new HTTPException20(403, {
9683
9747
  message: "caller attempt claims are incomplete"
9684
9748
  });
9685
9749
  }
@@ -9710,31 +9774,31 @@ function normalizedSessionMcpCredentialHeaders(headers) {
9710
9774
  }
9711
9775
  const entries = Object.entries(headers).map(([name, value]) => [name.trim(), value]).filter(([name]) => name.length > 0);
9712
9776
  if (entries.length > maxSessionMcpCredentialHeaders) {
9713
- throw new HTTPException19(422, {
9777
+ throw new HTTPException20(422, {
9714
9778
  message: `a session MCP server supports at most ${maxSessionMcpCredentialHeaders} credential headers`
9715
9779
  });
9716
9780
  }
9717
9781
  const seen = /* @__PURE__ */ new Set();
9718
9782
  for (const [name, value] of entries) {
9719
9783
  if (!sessionMcpCredentialHeaderName.test(name)) {
9720
- throw new HTTPException19(422, {
9784
+ throw new HTTPException20(422, {
9721
9785
  message: `invalid credential header name: ${name}`
9722
9786
  });
9723
9787
  }
9724
9788
  const lower = name.toLowerCase();
9725
9789
  if (seen.has(lower)) {
9726
- throw new HTTPException19(422, {
9790
+ throw new HTTPException20(422, {
9727
9791
  message: `duplicate credential header name: ${name}`
9728
9792
  });
9729
9793
  }
9730
9794
  seen.add(lower);
9731
9795
  if (value.length === 0 || value.length > maxSessionMcpCredentialHeaderValueLength) {
9732
- throw new HTTPException19(422, {
9796
+ throw new HTTPException20(422, {
9733
9797
  message: `credential header ${name} must be 1-${maxSessionMcpCredentialHeaderValueLength} characters`
9734
9798
  });
9735
9799
  }
9736
9800
  if (/[\u0000-\u0008\u000A-\u001F\u007F]/.test(value)) {
9737
- throw new HTTPException19(422, {
9801
+ throw new HTTPException20(422, {
9738
9802
  message: `credential header ${name} contains forbidden control characters`
9739
9803
  });
9740
9804
  }
@@ -9802,13 +9866,13 @@ function validateSessionMcpServersForCreate(settings, grant, servers) {
9802
9866
  for (const server of servers) {
9803
9867
  assertHostMcpAuthoritySourceAdmissionEnabled(settings, server.connectionRef);
9804
9868
  if (seenIds.has(server.id)) {
9805
- throw new HTTPException19(422, {
9869
+ throw new HTTPException20(422, {
9806
9870
  message: `duplicate session MCP server id: ${server.id}`
9807
9871
  });
9808
9872
  }
9809
9873
  seenIds.add(server.id);
9810
9874
  if (reservedSessionMcpServerIds.has(server.id) || existingIds.has(server.id)) {
9811
- throw new HTTPException19(422, {
9875
+ throw new HTTPException20(422, {
9812
9876
  message: `MCP server id already exists: ${server.id}`
9813
9877
  });
9814
9878
  }
@@ -9850,13 +9914,13 @@ function validateInheritedSessionMcpServersForCreate(servers) {
9850
9914
  const seenIds = /* @__PURE__ */ new Set();
9851
9915
  for (const server of servers) {
9852
9916
  if (seenIds.has(server.id)) {
9853
- throw new HTTPException19(422, {
9917
+ throw new HTTPException20(422, {
9854
9918
  message: `duplicate inherited session MCP server id: ${server.id}`
9855
9919
  });
9856
9920
  }
9857
9921
  seenIds.add(server.id);
9858
9922
  if (reservedSessionMcpServerIds.has(server.id)) {
9859
- throw new HTTPException19(422, {
9923
+ throw new HTTPException20(422, {
9860
9924
  message: `reserved inherited session MCP server id: ${server.id}`
9861
9925
  });
9862
9926
  }
@@ -9888,13 +9952,13 @@ function validateSessionMcpCredentialUpdates(input) {
9888
9952
  const seenIds = /* @__PURE__ */ new Set();
9889
9953
  const encryptedUpdates = input.updates.map((update) => {
9890
9954
  if (seenIds.has(update.id)) {
9891
- throw new HTTPException19(422, {
9955
+ throw new HTTPException20(422, {
9892
9956
  message: `duplicate session MCP credential update id: ${update.id}`
9893
9957
  });
9894
9958
  }
9895
9959
  seenIds.add(update.id);
9896
9960
  if (!knownIds.has(update.id)) {
9897
- throw new HTTPException19(422, {
9961
+ throw new HTTPException20(422, {
9898
9962
  message: `unknown session MCP server id: ${update.id}`
9899
9963
  });
9900
9964
  }
@@ -9961,7 +10025,7 @@ async function createAndStartSessionWithOutcome(input) {
9961
10025
  let targetSeededBeforeCreateCommit = false;
9962
10026
  const beforeCreateCommit = requiresActiveWorkspaceCustomModel || input.consumeNewSessionDraft || input.beforeCreateCommit || preflightTarget || targetPreflightFailureMessage ? async (tx, sessionId, context) => {
9963
10027
  if (targetPreflightFailureMessage && context?.created !== false) {
9964
- throw new HTTPException19(422, {
10028
+ throw new HTTPException20(422, {
9965
10029
  message: targetPreflightFailureMessage
9966
10030
  });
9967
10031
  }
@@ -9977,7 +10041,7 @@ async function createAndStartSessionWithOutcome(input) {
9977
10041
  reference
9978
10042
  });
9979
10043
  if (!active) {
9980
- throw new HTTPException19(422, {
10044
+ throw new HTTPException20(422, {
9981
10045
  message: `model is not available: ${input.model}`
9982
10046
  });
9983
10047
  }
@@ -10002,7 +10066,7 @@ async function createAndStartSessionWithOutcome(input) {
10002
10066
  workingDir: preflightTarget.workingDir
10003
10067
  });
10004
10068
  if (!seeded.swapped) {
10005
- throw new HTTPException19(422, {
10069
+ throw new HTTPException20(422, {
10006
10070
  message: `cannot target sandbox ${seedTargetForNewSession.sandboxId}: target authority changed during session creation`
10007
10071
  });
10008
10072
  }
@@ -10187,7 +10251,7 @@ async function finishStartSession(input, session) {
10187
10251
  input.seedTargetSandbox.workingDir ?? null
10188
10252
  );
10189
10253
  if (!seeded.swapped) {
10190
- throw new HTTPException19(422, {
10254
+ throw new HTTPException20(422, {
10191
10255
  message: `cannot target sandbox ${input.seedTargetSandbox.sandboxId}: ${seeded.reason ?? "target is not attachable"}`
10192
10256
  });
10193
10257
  }
@@ -10262,7 +10326,7 @@ function canonicalConfiguredModel(settings, model) {
10262
10326
  if (settings.supergrokSubscriptionEnabled && canonicalModel.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)) {
10263
10327
  return canonicalModel;
10264
10328
  }
10265
- throw new HTTPException19(422, { message: `model is not available: ${model}` });
10329
+ throw new HTTPException20(422, { message: `model is not available: ${model}` });
10266
10330
  }
10267
10331
  function assertConfiguredModel(settings, model) {
10268
10332
  canonicalConfiguredModel(settings, model);
@@ -10303,7 +10367,7 @@ async function assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model
10303
10367
  modelId: canonicalModel
10304
10368
  });
10305
10369
  if (!verdict.allowed) {
10306
- throw new HTTPException19(422, {
10370
+ throw new HTTPException20(422, {
10307
10371
  message: verdict.reason === "provider" ? `model "${canonicalModel}" is not allowed by this workspace's model policy: provider "${providerId}" is not in the allowed providers` : `model "${canonicalModel}" is not allowed by this workspace's model policy`
10308
10372
  });
10309
10373
  }
@@ -10311,10 +10375,10 @@ async function assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model
10311
10375
  async function requireQueuedTurnForApi(db, workspaceId, sessionId, turnId) {
10312
10376
  const turn = await getSessionTurn(db, workspaceId, turnId);
10313
10377
  if (!turn || turn.sessionId !== sessionId) {
10314
- throw new HTTPException19(404, { message: "session turn not found" });
10378
+ throw new HTTPException20(404, { message: "session turn not found" });
10315
10379
  }
10316
10380
  if (turn.status !== "queued") {
10317
- throw new HTTPException19(409, {
10381
+ throw new HTTPException20(409, {
10318
10382
  message: `turn is ${turn.status}; only queued turns can be changed`
10319
10383
  });
10320
10384
  }
@@ -10425,7 +10489,7 @@ async function postUserMessageTurn(input) {
10425
10489
  assertSessionAllowsProductModel(sessionForModelGate, effectiveModelForGate);
10426
10490
  } catch (error) {
10427
10491
  if (error instanceof CodexCompactionV2ProviderLockedError) {
10428
- throw new HTTPException19(422, { message: error.message, cause: error });
10492
+ throw new HTTPException20(422, { message: error.message, cause: error });
10429
10493
  }
10430
10494
  throw error;
10431
10495
  }
@@ -10491,7 +10555,7 @@ async function postUserMessageTurn(input) {
10491
10555
  reference
10492
10556
  });
10493
10557
  if (!active) {
10494
- throw new HTTPException19(422, {
10558
+ throw new HTTPException20(422, {
10495
10559
  message: `model is not available: ${freshWorkspaceCustomModel}`
10496
10560
  });
10497
10561
  }
@@ -10506,19 +10570,19 @@ async function postUserMessageTurn(input) {
10506
10570
  throw error;
10507
10571
  }
10508
10572
  if (error instanceof PersonalResourceAttachmentAcceptanceError) {
10509
- throw new HTTPException19(
10573
+ throw new HTTPException20(
10510
10574
  error.kind === "invalid" ? 422 : error.kind === "forbidden" ? 403 : 409,
10511
10575
  { message: error.message, cause: error }
10512
10576
  );
10513
10577
  }
10514
10578
  if (error instanceof QueueCommandConflictError || error instanceof SessionControlConflictError) {
10515
- throw new HTTPException19(409, { message: error.message });
10579
+ throw new HTTPException20(409, { message: error.message });
10516
10580
  }
10517
10581
  if (error instanceof Error && error.message.includes("cancelled")) {
10518
- throw new HTTPException19(409, { message: error.message });
10582
+ throw new HTTPException20(409, { message: error.message });
10519
10583
  }
10520
10584
  if (error instanceof Error && error.message.startsWith("Unknown session MCP server")) {
10521
- throw new HTTPException19(422, { message: error.message });
10585
+ throw new HTTPException20(422, { message: error.message });
10522
10586
  }
10523
10587
  throw error;
10524
10588
  }
@@ -10574,28 +10638,28 @@ function resolveSessionCreateScope(input) {
10574
10638
  } else {
10575
10639
  const agentAccess = requested.agentAccessProvided ? requested.agentAccess : parent.agentAccess;
10576
10640
  if (AGENT_ACCESS_WIDTH[agentAccess] > AGENT_ACCESS_WIDTH[parent.agentAccess]) {
10577
- throw new HTTPException19(403, {
10641
+ throw new HTTPException20(403, {
10578
10642
  message: "child agent access may only narrow the parent session"
10579
10643
  });
10580
10644
  }
10581
10645
  if (requested.endUserProvided) {
10582
10646
  const same = requested.scopeSubjectId !== null && parent.scopeSubjectId !== null && requested.scopeSubjectId === parent.scopeSubjectId;
10583
10647
  if (!same) {
10584
- throw new HTTPException19(403, {
10648
+ throw new HTTPException20(403, {
10585
10649
  message: "child end-user label must equal the parent session label"
10586
10650
  });
10587
10651
  }
10588
10652
  }
10589
10653
  const memoryScope = requested.memoryScopeProvided ? requested.memoryScope : parent.memoryScope;
10590
10654
  if (MEMORY_SCOPE_WIDTH[memoryScope] > MEMORY_SCOPE_WIDTH[parent.memoryScope]) {
10591
- throw new HTTPException19(403, {
10655
+ throw new HTTPException20(403, {
10592
10656
  message: "child memory scope may only narrow the parent session"
10593
10657
  });
10594
10658
  }
10595
10659
  resolved = { agentAccess, scopeSubjectId: parent.scopeSubjectId, memoryScope };
10596
10660
  }
10597
10661
  if (resolved.memoryScope === "user" && resolved.scopeSubjectId === null) {
10598
- throw new HTTPException19(422, { message: 'memoryScope "user" requires an authenticated user' });
10662
+ throw new HTTPException20(422, { message: 'memoryScope "user" requires an authenticated user' });
10599
10663
  }
10600
10664
  return resolved;
10601
10665
  }
@@ -10645,14 +10709,14 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10645
10709
  const externalBeforeCreateCommit = externalContinuationCommitAuthorizer(authorization);
10646
10710
  const hostSelections = payload.selectedHostMcpDelegations ?? [];
10647
10711
  if (hostSelections.length && (!authorization || authorization.grant.accountId !== grant.accountId || authorization.grant.subjectId !== grant.subjectId || authorization?.grant.workspaceId !== workspaceId || !hasPermission(authorization?.grant.permissions ?? [], "connections:read") || !hasPermission(grant.permissions, "connections:read") || grant.metadata?.["sessionId"] || payload.startMode === "realtime")) {
10648
- throw new HTTPException19(403, {
10712
+ throw new HTTPException20(403, {
10649
10713
  message: "Host selection requires a verified direct owner and non-realtime start"
10650
10714
  });
10651
10715
  }
10652
10716
  if (hostSelections.length)
10653
10717
  prepareHostMcpOwnerAuthorization(authorization, workspaceId, "connections:read");
10654
10718
  if (hasReservedOpenGeniSlackBotSessionMetadata(payload.metadata)) {
10655
- throw new HTTPException19(422, {
10719
+ throw new HTTPException20(422, {
10656
10720
  message: `${OPENGENI_SLACK_BOT_SESSION_METADATA_KEY2} is reserved for scheduler routing`
10657
10721
  });
10658
10722
  }
@@ -10660,13 +10724,13 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10660
10724
  const visibilityProvided = hasOwnProperty(rawPayload, "visibility");
10661
10725
  if (payload.visibility === "private" && !grant.metadata?.["sessionId"]) {
10662
10726
  if (!authorization) {
10663
- throw new HTTPException19(403, {
10727
+ throw new HTTPException20(403, {
10664
10728
  message: "managed human session required"
10665
10729
  });
10666
10730
  }
10667
10731
  await requireManagedHumanPrivateSessionCreate(unresolvedDeps, authorization, workspaceId);
10668
10732
  if (payload.sandbox === "shared" || typeof payload.sandbox === "object") {
10669
- throw new HTTPException19(422, {
10733
+ throw new HTTPException20(422, {
10670
10734
  message: "Only-me sessions require their own sandbox"
10671
10735
  });
10672
10736
  }
@@ -10681,14 +10745,14 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10681
10745
  });
10682
10746
  } catch (error) {
10683
10747
  if (error instanceof SessionAuthorizationDeniedError) {
10684
- throw new HTTPException19(403, { message: error.message, cause: error });
10748
+ throw new HTTPException20(403, { message: error.message, cause: error });
10685
10749
  }
10686
10750
  throw error;
10687
10751
  }
10688
10752
  }
10689
10753
  const parentSession = parentSessionId ? await getSession2(db, workspaceId, parentSessionId) : null;
10690
10754
  if (parentSessionId && !parentSession) {
10691
- throw new HTTPException19(404, {
10755
+ throw new HTTPException20(404, {
10692
10756
  message: `parent session not found in workspace: ${parentSessionId}`
10693
10757
  });
10694
10758
  }
@@ -10696,7 +10760,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10696
10760
  const workspaceSessionToolDefaults = resolveWorkspaceSessionToolDefaults2(workspace.settings);
10697
10761
  const parentAuthority = parentSession ? await getSessionAuthorityProjection2(db, workspaceId, parentSession.id) : null;
10698
10762
  if (parentSession && !parentAuthority) {
10699
- throw new HTTPException19(403, {
10763
+ throw new HTTPException20(403, {
10700
10764
  message: "parent session authority is unavailable"
10701
10765
  });
10702
10766
  }
@@ -10708,7 +10772,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10708
10772
  parentVisibility: parentAuthority?.visibility ?? null
10709
10773
  });
10710
10774
  } catch (error) {
10711
- throw new HTTPException19(422, {
10775
+ throw new HTTPException20(422, {
10712
10776
  message: error instanceof Error ? error.message : "invalid child visibility"
10713
10777
  });
10714
10778
  }
@@ -10719,7 +10783,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10719
10783
  parentSession?.bundledSkillIds
10720
10784
  );
10721
10785
  } catch (error) {
10722
- throw new HTTPException19(422, {
10786
+ throw new HTTPException20(422, {
10723
10787
  message: error instanceof Error ? error.message : "Invalid bundled Skill selection"
10724
10788
  });
10725
10789
  }
@@ -10747,7 +10811,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10747
10811
  creationInitiator.actor.attemptId
10748
10812
  ) : null;
10749
10813
  if (creationInitiator.actor && (!parentCallingTurn || parentCallingTurn.sessionId !== parentSession?.id)) {
10750
- throw new HTTPException19(403, {
10814
+ throw new HTTPException20(403, {
10751
10815
  message: "caller attempt does not belong to the parent session"
10752
10816
  });
10753
10817
  }
@@ -10803,12 +10867,12 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10803
10867
  }
10804
10868
  } catch (error) {
10805
10869
  if (error instanceof SessionIdConflictError) {
10806
- throw new HTTPException19(409, {
10870
+ throw new HTTPException20(409, {
10807
10871
  message: "requested session id is already in use"
10808
10872
  });
10809
10873
  }
10810
10874
  if (error instanceof SessionCreateIdempotencyConflictError) {
10811
- throw new HTTPException19(409, { message: error.message, cause: error });
10875
+ throw new HTTPException20(409, { message: error.message, cause: error });
10812
10876
  }
10813
10877
  throw error;
10814
10878
  }
@@ -10861,7 +10925,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10861
10925
  }
10862
10926
  );
10863
10927
  } catch (error) {
10864
- throw new HTTPException19(422, {
10928
+ throw new HTTPException20(422, {
10865
10929
  message: error instanceof Error ? error.message : "invalid child goal root constraints"
10866
10930
  });
10867
10931
  }
@@ -10914,12 +10978,12 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10914
10978
  for (const capabilityId of selectedInstalledSkillIds) {
10915
10979
  const installed = installedById.get(capabilityId);
10916
10980
  if (!installed) {
10917
- throw new HTTPException19(422, {
10981
+ throw new HTTPException20(422, {
10918
10982
  message: `Session-selected Skill is not installed in this workspace: ${capabilityId}`
10919
10983
  });
10920
10984
  }
10921
10985
  if (installed.activationMode !== "session_selected") {
10922
- throw new HTTPException19(422, {
10986
+ throw new HTTPException20(422, {
10923
10987
  message: `Installed Skill does not require explicit session selection: ${capabilityId}`
10924
10988
  });
10925
10989
  }
@@ -10934,7 +10998,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10934
10998
  try {
10935
10999
  skills = SessionSkills.parse([...inheritedOrSubmittedSkills, ...selectedInstalledSkills]);
10936
11000
  } catch (error) {
10937
- throw new HTTPException19(422, {
11001
+ throw new HTTPException20(422, {
10938
11002
  message: error instanceof Error ? error.message : "invalid session Skill selection"
10939
11003
  });
10940
11004
  }
@@ -10998,7 +11062,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10998
11062
  const captureLinkedAuthority = prepareExternalLinkTurnAdmission(authorization);
10999
11063
  await validateGitHubRepositorySelection(db, workspaceId, resources);
11000
11064
  if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
11001
- throw new HTTPException19(503, {
11065
+ throw new HTTPException20(503, {
11002
11066
  message: "object storage is not configured"
11003
11067
  });
11004
11068
  }
@@ -11016,7 +11080,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11016
11080
  const rig = await getRig4(db, grant, requestedRigId);
11017
11081
  if (!rig || !rig.activeVersion) {
11018
11082
  if (payload.rigId) {
11019
- throw new HTTPException19(422, {
11083
+ throw new HTTPException20(422, {
11020
11084
  message: rig ? `rig ${payload.rigId} has no active version to bind` : `unknown rigId: ${payload.rigId}`
11021
11085
  });
11022
11086
  }
@@ -11037,7 +11101,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11037
11101
  if (payload.channelId) {
11038
11102
  const channel = await getChannel(db, workspaceId, payload.channelId);
11039
11103
  if (!channel) {
11040
- throw new HTTPException19(422, {
11104
+ throw new HTTPException20(422, {
11041
11105
  message: `unknown channelId: ${payload.channelId}`
11042
11106
  });
11043
11107
  }
@@ -11053,7 +11117,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11053
11117
  const reasoningEffort = payload.reasoningEffort ?? inheritedReasoningEffort;
11054
11118
  const latencyMode = payload.latencyMode ?? inheritedLatencyMode;
11055
11119
  if (payload.expectedNewSessionDraftRevision !== void 0 && payload.rigId === null) {
11056
- throw new HTTPException19(409, {
11120
+ throw new HTTPException20(409, {
11057
11121
  message: "The submitted session options are not represented by the new-session draft"
11058
11122
  });
11059
11123
  }
@@ -11094,7 +11158,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11094
11158
  if (parentFirstPartyMcpPermissions && payload.firstPartyMcpPermissions?.some(
11095
11159
  (permission) => !hasPermission(parentFirstPartyMcpPermissions, permission)
11096
11160
  )) {
11097
- throw new HTTPException19(403, {
11161
+ throw new HTTPException20(403, {
11098
11162
  message: "child first-party MCP permissions may only narrow the parent session grant"
11099
11163
  });
11100
11164
  }
@@ -11102,19 +11166,19 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11102
11166
  (permission) => hasPermission(grant.permissions, permission)
11103
11167
  ) : null);
11104
11168
  if (firstPartyMcpPermissions && firstPartyMcpPermissions.length === 0) {
11105
- throw new HTTPException19(422, {
11169
+ throw new HTTPException20(422, {
11106
11170
  message: "firstPartyMcpPermissions must not be empty; omit it for the default worker permission set"
11107
11171
  });
11108
11172
  }
11109
11173
  for (const permission of firstPartyMcpPermissions ?? []) {
11110
11174
  if (!hasPermission(grant.permissions, permission)) {
11111
- throw new HTTPException19(403, {
11175
+ throw new HTTPException20(403, {
11112
11176
  message: `cannot grant first-party MCP permission beyond the creating grant: ${permission}`
11113
11177
  });
11114
11178
  }
11115
11179
  }
11116
11180
  if (effectiveGoal && firstPartyMcpPermissions && !firstPartyMcpPermissions.includes("goals:manage")) {
11117
- throw new HTTPException19(422, {
11181
+ throw new HTTPException20(422, {
11118
11182
  message: "goal-bearing sessions require goals:manage in the resulting first-party MCP permission set"
11119
11183
  });
11120
11184
  }
@@ -11123,7 +11187,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11123
11187
  (tool) => !deploymentFirstPartyMcpToolPolicy.allowed.includes(tool)
11124
11188
  );
11125
11189
  if (disallowedFirstPartyMcpTool) {
11126
- throw new HTTPException19(422, {
11190
+ throw new HTTPException20(422, {
11127
11191
  message: `first-party MCP tool is disabled by deployment policy: ${disallowedFirstPartyMcpTool}`
11128
11192
  });
11129
11193
  }
@@ -11156,13 +11220,13 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11156
11220
  (name) => !firstPartyMcpTools.includes(name)
11157
11221
  );
11158
11222
  if (missingGoalTools.length > 0) {
11159
- throw new HTTPException19(422, {
11223
+ throw new HTTPException20(422, {
11160
11224
  message: `goal-bearing sessions require first-party MCP tools: ${missingGoalTools.join(", ")}`
11161
11225
  });
11162
11226
  }
11163
11227
  }
11164
11228
  if (payload.targetSandboxId && payload.sandbox !== void 0 && payload.sandbox !== "new") {
11165
- throw new HTTPException19(422, {
11229
+ throw new HTTPException20(422, {
11166
11230
  message: "targetSandboxId requires an own sandbox (omit sandbox or pass 'new'); it cannot join a shared group"
11167
11231
  });
11168
11232
  }
@@ -11176,7 +11240,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11176
11240
  const rigVersionMatchesGroup = (memberRigVersionId) => memberRigVersionId === frozenRigVersionId;
11177
11241
  if (sandboxChoice === "shared") {
11178
11242
  if (!parentSessionId) {
11179
- throw new HTTPException19(422, {
11243
+ throw new HTTPException20(422, {
11180
11244
  message: "sandbox:'shared' requires a parent session (spawn from inside a session); use 'new' for a top-level create."
11181
11245
  });
11182
11246
  }
@@ -11199,7 +11263,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11199
11263
  }
11200
11264
  if (variableSetMismatch || rigMismatch) {
11201
11265
  if (payload.sandbox === "shared") {
11202
- throw new HTTPException19(422, {
11266
+ throw new HTTPException20(422, {
11203
11267
  message: variableSetMismatch ? "sandbox:'shared' requires the same variableSet / same environment as the creator's box (the box variable set/environment is fixed at creation); omit sandbox or pass 'new' when attaching a different variableSet/environment." : "sandbox:'shared' requires the same rig as the creator's box (the box's rig setup is fixed at creation); omit sandbox or pass 'new' when binding a different rig."
11204
11268
  });
11205
11269
  }
@@ -11215,7 +11279,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11215
11279
  } else if (typeof sandboxChoice === "object") {
11216
11280
  const member = await getAnySessionInGroup(db, workspaceId, sandboxChoice.groupId);
11217
11281
  if (!member) {
11218
- throw new HTTPException19(404, {
11282
+ throw new HTTPException20(404, {
11219
11283
  message: `sandbox group not found in workspace: ${sandboxChoice.groupId}`
11220
11284
  });
11221
11285
  }
@@ -11228,7 +11292,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11228
11292
  if (!memberVariableSetSelections.every(
11229
11293
  (memberVariableSetIds) => variableSetsMatchGroup(memberVariableSetIds)
11230
11294
  )) {
11231
- throw new HTTPException19(422, {
11295
+ throw new HTTPException20(422, {
11232
11296
  message: `sandbox group ${sandboxChoice.groupId} runs a different variableSet / different environment (the box variable set/environment is fixed at creation); create with the group's variableSet/environment or omit sandbox for an own box.`
11233
11297
  });
11234
11298
  }
@@ -11240,7 +11304,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11240
11304
  if (!memberRigVersionIds.every(
11241
11305
  (memberRigVersionId) => rigVersionMatchesGroup(memberRigVersionId)
11242
11306
  )) {
11243
- throw new HTTPException19(422, {
11307
+ throw new HTTPException20(422, {
11244
11308
  message: `sandbox group ${sandboxChoice.groupId} runs a different rig (the box's rig setup is fixed at creation); create with the group's rig or omit sandbox for an own box.`
11245
11309
  });
11246
11310
  }
@@ -11250,12 +11314,12 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11250
11314
  inheritedSandboxOs = member.sandboxOs;
11251
11315
  }
11252
11316
  if (payload.workingDir !== void 0 && !payload.targetSandboxId) {
11253
- throw new HTTPException19(422, {
11317
+ throw new HTTPException20(422, {
11254
11318
  message: "workingDir requires targetSandboxId (it is the targeted machine's working directory)"
11255
11319
  });
11256
11320
  }
11257
11321
  if (inheritedBackend === void 0 && !payload.targetSandboxId && (payload.sandboxBackend ?? settings.sandboxBackend) === "selfhosted") {
11258
- throw new HTTPException19(422, {
11322
+ throw new HTTPException20(422, {
11259
11323
  message: "selfhosted sessions require targetSandboxId; select an online Connected Machine"
11260
11324
  });
11261
11325
  }
@@ -11288,7 +11352,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11288
11352
  workingDir: payload.workingDir ?? null
11289
11353
  } : inheritedActiveTarget;
11290
11354
  if (effectiveSandboxBackend === "selfhosted" && effectiveSeedTarget === null && managedSessionGroupBackend(settings.sandboxBackend, effectiveSandboxBackend) === null) {
11291
- throw new HTTPException19(422, {
11355
+ throw new HTTPException20(422, {
11292
11356
  message: "self-hosted execution runs on a Connected Machine, but no machine was selected or inherited; connect the parent session to a machine or provide machineTarget"
11293
11357
  });
11294
11358
  }
@@ -11411,39 +11475,39 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
11411
11475
  });
11412
11476
  } catch (error) {
11413
11477
  if (error instanceof PersonalResourceAttachmentAcceptanceError) {
11414
- throw new HTTPException19(
11478
+ throw new HTTPException20(
11415
11479
  error.kind === "invalid" ? 422 : error.kind === "forbidden" ? 403 : 409,
11416
11480
  { message: error.message, cause: error }
11417
11481
  );
11418
11482
  }
11419
11483
  if (error instanceof HostMcpDelegationAuthorityError2) {
11420
- throw new HTTPException19(403, {
11484
+ throw new HTTPException20(403, {
11421
11485
  message: "Host delegation authority unavailable",
11422
11486
  cause: error
11423
11487
  });
11424
11488
  }
11425
11489
  if (error instanceof HostMcpBindingConflictError) {
11426
- throw new HTTPException19(409, {
11490
+ throw new HTTPException20(409, {
11427
11491
  message: "Host delegation selection conflicts",
11428
11492
  cause: error
11429
11493
  });
11430
11494
  }
11431
11495
  if (error instanceof AgentCommandAuthorityError) {
11432
- throw new HTTPException19(403, { message: error.message });
11496
+ throw new HTTPException20(403, { message: error.message });
11433
11497
  }
11434
11498
  if (error instanceof SessionIdConflictError) {
11435
- throw new HTTPException19(409, {
11499
+ throw new HTTPException20(409, {
11436
11500
  message: "requested session id is already in use"
11437
11501
  });
11438
11502
  }
11439
11503
  if (error instanceof NewSessionDraftConflictError) {
11440
- throw new HTTPException19(409, {
11504
+ throw new HTTPException20(409, {
11441
11505
  message: error.message,
11442
11506
  cause: error
11443
11507
  });
11444
11508
  }
11445
11509
  if (error instanceof SessionCreateIdempotencyConflictError) {
11446
- throw new HTTPException19(409, { message: error.message, cause: error });
11510
+ throw new HTTPException20(409, { message: error.message, cause: error });
11447
11511
  }
11448
11512
  throw error;
11449
11513
  }
@@ -11472,7 +11536,7 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload, aut
11472
11536
  function prepareSelectedHostTurnAuthority(settings, tools, grant, workspaceId, selections, authorization) {
11473
11537
  if (!selections.length) return void 0;
11474
11538
  if (!authorization || authorization.grant.accountId !== grant.accountId || authorization.grant.subjectId !== grant.subjectId || authorization?.grant.workspaceId !== workspaceId || !hasPermission(authorization?.grant.permissions ?? [], "connections:read") || !hasPermission(grant.permissions, "connections:read") || grant.metadata?.["sessionId"])
11475
- throw new HTTPException19(403, {
11539
+ throw new HTTPException20(403, {
11476
11540
  message: "Host selection requires a verified direct owner"
11477
11541
  });
11478
11542
  const beforeCommit = prepareHostMcpOwnerAuthorization(
@@ -11484,7 +11548,7 @@ function prepareSelectedHostTurnAuthority(settings, tools, grant, workspaceId, s
11484
11548
  selections.map((selection) => {
11485
11549
  const configured = settings.mcpServers.find((server) => server.id === selection.serverId);
11486
11550
  if (!configured || !tools.some((tool) => tool.kind === "mcp" && tool.id === selection.serverId) || configured.connectionRef?.authoritySource !== "host" || !configured.connectionRef.hostBinding || !configured.url)
11487
- throw new HTTPException19(422, {
11551
+ throw new HTTPException20(422, {
11488
11552
  message: "Host delegation must match a selected configured host server"
11489
11553
  });
11490
11554
  assertHostMcpAuthoritySourceAdmissionEnabled(settings, configured.connectionRef);
@@ -11504,7 +11568,7 @@ function prepareSelectedHostTurnAuthority(settings, tools, grant, workspaceId, s
11504
11568
  connectionRef
11505
11569
  });
11506
11570
  if (!delegation || !binding || delegation.status !== "active" || binding.status !== "active" || !definition.success || delegation.generation !== selection.generation || binding.id !== hostBinding.bindingId || binding.generation !== hostBinding.generation || stableJson4(definition.data) !== stableJson4(binding.definition))
11507
- throw new HTTPException19(403, { message: "Host delegation selection changed" });
11571
+ throw new HTTPException20(403, { message: "Host delegation selection changed" });
11508
11572
  try {
11509
11573
  await captureDirectHostMcpAuthority(tx, owner, {
11510
11574
  sessionId,
@@ -11514,9 +11578,9 @@ function prepareSelectedHostTurnAuthority(settings, tools, grant, workspaceId, s
11514
11578
  });
11515
11579
  } catch (error) {
11516
11580
  if (error instanceof HostMcpDelegationAuthorityError2)
11517
- throw new HTTPException19(403, { message: "Host delegation authority unavailable" });
11581
+ throw new HTTPException20(403, { message: "Host delegation authority unavailable" });
11518
11582
  if (error instanceof HostMcpBindingConflictError)
11519
- throw new HTTPException19(409, { message: "Host delegation selection conflicts" });
11583
+ throw new HTTPException20(409, { message: "Host delegation selection conflicts" });
11520
11584
  throw error;
11521
11585
  }
11522
11586
  }
@@ -11556,7 +11620,7 @@ async function acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, ses
11556
11620
  (a, b) => a.serverId < b.serverId ? -1 : a.serverId > b.serverId ? 1 : 0
11557
11621
  );
11558
11622
  if (hostSelections.length && (!input.authorization || input.authorization.grant.accountId !== grant.accountId || input.authorization.grant.subjectId !== grant.subjectId || input.authorization?.grant.workspaceId !== workspaceId || !hasPermission(input.authorization?.grant.permissions ?? [], "connections:read") || !hasPermission(grant.permissions, "connections:read") || grant.metadata?.["sessionId"]))
11559
- throw new HTTPException19(403, {
11623
+ throw new HTTPException20(403, {
11560
11624
  message: "Host selection requires a verified direct owner"
11561
11625
  });
11562
11626
  if (hostSelections.length)
@@ -11661,7 +11725,7 @@ async function acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, ses
11661
11725
  assertSessionAllowsProductModel(existingSession, effectiveModel);
11662
11726
  } catch (error) {
11663
11727
  if (error instanceof CodexCompactionV2ProviderLockedError) {
11664
- throw new HTTPException19(422, { message: error.message, cause: error });
11728
+ throw new HTTPException20(422, { message: error.message, cause: error });
11665
11729
  }
11666
11730
  throw error;
11667
11731
  }
@@ -11684,7 +11748,7 @@ async function acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, ses
11684
11748
  (resource) => !acceptedResources.has(stableJson4(resource))
11685
11749
  );
11686
11750
  if (unacceptedDraftResource) {
11687
- throw new HTTPException19(422, {
11751
+ throw new HTTPException20(422, {
11688
11752
  message: "composer draft resources must be included in the accepted resource set"
11689
11753
  });
11690
11754
  }
@@ -11703,7 +11767,7 @@ async function acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, ses
11703
11767
  model: effectiveModel
11704
11768
  });
11705
11769
  if (requestedResources.some((resource) => resource.kind === "file") && !objectStorage) {
11706
- throw new HTTPException19(503, {
11770
+ throw new HTTPException20(503, {
11707
11771
  message: "object storage is not configured"
11708
11772
  });
11709
11773
  }
@@ -11889,7 +11953,7 @@ async function updateSessionMcpApprovalPolicy(deps, grant, sessionId, serverId,
11889
11953
  async (_session, context) => {
11890
11954
  const result = await context.updateSessionMcpApprovalPolicy(serverId, normalizedPolicy);
11891
11955
  if (!result.server) {
11892
- throw new HTTPException19(404, {
11956
+ throw new HTTPException20(404, {
11893
11957
  message: "session MCP server not found"
11894
11958
  });
11895
11959
  }
@@ -11973,7 +12037,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
11973
12037
  (tool) => !validatedIds.has(`${tool.kind}:${tool.id}`)
11974
12038
  );
11975
12039
  if (unknown) {
11976
- throw new HTTPException19(422, {
12040
+ throw new HTTPException20(422, {
11977
12041
  message: `unknown MCP server id: ${unknown.id}`
11978
12042
  });
11979
12043
  }
@@ -11985,7 +12049,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
11985
12049
  (tool) => !deploymentFirstPartyMcpToolPolicy.allowed.includes(tool)
11986
12050
  );
11987
12051
  if (disallowedFirstPartyMcpTool) {
11988
- throw new HTTPException19(422, {
12052
+ throw new HTTPException20(422, {
11989
12053
  message: `first-party MCP tool is disabled by deployment policy: ${disallowedFirstPartyMcpTool}`
11990
12054
  });
11991
12055
  }
@@ -12018,7 +12082,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
12018
12082
  if (session.parentSessionId) {
12019
12083
  const parent = await context.getLockedSession(session.parentSessionId);
12020
12084
  if (!parent) {
12021
- throw new HTTPException19(409, {
12085
+ throw new HTTPException20(409, {
12022
12086
  message: "parent session is no longer available"
12023
12087
  });
12024
12088
  }
@@ -12040,7 +12104,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
12040
12104
  ].filter((tool) => deploymentAllowedFirstPartyMcpTools.has(tool));
12041
12105
  if (requestedMode === "workspace_default") {
12042
12106
  if (!parentTracksWorkspaceDefaults) {
12043
- throw new HTTPException19(403, {
12107
+ throw new HTTPException20(403, {
12044
12108
  message: "a child may adopt workspace defaults only while its parent tracks workspace defaults"
12045
12109
  });
12046
12110
  }
@@ -12062,7 +12126,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
12062
12126
  (tool) => !parentFirstPartySet.has(tool)
12063
12127
  );
12064
12128
  if (widenedFirstPartyTool) {
12065
- throw new HTTPException19(403, {
12129
+ throw new HTTPException20(403, {
12066
12130
  message: `session OpenGeni tools may only narrow the parent policy: ${widenedFirstPartyTool}`
12067
12131
  });
12068
12132
  }
@@ -12100,7 +12164,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
12100
12164
  (tool) => !currentFirstPartyCeiling.has(tool)
12101
12165
  );
12102
12166
  if (widenedFirstPartyTool) {
12103
- throw new HTTPException19(403, {
12167
+ throw new HTTPException20(403, {
12104
12168
  message: `an agent may only narrow its session OpenGeni tools: ${widenedFirstPartyTool}`
12105
12169
  });
12106
12170
  }
@@ -12167,13 +12231,13 @@ async function readSessionLineage(deps, grant, sessionId) {
12167
12231
  if (authorization?.relatedSessionAccess === "target") {
12168
12232
  const session = await getSession2(deps.db, grant.workspaceId, sessionId);
12169
12233
  if (!session) {
12170
- throw new HTTPException19(404, { message: "session not found" });
12234
+ throw new HTTPException20(404, { message: "session not found" });
12171
12235
  }
12172
12236
  return { ancestors: [], children: [], truncated: false };
12173
12237
  }
12174
12238
  const lineage = await getSessionLineage(deps.db, grant.workspaceId, sessionId);
12175
12239
  if (!lineage) {
12176
- throw new HTTPException19(404, { message: "session not found" });
12240
+ throw new HTTPException20(404, { message: "session not found" });
12177
12241
  }
12178
12242
  return lineage;
12179
12243
  }
@@ -12209,7 +12273,7 @@ function workspaceCustomModelCommitGuard(input) {
12209
12273
  reference
12210
12274
  });
12211
12275
  if (!active) {
12212
- throw new HTTPException20(422, {
12276
+ throw new HTTPException21(422, {
12213
12277
  message: `model is not available: ${input.modelId}`
12214
12278
  });
12215
12279
  }
@@ -12369,7 +12433,7 @@ async function createValidatedScheduledTask(input) {
12369
12433
  async function frozenScheduledTaskCreatorPolicy(input) {
12370
12434
  const session = await getSession3(input.db, input.grant.workspaceId, input.sessionId);
12371
12435
  if (!session) {
12372
- throw new HTTPException20(403, {
12436
+ throw new HTTPException21(403, {
12373
12437
  message: "the calling agent session is not available in this workspace"
12374
12438
  });
12375
12439
  }
@@ -12379,7 +12443,7 @@ async function frozenScheduledTaskCreatorPolicy(input) {
12379
12443
  );
12380
12444
  const firstPartyMcpPermissions = (session.firstPartyMcpPermissions ?? [...DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2]).filter((permission) => hasPermission(input.grant.permissions, permission));
12381
12445
  if (firstPartyMcpPermissions.length === 0) {
12382
- throw new HTTPException20(403, {
12446
+ throw new HTTPException21(403, {
12383
12447
  message: "the calling agent session holds no first-party MCP permission it could delegate to scheduled runs"
12384
12448
  });
12385
12449
  }
@@ -12422,7 +12486,7 @@ async function withScheduledTaskAuthorityWriteErrors(run) {
12422
12486
  } catch (error) {
12423
12487
  if (nestedPostgresSqlState5(error) === "42501") {
12424
12488
  const detail = nestedPostgresMessage(error);
12425
- throw new HTTPException20(409, {
12489
+ throw new HTTPException21(409, {
12426
12490
  message: detail ? `scheduled task authority denied: ${detail}` : "scheduled task authority denied"
12427
12491
  });
12428
12492
  }
@@ -12437,20 +12501,20 @@ async function updateScheduledTaskForApi(db, workspaceId, taskId, update) {
12437
12501
  async function validateScheduledTaskTarget(input) {
12438
12502
  if (input.runMode !== "existing_session") {
12439
12503
  if (input.targetSessionId) {
12440
- throw new HTTPException20(422, {
12504
+ throw new HTTPException21(422, {
12441
12505
  message: "targetSessionId requires runMode=existing_session"
12442
12506
  });
12443
12507
  }
12444
12508
  return null;
12445
12509
  }
12446
12510
  if (!input.targetSessionId) {
12447
- throw new HTTPException20(input.missingTargetStatus ?? 422, {
12511
+ throw new HTTPException21(input.missingTargetStatus ?? 422, {
12448
12512
  message: input.missingTargetStatus === 404 ? "target session not found" : "targetSessionId is required when runMode=existing_session"
12449
12513
  });
12450
12514
  }
12451
12515
  requirePermission(input.grant, "sessions:control");
12452
12516
  if (input.agentConfig.goal) {
12453
- throw new HTTPException20(422, {
12517
+ throw new HTTPException21(422, {
12454
12518
  message: "agentConfig.goal cannot be used with an existing-session target"
12455
12519
  });
12456
12520
  }
@@ -12469,44 +12533,44 @@ async function validateScheduledTaskTarget(input) {
12469
12533
  );
12470
12534
  } catch (error) {
12471
12535
  if (error instanceof SessionAuthorizationDeniedError) {
12472
- throw new HTTPException20(404, { message: "target session not found" });
12536
+ throw new HTTPException21(404, { message: "target session not found" });
12473
12537
  }
12474
12538
  if (error instanceof SessionAuthorizationUnavailableError) {
12475
- throw new HTTPException20(503, { message: "session authorization is unavailable" });
12539
+ throw new HTTPException21(503, { message: "session authorization is unavailable" });
12476
12540
  }
12477
12541
  throw error;
12478
12542
  }
12479
12543
  const session = await getSession3(input.db, input.grant.workspaceId, input.targetSessionId);
12480
12544
  if (!session || session.accountId !== input.grant.accountId) {
12481
- throw new HTTPException20(404, { message: "target session not found" });
12545
+ throw new HTTPException21(404, { message: "target session not found" });
12482
12546
  }
12483
12547
  if (input.agentConfig.bundledSkillIds !== void 0 && !isDeepStrictEqual(input.agentConfig.bundledSkillIds, session.bundledSkillIds)) {
12484
- throw new HTTPException20(422, {
12548
+ throw new HTTPException21(422, {
12485
12549
  message: "An existing-session schedule cannot change that session's bundled Skill selection"
12486
12550
  });
12487
12551
  }
12488
12552
  if (session.status === "cancelled") {
12489
- throw new HTTPException20(409, {
12553
+ throw new HTTPException21(409, {
12490
12554
  message: "target session is cancelled; choose a revivable session"
12491
12555
  });
12492
12556
  }
12493
12557
  if ((session.variableSetId ?? null) !== (input.variableSetId ?? null)) {
12494
- throw new HTTPException20(422, {
12558
+ throw new HTTPException21(422, {
12495
12559
  message: "target session variableSet attachment does not match the scheduled task"
12496
12560
  });
12497
12561
  }
12498
12562
  if ((session.rigId ?? null) !== (input.rigId ?? null)) {
12499
- throw new HTTPException20(422, {
12563
+ throw new HTTPException21(422, {
12500
12564
  message: "target session rig does not match the scheduled task"
12501
12565
  });
12502
12566
  }
12503
12567
  if (input.agentConfig.sandboxBackend !== void 0 && input.agentConfig.sandboxBackend !== session.sandboxBackend) {
12504
- throw new HTTPException20(422, {
12568
+ throw new HTTPException21(422, {
12505
12569
  message: "target session sandbox backend does not match the scheduled task"
12506
12570
  });
12507
12571
  }
12508
12572
  if (scheduledSlackBotConnectionId(session.metadata) !== (input.agentConfig.slackBotConnectionId ?? null)) {
12509
- throw new HTTPException20(422, {
12573
+ throw new HTTPException21(422, {
12510
12574
  message: "target session OpenGeni Slack bot binding does not match the scheduled task"
12511
12575
  });
12512
12576
  }
@@ -12516,19 +12580,19 @@ async function validateScheduledTaskMachineTarget(input) {
12516
12580
  const machineTarget = input.agentConfig.machineTarget;
12517
12581
  if (!machineTarget) {
12518
12582
  if (input.runMode !== "existing_session" && (input.agentConfig.sandboxBackend ?? input.settings.sandboxBackend) === "selfhosted") {
12519
- throw new HTTPException20(422, {
12583
+ throw new HTTPException21(422, {
12520
12584
  message: "self-hosted scheduled tasks require a Connected Machine; select a machine before saving"
12521
12585
  });
12522
12586
  }
12523
12587
  return null;
12524
12588
  }
12525
12589
  if (input.runMode === "existing_session") {
12526
- throw new HTTPException20(422, {
12590
+ throw new HTTPException21(422, {
12527
12591
  message: "machineTarget cannot be used with an existing-session target"
12528
12592
  });
12529
12593
  }
12530
12594
  if (!input.settings.sandboxOwnershipEnabled || !input.settings.sandboxSelfhostedEnabled) {
12531
- throw new HTTPException20(422, {
12595
+ throw new HTTPException21(422, {
12532
12596
  message: "Connected Machines are not enabled for scheduled tasks in this deployment"
12533
12597
  });
12534
12598
  }
@@ -12539,23 +12603,23 @@ async function validateScheduledTaskMachineTarget(input) {
12539
12603
  };
12540
12604
  const sandbox = await getSandbox4(input.db, access, machineTarget.targetSandboxId);
12541
12605
  if (!sandbox || sandbox.kind !== "selfhosted" || !sandbox.enrollmentId) {
12542
- throw new HTTPException20(422, {
12606
+ throw new HTTPException21(422, {
12543
12607
  message: "the selected Connected Machine is unavailable"
12544
12608
  });
12545
12609
  }
12546
12610
  if (sandbox.scope === "user") {
12547
- throw new HTTPException20(422, {
12611
+ throw new HTTPException21(422, {
12548
12612
  message: "personal Connected Machines cannot run unattended schedules; select a workspace or organization machine"
12549
12613
  });
12550
12614
  }
12551
12615
  const enrollment = input.requireOnline ? await getLiveEnrollmentConnection3(input.db, access, sandbox.enrollmentId) : await getEnrollment3(input.db, access, sandbox.enrollmentId);
12552
12616
  if (!enrollment || enrollment.status !== "active") {
12553
- throw new HTTPException20(422, {
12617
+ throw new HTTPException21(422, {
12554
12618
  message: input.requireOnline ? "the selected Connected Machine is offline" : "the selected Connected Machine is unavailable"
12555
12619
  });
12556
12620
  }
12557
12621
  if (input.requireOnline && !enrollment.workspaceRoot) {
12558
- throw new HTTPException20(422, {
12622
+ throw new HTTPException21(422, {
12559
12623
  message: "the selected Connected Machine has not reported a workspace root; reconnect it with a current agent"
12560
12624
  });
12561
12625
  }
@@ -12589,25 +12653,25 @@ function scheduledTaskAuthorityUpdateForGrant(grant) {
12589
12653
  async function requireScheduledTaskRig(db, access, rigId) {
12590
12654
  const rig = await getRig5(db, access, rigId);
12591
12655
  if (!rig) {
12592
- throw new HTTPException20(422, { message: `unknown rigId: ${rigId}` });
12656
+ throw new HTTPException21(422, { message: `unknown rigId: ${rigId}` });
12593
12657
  }
12594
12658
  }
12595
12659
  async function validatedScheduledTaskUpdate(input) {
12596
12660
  const update = {};
12597
12661
  const existingKnowledge = input.existing.action.kind === "knowledge_source_sync";
12598
12662
  if (input.payload.action && input.payload.action.kind !== input.existing.action.kind) {
12599
- throw new HTTPException20(409, {
12663
+ throw new HTTPException21(409, {
12600
12664
  message: "scheduled task action kind is immutable; create a new schedule"
12601
12665
  });
12602
12666
  }
12603
12667
  if (existingKnowledge) {
12604
12668
  if (input.payload.agentConfig !== void 0 || input.payload.runMode !== void 0 || input.payload.targetSessionId !== void 0 || input.payload.variableSetId !== void 0 || input.payload.rigId !== void 0 || input.payload.connectionAuthorities !== void 0 || input.payload.selectedHostMcpDelegations !== void 0) {
12605
- throw new HTTPException20(422, {
12669
+ throw new HTTPException21(422, {
12606
12670
  message: "knowledge source schedules do not accept agent/session configuration"
12607
12671
  });
12608
12672
  }
12609
12673
  if (input.payload.overlapPolicy === "allow_concurrent") {
12610
- throw new HTTPException20(422, {
12674
+ throw new HTTPException21(422, {
12611
12675
  message: "knowledge source schedules require skip or buffer_one overlap"
12612
12676
  });
12613
12677
  }
@@ -12636,7 +12700,7 @@ async function validatedScheduledTaskUpdate(input) {
12636
12700
  const nextRunMode = input.payload.runMode ?? input.existing.runMode;
12637
12701
  const nextTargetSessionId = input.payload.targetSessionId !== void 0 ? input.payload.targetSessionId : nextRunMode === "existing_session" ? existingTarget : null;
12638
12702
  if (input.existing.runMode === "reusable_session" && input.existing.reusableSessionId && nextRunMode === "existing_session") {
12639
- throw new HTTPException20(409, {
12703
+ throw new HTTPException21(409, {
12640
12704
  message: "cannot target an existing session after this task created its reusable session; create a new task"
12641
12705
  });
12642
12706
  }
@@ -12662,7 +12726,7 @@ async function validatedScheduledTaskUpdate(input) {
12662
12726
  if (input.payload.variableSetId !== void 0) {
12663
12727
  const nextVariableSetId = input.payload.variableSetId;
12664
12728
  if ((input.existing.variableSetId ?? null) !== (nextVariableSetId ?? null) && input.existing.runMode === "reusable_session" && input.existing.reusableSessionId) {
12665
- throw new HTTPException20(409, {
12729
+ throw new HTTPException21(409, {
12666
12730
  message: "cannot change variableSet of a task with a live reusable session; recreate the task"
12667
12731
  });
12668
12732
  }
@@ -12683,7 +12747,7 @@ async function validatedScheduledTaskUpdate(input) {
12683
12747
  }
12684
12748
  if (input.payload.rigId !== void 0) {
12685
12749
  if (input.existing.runMode === "reusable_session" && input.existing.reusableSessionId !== null && input.payload.rigId !== input.existing.rigId) {
12686
- throw new HTTPException20(409, {
12750
+ throw new HTTPException21(409, {
12687
12751
  message: "A reusable-session task cannot change rigId after materialization; recreate it"
12688
12752
  });
12689
12753
  }
@@ -12715,7 +12779,7 @@ async function validatedScheduledTaskUpdate(input) {
12715
12779
  ...input.toolsProvided !== void 0 ? { toolsProvided: input.toolsProvided } : {}
12716
12780
  });
12717
12781
  if (input.existing.reusableSessionId && input.existing.runMode === "reusable_session" && (input.existing.agentConfig.slackBotConnectionId ?? null) !== (nextAgentConfig2.slackBotConnectionId ?? null)) {
12718
- throw new HTTPException20(409, {
12782
+ throw new HTTPException21(409, {
12719
12783
  message: "cannot change the OpenGeni Slack bot connection of a task with a live reusable session; recreate the task"
12720
12784
  });
12721
12785
  }
@@ -12739,7 +12803,7 @@ async function validatedScheduledTaskUpdate(input) {
12739
12803
  input.existing.id
12740
12804
  );
12741
12805
  if (existingXaiAuthority.scope === "user" && materialExecutionChange && (input.existing.createdBy.kind !== "subject" || input.existing.createdBy.subjectId !== input.grant.subjectId)) {
12742
- throw new HTTPException20(409, {
12806
+ throw new HTTPException21(409, {
12743
12807
  message: "changing a user-scoped xAI scheduled task requires the same causal human"
12744
12808
  });
12745
12809
  }
@@ -12750,12 +12814,12 @@ async function validatedScheduledTaskUpdate(input) {
12750
12814
  );
12751
12815
  if (input.payload.connectionAuthorities === void 0) {
12752
12816
  if (materialExecutionChange && existingDelegations.some((delegation) => delegation.connectionType === "github_personal")) {
12753
- throw new HTTPException20(409, {
12817
+ throw new HTTPException21(409, {
12754
12818
  message: "material changes to a personal GitHub-authorized task require explicit connectionAuthorities"
12755
12819
  });
12756
12820
  }
12757
12821
  if (existingDelegations.length > 0 && !isDeepStrictEqual(nextAgentConfig.tools, input.existing.agentConfig.tools)) {
12758
- throw new HTTPException20(409, {
12822
+ throw new HTTPException21(409, {
12759
12823
  message: "changing tools on a connection-authorized task requires explicit connectionAuthorities"
12760
12824
  });
12761
12825
  }
@@ -12763,7 +12827,7 @@ async function validatedScheduledTaskUpdate(input) {
12763
12827
  if (materialExecutionChange && existingDelegations.some(
12764
12828
  (delegation) => delegation.ownerSubjectId !== input.grant.subjectId
12765
12829
  )) {
12766
- throw new HTTPException20(409, {
12830
+ throw new HTTPException21(409, {
12767
12831
  message: "preserving connection authority requires the same causal human; provide a new explicit selection"
12768
12832
  });
12769
12833
  }
@@ -12775,7 +12839,7 @@ async function validatedScheduledTaskUpdate(input) {
12775
12839
  }
12776
12840
  } else if (input.payload.connectionAuthorities.length === 0) {
12777
12841
  if (personalGitHubRepositoryResources(nextAgentConfig.resources).length > 0) {
12778
- throw new HTTPException20(409, {
12842
+ throw new HTTPException21(409, {
12779
12843
  message: "personal GitHub repository resources cannot be retained without connectionAuthorities"
12780
12844
  });
12781
12845
  }
@@ -12885,7 +12949,7 @@ async function validatedScheduledTaskUpdate(input) {
12885
12949
  async function requireScheduledTaskForApi(db, workspaceId, taskId) {
12886
12950
  const task = await getScheduledTask(db, workspaceId, taskId);
12887
12951
  if (!task) {
12888
- throw new HTTPException20(404, { message: "scheduled task not found" });
12952
+ throw new HTTPException21(404, { message: "scheduled task not found" });
12889
12953
  }
12890
12954
  return task;
12891
12955
  }
@@ -12919,12 +12983,12 @@ async function restoreScheduledTask(db, previous) {
12919
12983
  }
12920
12984
  async function validateKnowledgeSourceSyncAction(input) {
12921
12985
  if (input.action.initiatingSubjectId !== input.grant.subjectId) {
12922
- throw new HTTPException20(403, {
12986
+ throw new HTTPException21(403, {
12923
12987
  message: "knowledge source sync must preserve the exact initiating subject"
12924
12988
  });
12925
12989
  }
12926
12990
  if (input.action.connection.ownerSubjectId !== input.grant.subjectId) {
12927
- throw new HTTPException20(403, {
12991
+ throw new HTTPException21(403, {
12928
12992
  message: "knowledge source connection must belong to the initiating subject"
12929
12993
  });
12930
12994
  }
@@ -12935,10 +12999,10 @@ async function validateKnowledgeSourceSyncAction(input) {
12935
12999
  initiatingSubjectId: input.grant.subjectId
12936
13000
  });
12937
13001
  if (!resolved || resolved.source.lifecycleState !== "active") {
12938
- throw new HTTPException20(404, { message: "knowledge source not found" });
13002
+ throw new HTTPException21(404, { message: "knowledge source not found" });
12939
13003
  }
12940
13004
  if (resolved.source.syncGeneration !== input.action.sourceGeneration || resolved.source.lifecycleGeneration !== input.action.sourceLifecycleGeneration || scopedKnowledgeScopeKey(resolved.source.scope) !== scopedKnowledgeScopeKey(input.action.destination)) {
12941
- throw new HTTPException20(409, {
13005
+ throw new HTTPException21(409, {
12942
13006
  message: "knowledge source authority or generation changed"
12943
13007
  });
12944
13008
  }
@@ -12949,7 +13013,7 @@ async function validateKnowledgeSourceSyncAction(input) {
12949
13013
  input.grant.subjectId
12950
13014
  );
12951
13015
  if (!connection || connection.accountId !== input.grant.accountId || connection.workspaceId !== input.grant.workspaceId || connection.subjectId !== input.action.connection.ownerSubjectId || connection.version !== input.action.connection.connectionVersion || connection.providerDomain.toLowerCase() !== input.action.connection.providerDomain.toLowerCase() || connection.kind !== input.action.connection.kind || connection.status !== "active") {
12952
- throw new HTTPException20(409, {
13016
+ throw new HTTPException21(409, {
12953
13017
  message: "knowledge source connection authority changed or requires reconnect"
12954
13018
  });
12955
13019
  }
@@ -12973,10 +13037,10 @@ async function deleteScheduledTaskLifecycle(input) {
12973
13037
  input.workspaceId,
12974
13038
  input.taskId
12975
13039
  );
12976
- if (!task2) throw new HTTPException20(404, { message: "Scheduled task not found" });
13040
+ if (!task2) throw new HTTPException21(404, { message: "Scheduled task not found" });
12977
13041
  const wasLive = task2.deletedAt === null;
12978
13042
  if (task2.action.kind === "knowledge_source_sync" && (task2.action.initiatingSubjectId !== input.subjectId || task2.action.connection.ownerSubjectId !== input.subjectId)) {
12979
- throw new HTTPException20(403, {
13043
+ throw new HTTPException21(403, {
12980
13044
  message: "knowledge source schedule requires the exact initiating subject"
12981
13045
  });
12982
13046
  }
@@ -13049,7 +13113,7 @@ async function validateScheduledTaskAgentConfig(input) {
13049
13113
  const actor = creationInitiatorForGrant(input.grant).actor;
13050
13114
  const parent = actor ? await getSession3(input.db, input.workspaceId, actor.sessionId) : null;
13051
13115
  if (actor && (!parent || parent.accountId !== input.grant.accountId)) {
13052
- throw new HTTPException20(403, {
13116
+ throw new HTTPException21(403, {
13053
13117
  message: "Scheduled Skill selection requires the creating agent's session"
13054
13118
  });
13055
13119
  }
@@ -13060,7 +13124,7 @@ async function validateScheduledTaskAgentConfig(input) {
13060
13124
  parent?.bundledSkillIds
13061
13125
  );
13062
13126
  } catch (error) {
13063
- throw new HTTPException20(422, {
13127
+ throw new HTTPException21(422, {
13064
13128
  message: error instanceof Error ? error.message : "Invalid bundled Skill selection"
13065
13129
  });
13066
13130
  }
@@ -13084,16 +13148,16 @@ async function validateScheduledTaskAgentConfig(input) {
13084
13148
  );
13085
13149
  const prompt = input.payload.agentConfig.prompt.trim();
13086
13150
  if (!prompt) {
13087
- throw new HTTPException20(422, { message: "scheduled task prompt is required" });
13151
+ throw new HTTPException21(422, { message: "scheduled task prompt is required" });
13088
13152
  }
13089
13153
  if (hasReservedOpenGeniSlackBotSessionMetadata(input.payload.agentConfig.metadata)) {
13090
- throw new HTTPException20(422, {
13154
+ throw new HTTPException21(422, {
13091
13155
  message: `${OPENGENI_SLACK_BOT_SESSION_METADATA_KEY3} is reserved for scheduler routing`
13092
13156
  });
13093
13157
  }
13094
13158
  await validateGitHubRepositorySelection(input.db, input.workspaceId, resources);
13095
13159
  if (resources.some((resource) => resource.kind === "file") && !input.objectStorage) {
13096
- throw new HTTPException20(503, { message: "object storage is not configured" });
13160
+ throw new HTTPException21(503, { message: "object storage is not configured" });
13097
13161
  }
13098
13162
  await validateFileResources(
13099
13163
  input.db,
@@ -13116,7 +13180,7 @@ async function validateScheduledTaskAgentConfig(input) {
13116
13180
  const deploymentPolicy = await getNestedAgentDepthDeploymentPolicy(input.db);
13117
13181
  const inheritedMaxDepth = typeof workspaceMaxDepth === "number" ? workspaceMaxDepth : deploymentPolicy.maxNestedAgentDepth;
13118
13182
  if (requestedMaxDepth > inheritedMaxDepth && !hasPermission(input.grant.permissions, "workspace:admin")) {
13119
- throw new HTTPException20(403, {
13183
+ throw new HTTPException21(403, {
13120
13184
  message: `scheduled task maxNestedAgentDepth ${requestedMaxDepth} exceeds inherited limit ${inheritedMaxDepth}; workspace:admin is required to increase it`
13121
13185
  });
13122
13186
  }
@@ -13137,13 +13201,13 @@ function validateIncidentTelemetryPreflightSelection(settings, agentConfig) {
13137
13201
  const preflight = agentConfig.incidentTelemetryPreflight;
13138
13202
  if (executionClass === void 0 && preflight === void 0) return;
13139
13203
  if (executionClass !== "incident_telemetry" || !preflight) {
13140
- throw new HTTPException20(422, {
13204
+ throw new HTTPException21(422, {
13141
13205
  message: "executionClass=incident_telemetry and incidentTelemetryPreflight must be configured together"
13142
13206
  });
13143
13207
  }
13144
13208
  for (const required of preflight.requiredResources) {
13145
13209
  if (!agentConfig.resources.some((selected) => isDeepStrictEqual(selected, required))) {
13146
- throw new HTTPException20(422, {
13210
+ throw new HTTPException21(422, {
13147
13211
  message: "incidentTelemetryPreflight.requiredResources must be exact selected resources"
13148
13212
  });
13149
13213
  }
@@ -13151,13 +13215,13 @@ function validateIncidentTelemetryPreflightSelection(settings, agentConfig) {
13151
13215
  const selectedMcpServerIds = new Set(agentConfig.tools.map((tool) => tool.id));
13152
13216
  selectedMcpServerIds.add("opengeni");
13153
13217
  if (preflight.requiredMcpServerIds.some((id) => !selectedMcpServerIds.has(id))) {
13154
- throw new HTTPException20(422, {
13218
+ throw new HTTPException21(422, {
13155
13219
  message: "incidentTelemetryPreflight.requiredMcpServerIds must be exact selected MCP servers"
13156
13220
  });
13157
13221
  }
13158
13222
  const selectedFirstPartyTools = new Set(resolveFirstPartyMcpToolPolicy2(settings).default);
13159
13223
  if (preflight.requiredFirstPartyMcpTools.some((tool) => !selectedFirstPartyTools.has(tool))) {
13160
- throw new HTTPException20(422, {
13224
+ throw new HTTPException21(422, {
13161
13225
  message: "incidentTelemetryPreflight.requiredFirstPartyMcpTools must be present in the selected first-party tool policy"
13162
13226
  });
13163
13227
  }
@@ -13165,18 +13229,18 @@ function validateIncidentTelemetryPreflightSelection(settings, agentConfig) {
13165
13229
  if ((preflight.requiredFirstPartyMcpPermissions ?? []).some(
13166
13230
  (permission) => !selectedFirstPartyPermissions.has(permission)
13167
13231
  )) {
13168
- throw new HTTPException20(422, {
13232
+ throw new HTTPException21(422, {
13169
13233
  message: "incidentTelemetryPreflight.requiredFirstPartyMcpPermissions must be present in the scheduled responder permission policy"
13170
13234
  });
13171
13235
  }
13172
13236
  const route = preflight.dataSource.route;
13173
13237
  if (route.kind === "mcp" && !selectedMcpServerIds.has(route.serverId)) {
13174
- throw new HTTPException20(422, {
13238
+ throw new HTTPException21(422, {
13175
13239
  message: "incidentTelemetryPreflight.dataSource.route must use a selected MCP server"
13176
13240
  });
13177
13241
  }
13178
13242
  if (route.kind === "first_party" && !selectedFirstPartyTools.has(route.tool)) {
13179
- throw new HTTPException20(422, {
13243
+ throw new HTTPException21(422, {
13180
13244
  message: "incidentTelemetryPreflight.dataSource.route must use a selected first-party tool"
13181
13245
  });
13182
13246
  }
@@ -13184,14 +13248,14 @@ function validateIncidentTelemetryPreflightSelection(settings, agentConfig) {
13184
13248
  const declaredSets = new Set(preflight.requiredVariableSetNames);
13185
13249
  const declaredVariables = new Set(preflight.requiredVariableNames);
13186
13250
  if (!declaredSets.has(route.variableSetName) || route.variableNames.some((name) => !declaredVariables.has(name))) {
13187
- throw new HTTPException20(422, {
13251
+ throw new HTTPException21(422, {
13188
13252
  message: "incidentTelemetryPreflight.dataSource.route variable metadata must be declared as required"
13189
13253
  });
13190
13254
  }
13191
13255
  }
13192
13256
  if (route.kind === "rig_credential_hook") {
13193
13257
  if (!preflight.requiredRig?.credentialHookIds.includes(route.credentialHookId)) {
13194
- throw new HTTPException20(422, {
13258
+ throw new HTTPException21(422, {
13195
13259
  message: "incidentTelemetryPreflight.dataSource.route rig hook must be declared as required"
13196
13260
  });
13197
13261
  }
@@ -13202,13 +13266,13 @@ function validateScheduledTaskSchedule(schedule) {
13202
13266
  return;
13203
13267
  }
13204
13268
  if (new Date(schedule.startAt).getTime() >= new Date(schedule.endAt).getTime()) {
13205
- throw new HTTPException20(422, { message: "interval schedule endAt must be after startAt" });
13269
+ throw new HTTPException21(422, { message: "interval schedule endAt must be after startAt" });
13206
13270
  }
13207
13271
  }
13208
13272
  function trimmedScheduledTaskName(name) {
13209
13273
  const trimmed = name.trim();
13210
13274
  if (!trimmed) {
13211
- throw new HTTPException20(422, { message: "scheduled task name is required" });
13275
+ throw new HTTPException21(422, { message: "scheduled task name is required" });
13212
13276
  }
13213
13277
  return trimmed;
13214
13278
  }
@@ -15818,7 +15882,7 @@ function providerReceiptWire(receipt2) {
15818
15882
  }
15819
15883
 
15820
15884
  // src/domain/workspace-members.ts
15821
- import { HTTPException as HTTPException21 } from "hono/http-exception";
15885
+ import { HTTPException as HTTPException22 } from "hono/http-exception";
15822
15886
  var MEMBER_ADMIN_PERMISSIONS = ["workspace:admin", "members:manage"];
15823
15887
  function memberCanAdminister(member) {
15824
15888
  return member.permissions.some((permission) => MEMBER_ADMIN_PERMISSIONS.includes(permission));
@@ -15828,27 +15892,27 @@ function isUserMember(member) {
15828
15892
  }
15829
15893
  function resolveMemberSubjectId(userId) {
15830
15894
  if (!userId) {
15831
- throw new HTTPException21(404, { message: "user is not registered" });
15895
+ throw new HTTPException22(404, { message: "user is not registered" });
15832
15896
  }
15833
15897
  return `user:${userId}`;
15834
15898
  }
15835
15899
  function assertWorkspaceMemberRemovable(input) {
15836
15900
  const { members, subjectId, callerSubjectId } = input;
15837
15901
  if (subjectId === callerSubjectId) {
15838
- throw new HTTPException21(409, {
15902
+ throw new HTTPException22(409, {
15839
15903
  message: "you cannot remove your own membership"
15840
15904
  });
15841
15905
  }
15842
15906
  const target = members.find((member) => member.subjectId === subjectId);
15843
15907
  if (!target) {
15844
- throw new HTTPException21(404, { message: "member not found" });
15908
+ throw new HTTPException22(404, { message: "member not found" });
15845
15909
  }
15846
15910
  if (memberCanAdminister(target)) {
15847
15911
  const remainingAdmins = members.filter(
15848
15912
  (member) => member.subjectId !== subjectId && memberCanAdminister(member)
15849
15913
  );
15850
15914
  if (remainingAdmins.length === 0) {
15851
- throw new HTTPException21(409, {
15915
+ throw new HTTPException22(409, {
15852
15916
  message: "cannot remove the last member who can manage this workspace"
15853
15917
  });
15854
15918
  }
@@ -15857,28 +15921,28 @@ function assertWorkspaceMemberRemovable(input) {
15857
15921
  function assertWorkspaceMemberUpdateAllowed(input) {
15858
15922
  const { members, subjectId, callerSubjectId, nextPermissions } = input;
15859
15923
  if (subjectId === callerSubjectId) {
15860
- throw new HTTPException21(409, {
15924
+ throw new HTTPException22(409, {
15861
15925
  message: "you cannot change your own workspace access"
15862
15926
  });
15863
15927
  }
15864
15928
  const target = members.find((member) => member.subjectId === subjectId);
15865
15929
  if (!target) {
15866
- throw new HTTPException21(404, { message: "member not found" });
15930
+ throw new HTTPException22(404, { message: "member not found" });
15867
15931
  }
15868
15932
  if (memberCanAdminister(target) && !memberCanAdminister({ permissions: nextPermissions }) && !members.some((member) => member.subjectId !== subjectId && memberCanAdminister(member))) {
15869
- throw new HTTPException21(409, {
15933
+ throw new HTTPException22(409, {
15870
15934
  message: "the workspace must keep at least one administrator"
15871
15935
  });
15872
15936
  }
15873
15937
  }
15874
15938
  function assertWorkspaceDeletable(input) {
15875
15939
  if (input.workspaceCountForAccount <= 1) {
15876
- throw new HTTPException21(409, {
15940
+ throw new HTTPException22(409, {
15877
15941
  message: "cannot delete the account's only workspace"
15878
15942
  });
15879
15943
  }
15880
15944
  if (input.activeSessionCount > 0) {
15881
- throw new HTTPException21(409, {
15945
+ throw new HTTPException22(409, {
15882
15946
  message: "stop the workspace's running sessions before deleting it"
15883
15947
  });
15884
15948
  }
@@ -16146,7 +16210,7 @@ import {
16146
16210
  saveNewSessionDraftInTransaction,
16147
16211
  withWorkspaceSubjectRls as withWorkspaceSubjectRls6
16148
16212
  } from "@opengeni/db";
16149
- import { HTTPException as HTTPException22 } from "hono/http-exception";
16213
+ import { HTTPException as HTTPException23 } from "hono/http-exception";
16150
16214
  function hasOwn(value, key) {
16151
16215
  return typeof value === "object" && value !== null && Object.hasOwn(value, key);
16152
16216
  }
@@ -16298,7 +16362,7 @@ async function saveActorNewSessionDraft(deps, grant, workspaceId, rawInput, cano
16298
16362
  const tools = toolsProvided ? validateToolRefs(input.tools, runtimeSettings) : [];
16299
16363
  await validateGitHubRepositorySelection(deps.db, workspaceId, resources);
16300
16364
  if (resources.some((resource) => resource.kind === "file") && !deps.objectStorage) {
16301
- throw new HTTPException22(503, {
16365
+ throw new HTTPException23(503, {
16302
16366
  message: "object storage is not configured"
16303
16367
  });
16304
16368
  }
@@ -16341,7 +16405,7 @@ async function saveActorNewSessionDraft(deps, grant, workspaceId, rawInput, cano
16341
16405
  return mapNewSessionDraft(saved);
16342
16406
  } catch (error) {
16343
16407
  if (error instanceof NewSessionDraftAccessError) {
16344
- throw new HTTPException22(403, { message: error.message });
16408
+ throw new HTTPException23(403, { message: error.message });
16345
16409
  }
16346
16410
  throw error;
16347
16411
  }