@taiga-ai/mcp-server 0.36.0-rc.778 → 0.36.0-rc.892

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1181 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -505,6 +505,7 @@ function hide(inst, key, value) {
505
505
  return own$5(inst, key, value, false);
506
506
  }
507
507
  /** Adds members a table derives from the instance: each builds on first read and shadows as own data, and assignment shadows the same way, as when these were own properties. */
508
+ /* @__NO_SIDE_EFFECTS__ */
508
509
  function derived(computes, table) {
509
510
  for (const key in computes) {
510
511
  const compute = computes[key];
@@ -658,6 +659,7 @@ function newError(Definition) {
658
659
  }
659
660
  return new Definition();
660
661
  }
662
+ /* @__NO_SIDE_EFFECTS__ */
661
663
  function $constructor(name, initializer, proto, params) {
662
664
  const zodProto = {};
663
665
  function Internals(def) {
@@ -788,8 +790,7 @@ const initializer$1 = (inst, def) => {
788
790
  });
789
791
  }
790
792
  };
791
- const $ZodError = $constructor("$ZodError", initializer$1);
792
- $constructor("$ZodError", initializer$1, void 0, { Parent: Error });
793
+ const $ZodError = /* @__PURE__ */ $constructor("$ZodError", initializer$1);
793
794
  /** Get-or-create `obj[key]` as an own data property. A path segment naming an inherited member
794
795
  * ("toString", "constructor") would otherwise read through to the prototype, and assigning
795
796
  * "__proto__" would hit the setter instead of creating a key. */
@@ -1078,7 +1079,7 @@ const guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9
1078
1079
  /** Returns a regex for validating an RFC 9562/4122 UUID.
1079
1080
  *
1080
1081
  * @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */
1081
- const uuid = (version) => {
1082
+ const uuid$1 = (version) => {
1082
1083
  if (!version) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
1083
1084
  return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
1084
1085
  };
@@ -1618,8 +1619,8 @@ const $ZodUUID = /*@__PURE__*/ $constructor("$ZodUUID", (inst, def) => {
1618
1619
  v8: 8
1619
1620
  }[def.version];
1620
1621
  if (v === void 0) throw new Error(`Invalid UUID version: "${def.version}"`);
1621
- def.pattern ?? (def.pattern = uuid(v));
1622
- } else def.pattern ?? (def.pattern = uuid());
1622
+ def.pattern ?? (def.pattern = uuid$1(v));
1623
+ } else def.pattern ?? (def.pattern = uuid$1());
1623
1624
  $ZodStringFormat.init(inst, def);
1624
1625
  });
1625
1626
  const $ZodEmail = /*@__PURE__*/ $constructor("$ZodEmail", (inst, def) => {
@@ -5675,6 +5676,14 @@ function object(shape, params) {
5675
5676
  };
5676
5677
  return new ZodObject(def);
5677
5678
  }
5679
+ function strictObject(shape, params) {
5680
+ return new ZodObject({
5681
+ type: "object",
5682
+ shape,
5683
+ catchall: never(),
5684
+ ...normalizeParams(params)
5685
+ });
5686
+ }
5678
5687
  function looseObject(shape, params) {
5679
5688
  return new ZodObject({
5680
5689
  type: "object",
@@ -7941,7 +7950,23 @@ var SdkError = class extends Error {
7941
7950
  stampErrorBrands(this, new.target);
7942
7951
  }
7943
7952
  };
7944
- (class extends SdkError {
7953
+ /**
7954
+ * An {@linkcode SdkError} subclass for HTTP transport failures.
7955
+ *
7956
+ * Thrown by the streamable HTTP transport when the server responds with a
7957
+ * non-OK status code. Narrows {@linkcode SdkError.data | data} to
7958
+ * {@linkcode SdkHttpErrorData} so consumers can inspect the HTTP status
7959
+ * without unsafe casting.
7960
+ *
7961
+ * @example
7962
+ * ```ts source="./sdkErrors.examples.ts#SdkHttpError_basicUsage"
7963
+ * if (error instanceof SdkHttpError) {
7964
+ * console.log(error.status); // number
7965
+ * console.log(error.statusText); // string | undefined
7966
+ * }
7967
+ * ```
7968
+ */
7969
+ var SdkHttpError = class extends SdkError {
7945
7970
  static {
7946
7971
  Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkHttpError" });
7947
7972
  }
@@ -7958,7 +7983,7 @@ var SdkError = class extends Error {
7958
7983
  get statusText() {
7959
7984
  return this.data.statusText;
7960
7985
  }
7961
- });
7986
+ };
7962
7987
  function isPlainObject$7(value) {
7963
7988
  return value !== null && typeof value === "object" && !Array.isArray(value);
7964
7989
  }
@@ -23874,6 +23899,18 @@ var TaigaClient = class {
23874
23899
  async getLicenseSummary(productId) {
23875
23900
  return this.request("GET", `/products/${seg(productId)}/maintenance/licenses`);
23876
23901
  }
23902
+ /** One licence across a product's inventory (PRO-3251): what it asks, when to allow it, what brings it in. */
23903
+ async getProductLicense(productId, license) {
23904
+ return this.request("GET", `/products/${seg(productId)}/maintenance/licenses/detail?license=${encodeURIComponent(license)}`);
23905
+ }
23906
+ /** Allow a licence in one product: every licence finding in force for it there ignored (PRO-3727). */
23907
+ async allowProductLicense(productId, body) {
23908
+ return this.request("POST", `/products/${seg(productId)}/maintenance/licenses/ignore`, body);
23909
+ }
23910
+ /** Allow a licence in one product no longer: every one of its ignored licence findings there open again. */
23911
+ async reopenProductLicense(productId, license) {
23912
+ return this.request("POST", `/products/${seg(productId)}/maintenance/licenses/reopen`, { license });
23913
+ }
23877
23914
  /** The organization's approved list and every licence in use across its products (PRO-3311). */
23878
23915
  async getOrganizationLicenses() {
23879
23916
  return this.request("GET", "/tenants/current/licenses");
@@ -23901,6 +23938,10 @@ var TaigaClient = class {
23901
23938
  async updateFinding(id, body) {
23902
23939
  return this.request("PATCH", `/maintenance/findings/${seg(id)}`, body);
23903
23940
  }
23941
+ /** Ship notices (PRO-4165): raise the initiative that builds the product's open-source notices, or get the open one. */
23942
+ async shipNotices(productId) {
23943
+ return this.request("POST", `/products/${seg(productId)}/maintenance/notices`, {});
23944
+ }
23904
23945
  async remediateFinding(productId, findingId) {
23905
23946
  return this.request("POST", `/products/${seg(productId)}/maintenance/findings/${seg(findingId)}/remediate`, {});
23906
23947
  }
@@ -24040,7 +24081,7 @@ var TaigaClient = class {
24040
24081
  async refreshRealityDocuments(productId, docTypes) {
24041
24082
  return this.request("POST", `/products/${seg(productId)}/documents/refresh-reality`, docTypes ? { docTypes } : void 0);
24042
24083
  }
24043
- /** What still blocks finishing setup: required documents unpublished, required checks unstamped (PRO-2800). */
24084
+ /** What still blocks finishing setup: the required documents unpublished (PRO-2800). */
24044
24085
  async getProductSetupBlockers(productId) {
24045
24086
  return this.request("GET", `/products/${seg(productId)}/setup`);
24046
24087
  }
@@ -24089,6 +24130,25 @@ var TaigaClient = class {
24089
24130
  }
24090
24131
  };
24091
24132
  //#endregion
24133
+ //#region ../../packages/types/dist/clouds.js
24134
+ /**
24135
+ * Cloud provider platforms that Taiga can connect to.
24136
+ */
24137
+ const CLOUD_PROVIDERS = [
24138
+ "aws",
24139
+ "gcp",
24140
+ "azure",
24141
+ "upcloud",
24142
+ "kubernetes",
24143
+ "digitalocean",
24144
+ "vercel",
24145
+ "railway",
24146
+ "render",
24147
+ "fly",
24148
+ "on_prem",
24149
+ "other"
24150
+ ];
24151
+ //#endregion
24092
24152
  //#region ../../packages/types/dist/lenient-schema.js
24093
24153
  /**
24094
24154
  * Building blocks for reading JSON that arrives untyped -- a stored jsonb
@@ -24150,6 +24210,14 @@ const optionalUuid = string$2().uuid().optional();
24150
24210
  */
24151
24211
  const optionalLineAnchor = number$1().int().min(0).optional().transform((v) => v ? v : void 0);
24152
24212
  //#endregion
24213
+ //#region ../../packages/types/dist/entitlements.js
24214
+ /**
24215
+ * Which agent implementation serves a planning job or build run (PRO-1331).
24216
+ * Recorded on `agent_jobs.runtime` / `build_runs.runtime` at dispatch so
24217
+ * outcomes can be compared per runtime and the UI can badge runs.
24218
+ */
24219
+ const AGENT_RUNTIMES = ["legacy", "engineer"];
24220
+ //#endregion
24153
24221
  //#region ../../packages/types/dist/tenants.js
24154
24222
  const TENANT_DOCUMENT_TYPES = [
24155
24223
  "policy_security",
@@ -24350,21 +24418,58 @@ const EU_COUNTRIES = [
24350
24418
  }
24351
24419
  ];
24352
24420
  /**
24353
- * EEA countries outside the EU. Norway only: Iceland and Liechtenstein have
24354
- * no overlay and no customer asking for one.
24421
+ * EEA countries outside the EU, all three, so {@link COUNTRIES} covers the
24422
+ * whole EU and EEA (PRO-3791). Only Norway has a country layer; Iceland and
24423
+ * Liechtenstein get the `eea` region layer alone, and neither is offered as a
24424
+ * Taiga tenant country ({@link offeredCountries}).
24425
+ */
24426
+ const EEA_COUNTRIES = [
24427
+ {
24428
+ code: "NO",
24429
+ name: "Norway"
24430
+ },
24431
+ {
24432
+ code: "IS",
24433
+ name: "Iceland"
24434
+ },
24435
+ {
24436
+ code: "LI",
24437
+ name: "Liechtenstein"
24438
+ }
24439
+ ];
24440
+ /**
24441
+ * The United Kingdom (PRO-3420): ISO code `GB`, neither EU nor EEA, in the
24442
+ * `uk` region. Offered only while the `regulations` flag is on.
24355
24443
  */
24356
- const EEA_COUNTRIES = [{
24357
- code: "NO",
24358
- name: "Norway"
24444
+ const UK_COUNTRIES = [{
24445
+ code: "GB",
24446
+ name: "United Kingdom"
24359
24447
  }];
24360
- [...EU_COUNTRIES, ...EEA_COUNTRIES];
24448
+ [
24449
+ ...EU_COUNTRIES,
24450
+ ...EEA_COUNTRIES,
24451
+ ...UK_COUNTRIES
24452
+ ];
24361
24453
  /** Countries available during closed beta. Expand as we open new markets. */
24362
24454
  const BETA_COUNTRIES = ["FI"];
24455
+ [...EU_COUNTRIES.map((c) => c.code).filter((code) => !BETA_COUNTRIES.includes(code))];
24363
24456
  EU_COUNTRIES.filter((c) => BETA_COUNTRIES.includes(c.code));
24364
- Object.fromEntries([...EU_COUNTRIES.map((c) => [c.code, "eu"]), ...EEA_COUNTRIES.map((c) => [c.code, "eea"])]);
24457
+ Object.fromEntries([
24458
+ ...EU_COUNTRIES.map((c) => [c.code, "eu"]),
24459
+ ...EEA_COUNTRIES.map((c) => [c.code, "eea"]),
24460
+ ...UK_COUNTRIES.map((c) => [c.code, "uk"])
24461
+ ]);
24365
24462
  //#endregion
24366
24463
  //#region ../../packages/types/dist/factories.js
24367
24464
  const FACTORY_DOCUMENT_TYPES = ["factory_instructions"];
24465
+ /** The single canonical factory-document type (the free-form Factory Instructions doc). */
24466
+ const FACTORY_INSTRUCTIONS_TYPE = "factory_instructions";
24467
+ /**
24468
+ * The pre-PRO-1671 value of that type. Old clients still name it in URLs
24469
+ * (`/factories/:id/documents/team_instructions`), so the API accepts it as
24470
+ * an alias for a deprecation window.
24471
+ */
24472
+ const LEGACY_FACTORY_INSTRUCTIONS_TYPE = "team_instructions";
24368
24473
  //#endregion
24369
24474
  //#region ../../packages/types/dist/forge.js
24370
24475
  /** Source-control hosts. `bitbucket` is a stored link provider without an adapter yet. */
@@ -24416,6 +24521,15 @@ const FORGE_SUPPORT = {
24416
24521
  ],
24417
24522
  bitbucket: []
24418
24523
  };
24524
+ /**
24525
+ * Whether a repository link under this forge may name a GitHub App
24526
+ * installation's repository: only GitHub's. Stored under another forge, the
24527
+ * link would be dispatched with GitHub credentials while the rest of its
24528
+ * lifecycle reads it as that forge.
24529
+ */
24530
+ function linksGitHubAppRepositories(provider) {
24531
+ return provider === "github";
24532
+ }
24419
24533
  const FORGE_TERMS = {
24420
24534
  github: {
24421
24535
  name: "GitHub",
@@ -24471,6 +24585,11 @@ const PRODUCT_DOCUMENT_TYPES = [
24471
24585
  "product_instructions"
24472
24586
  ];
24473
24587
  /**
24588
+ * The pre-PRO-1671 value of `product_instructions`. Old clients still name it
24589
+ * in URLs, so the API accepts it as an alias for a deprecation window.
24590
+ */
24591
+ const LEGACY_PRODUCT_INSTRUCTIONS_TYPE = "project_instructions";
24592
+ /**
24474
24593
  * The document types the codebase-intelligence agent can derive from a repo --
24475
24594
  * the valid values for a single-document reality refresh (PRO-1141).
24476
24595
  *
@@ -24566,6 +24685,8 @@ const PRODUCT_DOCUMENT_DISPLAY_NAMES = {
24566
24685
  service_blueprint: "Service Blueprint"
24567
24686
  };
24568
24687
  const PRODUCT_DOCUMENT_ORIGINS = ["plan", "reality"];
24688
+ /** The forges a repository link can name: `FORGE_PROVIDERS`, under the name links have always used. */
24689
+ const VCS_PROVIDERS = FORGE_PROVIDERS;
24569
24690
  //#endregion
24570
24691
  //#region ../../packages/types/dist/route-key.js
24571
24692
  /**
@@ -24808,7 +24929,7 @@ const HALT_CAUSES = {
24808
24929
  * reaped on a dead heartbeat, or found at 'planning' with no live job by the
24809
24930
  * reconciler, which fails it with that cause on the tick that notices
24810
24931
  * (`armPlanning` writes the status and creates the job in ONE transaction,
24811
- * so that shape is never a race). One of three halts that mirror
24932
+ * so that shape is never a race). One of four halts that mirror
24812
24933
  * `PLAN_FAILURE_CAUSES` one-to-one; see `PLAN_FAILURE_HALT`.
24813
24934
  */
24814
24935
  planning_stalled: {
@@ -24821,6 +24942,22 @@ const HALT_CAUSES = {
24821
24942
  "remove"
24822
24943
  ]
24823
24944
  },
24945
+ /**
24946
+ * The planner concluded nothing should be built (PRO-3941): a licence's
24947
+ * replacement where only build tools carry the licence and no maintained
24948
+ * package replaces them. Not a fault, so not `planning_failed`; its reason
24949
+ * is the detail, read off the planning job as a failure's is.
24950
+ */
24951
+ planning_declined: {
24952
+ message: "The plan recommends not building this, so the queue stopped here.",
24953
+ params: [],
24954
+ clearedWhen: "planning is retried, or a re-plan replaces it",
24955
+ exits: [
24956
+ "auto",
24957
+ "reorder",
24958
+ "remove"
24959
+ ]
24960
+ },
24824
24961
  /** The run errored out. */
24825
24962
  run_failed: {
24826
24963
  message: "The run failed, so the queue stopped here.",
@@ -25102,7 +25239,8 @@ const READY_BLOCKERS = [
25102
25239
  "bar_lowering",
25103
25240
  "bar_lowering_unchecked",
25104
25241
  "criticals_unresolved",
25105
- "clean_checkout_failed"
25242
+ "clean_checkout_failed",
25243
+ "license_not_approved"
25106
25244
  ];
25107
25245
  /**
25108
25246
  * Why the autonomous loop handed a live pull request to a person (PRO-2409).
@@ -25215,6 +25353,7 @@ const INBOX_ITEM_KINDS = [
25215
25353
  "review_pr",
25216
25354
  "review_maintenance",
25217
25355
  "review_policy",
25356
+ "attest_policy",
25218
25357
  "setup_env_var",
25219
25358
  "setup_secret",
25220
25359
  "setup_third_party",
@@ -25524,6 +25663,29 @@ object({
25524
25663
  [...BUILD_RUN_ACTIVE_STATUSES.filter((s) => s !== "reviewing")];
25525
25664
  //#endregion
25526
25665
  //#region ../../packages/types/dist/agents.js
25666
+ const AGENT_JOB_TYPES = [
25667
+ "generate_document",
25668
+ "generate_backlog",
25669
+ "plan_backlog_item",
25670
+ "infer_backlog_dependencies",
25671
+ "deploy",
25672
+ "provision_infrastructure",
25673
+ "health_check",
25674
+ "refresh_reality_docs",
25675
+ "derive_policy_update",
25676
+ "derive_design_from_documents",
25677
+ "sync_design_tokens",
25678
+ "generate_design_component",
25679
+ "maintenance_sweep",
25680
+ "maintenance_verify",
25681
+ "propose_ui_concepts",
25682
+ "iterate_ui_concept",
25683
+ "fill_questionnaire",
25684
+ "read_questionnaire_layout",
25685
+ "generate_ui_direction",
25686
+ "assess_policy_conformance",
25687
+ "replan_run"
25688
+ ];
25527
25689
  /**
25528
25690
  * Why a PROMPTED `generate_backlog` job planned nothing.
25529
25691
  *
@@ -25932,10 +26094,34 @@ const MCP_TOOLSETS = [
25932
26094
  "observe",
25933
26095
  "documents"
25934
26096
  ];
26097
+ Object.freeze({
26098
+ required: false,
26099
+ gracePeriodDays: 0,
26100
+ requiredSince: null
26101
+ });
25935
26102
  [...TURN_EVENT_TRIGGERS];
25936
26103
  /** Longest request for changes from Taiga (PRO-3035): about four pages of prose. */
25937
26104
  const CHANGE_REQUEST_MAX_CHARS = 1e4;
25938
26105
  //#endregion
26106
+ //#region ../../packages/types/dist/requests.js
26107
+ /** Every way a request can be answered, whatever its kind. */
26108
+ const REQUEST_EXITS = [
26109
+ "resume_run",
26110
+ "replan",
26111
+ "clear",
26112
+ "leave_line",
26113
+ "approve",
26114
+ "reject",
26115
+ "merge",
26116
+ "close",
26117
+ "request_changes",
26118
+ "done",
26119
+ "dismiss",
26120
+ "reviewed"
26121
+ ];
26122
+ /** The most ids an answer's `seen` carries; a longer list is not sent, and the review is not checked. */
26123
+ const REVIEW_SEEN_MAX = 1e4;
26124
+ //#endregion
25939
26125
  //#region ../../packages/types/dist/maintenance.js
25940
26126
  /**
25941
26127
  * Maintenance taxonomy + API DTOs.
@@ -26030,9 +26216,15 @@ function isFixableFinding(finding) {
26030
26216
  if (finding.status !== "open") return false;
26031
26217
  if (finding.location?.historyOnly) return false;
26032
26218
  if (!isAgentRemediable(finding)) return false;
26219
+ if (finding.fingerprint?.startsWith("base-image:")) return typeof finding.baseMove === "boolean" ? finding.baseMove : baseImageMoveRemoves(finding.data);
26033
26220
  if (finding.kind === "vulnerable_dependency" && isMaliciousPackageFinding(finding)) return true;
26034
26221
  return isCodeRemediationKind(finding.kind) || !!finding.recommendedVersion;
26035
26222
  }
26223
+ function baseImageMoveRemoves(data) {
26224
+ const baseImage = typeof data === "object" && data !== null ? data.baseImage : null;
26225
+ const bases = typeof baseImage === "object" && baseImage !== null ? baseImage.bases : null;
26226
+ return Array.isArray(bases) && bases.length > 0 && bases.every((b) => typeof b === "object" && b !== null && typeof b.movesTo === "string" && b.removed === true);
26227
+ }
26036
26228
  /**
26037
26229
  * A malicious package (OSV `MAL-*`): its advisory id or one of its aliases
26038
26230
  * says so, or its tier is Act now with no KEV listing, which only a malware
@@ -26049,6 +26241,17 @@ function isMaliciousPackageFinding(finding) {
26049
26241
  if (Array.isArray(aliases) && aliases.some(mal)) return true;
26050
26242
  return finding.tier === "act_now" && finding.kevListedAt == null;
26051
26243
  }
26244
+ /**
26245
+ * The scanner CATEGORIES a sweep reports, in the order the agent reports them
26246
+ * (PRO-1725). Outcomes are keyed on the category, not the tool, so replacing a
26247
+ * tool in the agents' scanner manifest changes nothing here.
26248
+ */
26249
+ const MAINTENANCE_SCANNERS = [
26250
+ "deps",
26251
+ "secrets",
26252
+ "sast",
26253
+ "iac"
26254
+ ];
26052
26255
  new Map(Object.entries({
26053
26256
  deps: [
26054
26257
  "vulnerable_dependency",
@@ -26254,6 +26457,19 @@ object({
26254
26457
  commit: d.commit
26255
26458
  }));
26256
26459
  /**
26460
+ * The sources whose findings no agent scanner's outcome decides (PRO-4070):
26461
+ * the API's runtime judge (PRO-3659) and base OS judge (PRO-4074), and the
26462
+ * base image reading (PRO-1728), whose findings the agent drafts but only a
26463
+ * sweep that read the images may settle.
26464
+ */
26465
+ const MAINTENANCE_JUDGED_SOURCES = [
26466
+ "runtime",
26467
+ "base_os",
26468
+ "base_image"
26469
+ ];
26470
+ [...MAINTENANCE_SCANNERS, ...MAINTENANCE_JUDGED_SOURCES];
26471
+ new Set(MAINTENANCE_SCANNERS);
26472
+ /**
26257
26473
  * The longest licence string the package list filters by (PRO-2920). The
26258
26474
  * inventory keeps every expression whole whatever its length; this bounds only
26259
26475
  * the query string, far above any expression seen. A longer one is listed in
@@ -26261,6 +26477,213 @@ object({
26261
26477
  */
26262
26478
  const MAINTENANCE_LICENSE_FILTER_MAX = 4096;
26263
26479
  //#endregion
26480
+ //#region ../../packages/types/dist/maintenance-advisory.js
26481
+ /**
26482
+ * Pull the renderable advisory fields out of a finding's raw OSV `data` object.
26483
+ *
26484
+ * `data` is the scanner's raw OSV vulnerability payload (MaintenanceFindingData,
26485
+ * shape varies), so every field is read leniently: a missing or malformed key
26486
+ * yields an empty value rather than throwing. Shared by the web app's finding
26487
+ * detail and the Taiga Audit preview and report (PRO-3791), so both show the
26488
+ * same identifiers for the same finding.
26489
+ */
26490
+ /** A non-empty string, or null: anything else the scanner wrote reads as absent. */
26491
+ const text$3 = string$2().min(1).nullable().catch(null);
26492
+ /** The non-empty strings of a list; a value that is not a list reads as none. */
26493
+ const texts = array(unknown$1()).catch([]).transform((list) => list.filter((v) => typeof v === "string" && v.length > 0));
26494
+ object({
26495
+ type: text$3,
26496
+ score: text$3
26497
+ });
26498
+ object({
26499
+ type: text$3,
26500
+ url: text$3
26501
+ });
26502
+ object({
26503
+ aliases: texts,
26504
+ database_specific: object({
26505
+ cwe_ids: texts,
26506
+ severity: text$3
26507
+ }).nullable().catch(null),
26508
+ severity: array(unknown$1()).catch([]),
26509
+ references: array(unknown$1()).catch([]),
26510
+ published: text$3,
26511
+ modified: text$3
26512
+ });
26513
+ //#endregion
26514
+ //#region ../../packages/types/dist/finding-controls.js
26515
+ /**
26516
+ * A committed credential.
26517
+ *
26518
+ * SEC-3.4 "Secrets must never be stored in source code or logs"
26519
+ * SEC-5.1 "Secrets must be stored in an approved secrets management system"
26520
+ * SEC-5.2 "Secrets must be rotated at least annually" -- the half Taiga cannot
26521
+ * do, and the reason the remediation initiative insists the PR say so.
26522
+ * SDLC-3.5 "Secret detection must run pre-commit and in CI pipeline"
26523
+ */
26524
+ const SECRET_CONTROLS = [
26525
+ "SEC-3.4",
26526
+ "SEC-5.1",
26527
+ "SEC-5.2",
26528
+ "SDLC-3.5"
26529
+ ];
26530
+ /**
26531
+ * A vulnerable dependency.
26532
+ *
26533
+ * SCS-2.6 "critical security patches within 7 days, others within 30 days"
26534
+ * -- breached by a vulnerability whose fix has been out past its
26535
+ * window, and by no other (PRO-3420). One found this morning has missed
26536
+ * nothing; neither has one with no fixed release to move to.
26537
+ * SCS-5.2 "Critical vulnerabilities must block deployment"
26538
+ *
26539
+ * Two controls this map deliberately does NOT claim:
26540
+ * - SCS-2.5 "Transitive dependency vulnerabilities must be monitored
26541
+ * continuously". A finding is that monitoring working, not evidence it is
26542
+ * missing; every vulnerable dependency read as a breach of it. The sweep's
26543
+ * own record shows it met instead (sweep-evidence.ts).
26544
+ * - Anything for an outdated dependency that is no vulnerability: SCS-2.6's
26545
+ * rationale is "timely vulnerability remediation", and an update that fixes
26546
+ * nothing is not a security patch.
26547
+ */
26548
+ const DEPENDENCY_CONTROLS = ["SCS-2.6", "SCS-5.2"];
26549
+ /**
26550
+ * A package whose licence the allowlist does not satisfy (PRO-2862).
26551
+ *
26552
+ * TIC-6.2 "Open source licenses must be reviewed and approved before adoption"
26553
+ * -- the same policy section lists the approved licences and the ones
26554
+ * requiring review, which is where the default allowlist comes from.
26555
+ */
26556
+ const LICENSE_CONTROLS = ["TIC-6.2"];
26557
+ /**
26558
+ * Injection and unsanitized input, whatever the sink.
26559
+ *
26560
+ * SDLC-5.1 "All user input must be validated and sanitized"
26561
+ * SEC-6.1 "All API endpoints must validate and sanitize input"
26562
+ */
26563
+ const INJECTION_CONTROLS = ["SDLC-5.1", "SEC-6.1"];
26564
+ /**
26565
+ * Every opengrep rule Taiga ships, mapped explicitly.
26566
+ *
26567
+ * A closed set, unlike checkov's -- these rules are ours, so an exhaustive map
26568
+ * is possible and a keyword heuristic would be a worse answer to an easier
26569
+ * question. `policy-map.test.ts` reads the rule ids straight out of the YAML
26570
+ * so a new rule that lands unmapped fails the build instead of silently
26571
+ * emitting no control.
26572
+ */
26573
+ const SAST_CONTROLS = {
26574
+ "taiga.csharp.sql-string-concat": INJECTION_CONTROLS,
26575
+ "taiga.go.sql-string-concat": INJECTION_CONTROLS,
26576
+ "taiga.javascript.sql-template-concat": INJECTION_CONTROLS,
26577
+ "taiga.python.sqli-fstring": INJECTION_CONTROLS,
26578
+ "taiga.rust.sql-format-concat": INJECTION_CONTROLS,
26579
+ "taiga.csharp.process-start-shell-dynamic": INJECTION_CONTROLS,
26580
+ "taiga.go.exec-command-dynamic": INJECTION_CONTROLS,
26581
+ "taiga.javascript.child-process-exec-dynamic": INJECTION_CONTROLS,
26582
+ "taiga.python.os-system-dynamic": INJECTION_CONTROLS,
26583
+ "taiga.python.subprocess-shell-true": INJECTION_CONTROLS,
26584
+ "taiga.rust.command-shell-dynamic": INJECTION_CONTROLS,
26585
+ "taiga.javascript.eval-dynamic": INJECTION_CONTROLS,
26586
+ "taiga.python.eval-exec-dynamic": INJECTION_CONTROLS,
26587
+ "taiga.javascript.dangerously-set-inner-html": INJECTION_CONTROLS,
26588
+ "taiga.javascript.innerhtml-dynamic": INJECTION_CONTROLS,
26589
+ "taiga.csharp.binaryformatter-deserialize": ["SDLC-5.7"],
26590
+ "taiga.python.pickle-load-untrusted": ["SDLC-5.7"],
26591
+ "taiga.python.yaml-load-unsafe": ["SDLC-5.7"],
26592
+ "taiga.csharp.cert-validation-disabled": ["SEC-3.2"],
26593
+ "taiga.go.tls-insecure-skip-verify": ["SEC-3.2"],
26594
+ "taiga.javascript.tls-reject-unauthorized-false": ["SEC-3.2"],
26595
+ "taiga.python.requests-verify-false": ["SEC-3.2"],
26596
+ "taiga.rust.tls-verification-disabled": ["SEC-3.2"],
26597
+ "taiga.javascript.jwt-verify-none": ["SDLC-5.4", "SEC-9.1"],
26598
+ "taiga.python.flask-debug-true": ["SDLC-5.5"],
26599
+ "taiga.go.pprof-import": ["SDLC-5.5"],
26600
+ "taiga.csharp.weak-hash": [],
26601
+ "taiga.go.weak-hash": [],
26602
+ "taiga.javascript.weak-hash": [],
26603
+ "taiga.python.weak-hash": [],
26604
+ "taiga.rust.weak-hash": [],
26605
+ "taiga.go.math-rand-for-secrets": [],
26606
+ "taiga.rust.non-crypto-rng-for-secrets": []
26607
+ };
26608
+ /**
26609
+ * Checkov, matched on the check's own description.
26610
+ *
26611
+ * Checkov's rule set is open and numbers in the thousands, so an exhaustive
26612
+ * id map is not available the way it is for our own rules. These phrases are
26613
+ * matched against the check title checkov itself writes ("Ensure all data
26614
+ * stored in the RDS is securely encrypted at rest"), and they are deliberately
26615
+ * narrow: a title matching nothing yields no control, which is the safe
26616
+ * direction to be wrong in.
26617
+ *
26618
+ * Exported so `policy-map.test.ts` validates THESE ids against the registry
26619
+ * too. They are the half most likely to drift, and an id-map that only
26620
+ * validates the closed half is a claim the tests do not support.
26621
+ */
26622
+ const IAC_PHRASES = [
26623
+ {
26624
+ pattern: /encrypt\w*\s+at\s+rest|at\s+rest.*encrypt|securely encrypted/i,
26625
+ controls: ["SEC-3.1"],
26626
+ why: "at-rest encryption"
26627
+ },
26628
+ {
26629
+ pattern: /\bKMS\b|customer[- ]managed key|\bCMK\b/i,
26630
+ controls: ["SEC-3.3"],
26631
+ why: "key management"
26632
+ },
26633
+ {
26634
+ pattern: /in\s+transit|\bTLS\b|\bSSL\b|\bHTTPS\b/i,
26635
+ controls: ["SEC-3.2"],
26636
+ why: "transport encryption"
26637
+ },
26638
+ {
26639
+ pattern: /0\.0\.0\.0\/0|ingress from|security group/i,
26640
+ controls: ["CLD-7.2"],
26641
+ why: "network default-deny"
26642
+ },
26643
+ {
26644
+ pattern: /public(ly)?\s+(access|read|write|expos)|public access/i,
26645
+ controls: ["CLD-1.3"],
26646
+ why: "public exposure"
26647
+ },
26648
+ {
26649
+ pattern: /least privilege|wildcard|admin(istrator)? (policy|access)/i,
26650
+ controls: ["SEC-2.1"],
26651
+ why: "least privilege"
26652
+ },
26653
+ {
26654
+ pattern: /backup|point[- ]in[- ]time recovery/i,
26655
+ controls: ["OPS-4.1"],
26656
+ why: "backup"
26657
+ },
26658
+ {
26659
+ pattern: /audit log|cloudtrail|access logging|logging (is )?enabled/i,
26660
+ controls: ["SDLC-5.8"],
26661
+ why: "security logging"
26662
+ }
26663
+ ];
26664
+ [.../* @__PURE__ */ new Set([
26665
+ ...SECRET_CONTROLS,
26666
+ ...DEPENDENCY_CONTROLS,
26667
+ ...LICENSE_CONTROLS,
26668
+ ...Object.values(SAST_CONTROLS).flat(),
26669
+ ...IAC_PHRASES.flatMap((p) => p.controls)
26670
+ ])];
26671
+ new RegExp(`^\\|\\s*(${CONTROL_ID_SOURCE})\\s*\\|\\s*(.*?)\\s*(?:\\||$)`);
26672
+ REALITY_DERIVED_DOCUMENT_TYPES.filter((t) => t !== "codebase_snapshot");
26673
+ //#endregion
26674
+ //#region ../../packages/types/dist/custom-documents.js
26675
+ /**
26676
+ * Shared types for the custom-documents feature: customer-uploaded reference
26677
+ * files (PDFs, Markdown, OpenAPI specs, etc.) indexed into a Bedrock
26678
+ * Knowledge Base for retrieval by Taiga's internal agents.
26679
+ */
26680
+ const CUSTOM_DOCUMENT_STATUSES = [
26681
+ "pending",
26682
+ "ingesting",
26683
+ "ready",
26684
+ "failed"
26685
+ ];
26686
+ //#endregion
26264
26687
  //#region ../../packages/types/dist/design-tokens.js
26265
26688
  /**
26266
26689
  * The brand grounding contract (PRO-929).
@@ -26436,6 +26859,509 @@ object({
26436
26859
  fontSize: lenient(union([string$2(), number$1()]))
26437
26860
  });
26438
26861
  //#endregion
26862
+ //#region ../../packages/types/dist/questionnaires.js
26863
+ /**
26864
+ * Questionnaire filling (PRO-932).
26865
+ *
26866
+ * A customer uploads their own requirement spreadsheet; Taiga answers what the
26867
+ * project's evidence settles, cites the document version each answer rests on,
26868
+ * and hands back the gaps. The register these types describe is the durable
26869
+ * object -- the workbook is an import format on one side and an export format
26870
+ * on the other.
26871
+ */
26872
+ /**
26873
+ * Lifecycle of one questionnaire.
26874
+ *
26875
+ * `filling` is deliberately NOT set by the fill workflow (PRO-1187), and that
26876
+ * is a decision rather than an omission. A status set at the start of a
26877
+ * multi-minute run needs something to clear it when the run does not finish --
26878
+ * and if the worker dies mid-fill, nothing would: `agent_jobs` has a reaper for
26879
+ * stale JOBS, but it knows nothing about this table, so the register would read
26880
+ * `filling` forever. A state nothing can leave is worse than one nothing
26881
+ * enters.
26882
+ *
26883
+ * "Is a fill running" is already answerable, and from the row that has a reaper:
26884
+ * an active `fill_questionnaire` job with this questionnaire as its target.
26885
+ * Wire `filling` up when it comes with a path off it.
26886
+ */
26887
+ /**
26888
+ * `reading` and `needs_columns` are the import itself (PRO-1506). A workbook
26889
+ * is stored and its row exists from the first byte; the column read runs as
26890
+ * a job. `reading` is that job in flight; `needs_columns` is the read done
26891
+ * without confidence, waiting for a person to confirm the map. Only once the
26892
+ * map is confirmed do items exist and the row becomes `draft`.
26893
+ */
26894
+ /**
26895
+ * A sheet map's bounds, Excel's own: a column index becomes a column letter
26896
+ * in a cell reference the export writes to, so an unbounded one names
26897
+ * whatever it likes.
26898
+ */
26899
+ const EXCEL_MAX_COLUMN = 16384;
26900
+ const EXCEL_MAX_ROW = 1048576;
26901
+ //#endregion
26902
+ //#region ../../packages/types/dist/product-network.js
26903
+ /**
26904
+ * Where an engineer build's sandbox may connect to (PRO-2793, part of
26905
+ * PRO-1980).
26906
+ *
26907
+ * The engineer pod's credential gateway is the sandbox's only way out. By
26908
+ * default it lets through a standard list: the public package registries,
26909
+ * toolchain downloads, container registries, GitHub for reading public
26910
+ * dependencies, and the tenant's own registries. A product adds to that:
26911
+ *
26912
+ * - `allowlist` (the default, also what `null` means): the standard list plus
26913
+ * the product's own `networkHosts`.
26914
+ * - `unrestricted`: any public host. For a product whose builds need hosts
26915
+ * nobody can list in advance; the gateway still refuses private and
26916
+ * metadata addresses, and still never hands the sandbox a credential.
26917
+ *
26918
+ * A host is one exact name (`api.example.com`). There are no wildcards: a
26919
+ * wildcard over a multi-label public suffix (`*.co.uk`) or a provider's
26920
+ * namespace would admit every tenant under it, and telling those apart needs
26921
+ * the public suffix list. A build that needs many names under one domain is
26922
+ * what `unrestricted` is for.
26923
+ */
26924
+ const PRODUCT_NETWORK_ACCESS = ["allowlist", "unrestricted"];
26925
+ /**
26926
+ * A host a product may add: lowercase DNS labels, at least two, ending in a
26927
+ * letter label. No wildcard, scheme, port, path or IP literal.
26928
+ */
26929
+ const PRODUCT_NETWORK_HOST_PATTERN = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9]$/;
26930
+ /**
26931
+ * Domains where anyone can host content under their own name: a bucket, an
26932
+ * app, a tunnel. Listing one whole would let a prompt injection send code to
26933
+ * a bucket the attacker made, the way the gateway's standard list never lists
26934
+ * a shared CDN whole. A specific name under one (`my-bucket.s3.amazonaws.com`)
26935
+ * is fine.
26936
+ */
26937
+ const SHARED_HOSTING_DOMAINS = [
26938
+ "amazonaws.com",
26939
+ "s3.amazonaws.com",
26940
+ "cloudfront.net",
26941
+ "googleapis.com",
26942
+ "storage.googleapis.com",
26943
+ "appspot.com",
26944
+ "firebaseapp.com",
26945
+ "web.app",
26946
+ "blob.core.windows.net",
26947
+ "azureedge.net",
26948
+ "azurewebsites.net",
26949
+ "githubusercontent.com",
26950
+ "github.io",
26951
+ "herokuapp.com",
26952
+ "vercel.app",
26953
+ "netlify.app",
26954
+ "pages.dev",
26955
+ "workers.dev",
26956
+ "trycloudflare.com",
26957
+ "ngrok.io",
26958
+ "ngrok-free.app",
26959
+ "onrender.com",
26960
+ "fly.dev"
26961
+ ];
26962
+ /** Whether `host` has the shape of a host a product may list. */
26963
+ function isProductNetworkHost(host) {
26964
+ return PRODUCT_NETWORK_HOST_PATTERN.test(host);
26965
+ }
26966
+ /** Whether `host` is a domain anyone can host content on, listed whole. */
26967
+ function isSharedHostingDomain(host) {
26968
+ return SHARED_HOSTING_DOMAINS.includes(host);
26969
+ }
26970
+ strictObject({});
26971
+ const uuid = string$2().uuid();
26972
+ const sha256 = string$2().regex(/^[0-9a-f]{64}$/, "sha256 must be 64 lowercase hex characters");
26973
+ object({ jobId: string$2() });
26974
+ /** An upload's grant: a presigned PUT to staging and the headers it signed. */
26975
+ const uploadGrant = object({
26976
+ id: string$2(),
26977
+ url: string$2(),
26978
+ headers: record(string$2(), string$2()),
26979
+ expiresAt: string$2()
26980
+ });
26981
+ object({ version: number$1().int().min(1) });
26982
+ object({ documentId: string$2().uuid() });
26983
+ /** The uploads a product's move could not re-file, named while they fit the answer (PRO-3497). */
26984
+ const failedDocuments = array(object({
26985
+ id: string$2(),
26986
+ fileName: string$2(),
26987
+ error: string$2()
26988
+ }));
26989
+ object({
26990
+ backlogItemId: string$2(),
26991
+ divergenceCount: number$1(),
26992
+ created: boolean()
26993
+ });
26994
+ /** The options of a policy fix: build it once planned, and refuse it when the violation was fixed since. */
26995
+ const policyFixOptions = {
26996
+ autoBuild: boolean().optional(),
26997
+ refuseIfFixedSinceAssessment: boolean().optional()
26998
+ };
26999
+ object({
27000
+ findings: number$1(),
27001
+ findingIds: array(string$2()).nullable()
27002
+ });
27003
+ /** An ignore's expiry, when it has one, is in the future. */
27004
+ const expiresInFuture = (expiresAt) => !expiresAt || new Date(expiresAt).getTime() > Date.now();
27005
+ const EXPIRY_IN_FUTURE = {
27006
+ message: "The ignore expiry must be in the future.",
27007
+ path: ["ignoreExpiresAt"]
27008
+ };
27009
+ const ignoreNote = string$2().trim().max(280).nullish();
27010
+ const ignoreExpiresAt = string$2().datetime({ offset: true }).nullish();
27011
+ const productDocumentType = _enum([...PRODUCT_DOCUMENT_TYPES, LEGACY_PRODUCT_INSTRUCTIONS_TYPE]).transform((type) => type === "project_instructions" ? "product_instructions" : type);
27012
+ const customDocumentStatus = _enum(CUSTOM_DOCUMENT_STATUSES);
27013
+ const cloudPreferences = object({
27014
+ iacTool: _enum([
27015
+ "terraform",
27016
+ "pulumi",
27017
+ "cdk",
27018
+ "none"
27019
+ ]).nullable().optional(),
27020
+ stateBackend: _enum([
27021
+ "aws-s3",
27022
+ "gcs",
27023
+ "azurerm",
27024
+ "tfcloud",
27025
+ "other"
27026
+ ]).nullable().optional(),
27027
+ ciSystem: _enum([
27028
+ "github-actions",
27029
+ "gitlab-ci",
27030
+ "circleci",
27031
+ "jenkins",
27032
+ "other"
27033
+ ]).nullable().optional(),
27034
+ authMethod: _enum([
27035
+ "oidc",
27036
+ "static-keys",
27037
+ "unknown"
27038
+ ]).optional(),
27039
+ accountIsolation: _enum([
27040
+ "per-env",
27041
+ "shared",
27042
+ "unknown"
27043
+ ]).optional()
27044
+ }).optional();
27045
+ const cloudBestPractices = object({
27046
+ dedicatedAccount: boolean().optional(),
27047
+ oidcAuth: boolean().optional(),
27048
+ regionMatchesPolicy: boolean().optional(),
27049
+ stateInSameAccount: boolean().optional()
27050
+ }).optional();
27051
+ const cloudProviderConfig = discriminatedUnion("provider", [
27052
+ object({
27053
+ provider: literal("aws"),
27054
+ accountId: string$2().regex(/^\d{12}$/, "AWS account ID must be 12 digits").optional(),
27055
+ region: string$2().min(1),
27056
+ preferences: cloudPreferences,
27057
+ bestPractices: cloudBestPractices
27058
+ }),
27059
+ object({
27060
+ provider: literal("gcp"),
27061
+ projectId: string$2().regex(/^[a-z][a-z0-9-]{4,28}[a-z0-9]$/, "Invalid GCP project ID").optional(),
27062
+ region: string$2().min(1),
27063
+ preferences: cloudPreferences,
27064
+ bestPractices: cloudBestPractices
27065
+ }),
27066
+ object({
27067
+ provider: literal("azure"),
27068
+ subscriptionId: string$2().uuid("Azure subscription ID must be a UUID").optional(),
27069
+ tenantId: string$2().uuid("Azure tenant ID must be a UUID").optional(),
27070
+ region: string$2().min(1),
27071
+ preferences: cloudPreferences,
27072
+ bestPractices: cloudBestPractices
27073
+ }),
27074
+ object({
27075
+ provider: literal("upcloud"),
27076
+ zone: string$2().min(1),
27077
+ username: string$2().min(1).optional(),
27078
+ authMethod: _enum(["password", "token"]).optional(),
27079
+ preferences: cloudPreferences,
27080
+ bestPractices: cloudBestPractices
27081
+ })
27082
+ ]);
27083
+ const columnIndex = number$1().int().min(1).max(EXCEL_MAX_COLUMN);
27084
+ const sheetMap = object({
27085
+ sheetName: string$2().min(1).max(120),
27086
+ idColumn: columnIndex,
27087
+ questionColumn: columnIndex,
27088
+ detailColumns: array(columnIndex).max(20).optional(),
27089
+ sectionColumn: columnIndex.optional(),
27090
+ answerColumn: columnIndex,
27091
+ justificationColumn: columnIndex.optional(),
27092
+ firstDataRow: number$1().int().min(1).max(EXCEL_MAX_ROW)
27093
+ });
27094
+ const answerValue = string$2().trim().min(1).max(64);
27095
+ const justification = string$2().trim().max(2e3);
27096
+ const notApplicableReason = string$2().trim().min(1).max(1200);
27097
+ strictObject({ comment: string$2().trim().min(1).max(PLAN_REJECTION_COMMENT_MAX_CHARS) });
27098
+ strictObject({ expectedRunId: uuid.optional() }), strictObject({ force: boolean().optional() });
27099
+ strictObject({
27100
+ notes: string$2().trim().min(1).max(CHANGE_REQUEST_MAX_CHARS),
27101
+ /** Show the revised plan before the run continues (PRO-3164). */
27102
+ reviewPlan: boolean().optional(),
27103
+ /** The repository whose pull request should change, by its full name, for a run in several (PRO-3346). */
27104
+ repository: string$2().trim().min(1).max(255).optional()
27105
+ }), object({ steer: _enum([
27106
+ "replanning",
27107
+ "after_step",
27108
+ "after_round",
27109
+ "held",
27110
+ "sent"
27111
+ ]).optional() }), object({ files: array(object({
27112
+ id: string$2(),
27113
+ url: string$2().url()
27114
+ })) });
27115
+ object({ images: array(object({
27116
+ name: string$2(),
27117
+ url: string$2().url().nullable()
27118
+ })) });
27119
+ strictObject({
27120
+ exit: _enum(REQUEST_EXITS),
27121
+ comment: string$2().trim().min(1).max(PLAN_REJECTION_COMMENT_MAX_CHARS).optional(),
27122
+ force: boolean().optional(),
27123
+ seen: array(string$2().uuid()).max(REVIEW_SEEN_MAX).optional()
27124
+ }).refine((answer) => answer.exit === "reject" === (answer.comment !== void 0), {
27125
+ message: "A rejection takes a comment for the planner, and no other answer takes one.",
27126
+ path: ["comment"]
27127
+ }).refine((answer) => answer.exit === "replan" || answer.force === void 0, {
27128
+ message: "Only Plan again can plan anyway.",
27129
+ path: ["force"]
27130
+ }).refine((answer) => answer.exit === "reviewed" || answer.seen === void 0, {
27131
+ message: "Only Mark reviewed says what was shown.",
27132
+ path: ["seen"]
27133
+ });
27134
+ strictObject({ type: productDocumentType }), object({
27135
+ version: number$1().int().min(1),
27136
+ unknownCitations: object({
27137
+ shown: array(string$2()),
27138
+ count: number$1().int().min(0)
27139
+ })
27140
+ }), strictObject({ type: productDocumentType }), strictObject({ type: productDocumentType }), strictObject({}), object({ started: number$1().int().min(0) }), strictObject({
27141
+ githubRepositoryId: uuid.optional(),
27142
+ /** A GitLab project, by the connection whose bot reaches it and GitLab's numeric id (P-PRO-150). */
27143
+ gitlabConnectionId: uuid.optional(),
27144
+ gitlabProjectId: number$1().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
27145
+ provider: _enum(VCS_PROVIDERS).optional().default("github"),
27146
+ repositoryName: string$2().min(1).max(512).optional(),
27147
+ repositoryUrl: string$2().url().max(2048).optional(),
27148
+ repositoryDefaultBranch: string$2().max(255).optional().default("main"),
27149
+ /** The link this one replaces, retired in the same transaction (PRO-2522). */
27150
+ replacesRepositoryId: uuid.optional()
27151
+ }).refine((b) => b.gitlabConnectionId === void 0 === (b.gitlabProjectId === void 0), { message: "gitlabConnectionId and gitlabProjectId go together" }).refine((b) => b.gitlabConnectionId === void 0 || b.githubRepositoryId === void 0, { message: "A link is to a GitHub repository or a GitLab project, not both" }).refine((b) => b.githubRepositoryId === void 0 || linksGitHubAppRepositories(b.provider), { message: "githubRepositoryId is a GitHub repository; provider must be github" }), strictObject({
27152
+ repositoryId: uuid,
27153
+ baseBranch: string$2().trim().min(1).max(255).nullable()
27154
+ }), object({
27155
+ effectiveBranch: string$2(),
27156
+ protection: object({
27157
+ protected: boolean(),
27158
+ requiredStatusChecks: array(string$2()),
27159
+ enforcementLevel: string$2().nullable()
27160
+ }).nullable()
27161
+ }), strictObject({
27162
+ license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX),
27163
+ ignoreReason: _enum(MAINTENANCE_IGNORE_REASONS),
27164
+ ignoreNote,
27165
+ ignoreExpiresAt
27166
+ }).refine((args) => expiresInFuture(args.ignoreExpiresAt), EXPIRY_IN_FUTURE), strictObject({
27167
+ license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX),
27168
+ findingIds: array(uuid).min(1).optional()
27169
+ }), strictObject({ license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX) }), object({
27170
+ backlogItemIds: array(string$2()),
27171
+ findingCount: number$1()
27172
+ }), strictObject({}), object({
27173
+ backlogItemId: string$2(),
27174
+ created: boolean()
27175
+ }), strictObject({ host: string$2().trim().toLowerCase().refine(isProductNetworkHost, { message: "A host is one exact DNS name, such as api.example.com: no wildcard, scheme, port or path" }).refine((host) => !isSharedHostingDomain(host), { message: "Anyone can host content on this domain, so it cannot be listed whole; list the one host you need under it" }) }), strictObject({
27176
+ type: _enum(AGENT_JOB_TYPES),
27177
+ documentType: string$2().max(30).optional(),
27178
+ priority: number$1().int().min(0).max(99).optional(),
27179
+ metadata: record(string$2(), unknown$1()).optional(),
27180
+ /** Which implementation should serve it (PRO-1332): a request the server may refuse. */
27181
+ runtime: _enum(AGENT_RUNTIMES).optional()
27182
+ }), strictObject({
27183
+ fileName: string$2().trim().min(1).max(1024),
27184
+ sizeBytes: number$1().int().nonnegative(),
27185
+ sha256
27186
+ }), object({ upload: uploadGrant }), strictObject({ uploadId: uuid }), object({
27187
+ questionnaireId: string$2(),
27188
+ jobId: string$2()
27189
+ }), strictObject({ mode: _enum(["generate", "republish"]).default("generate") }), strictObject({ docTypes: array(_enum(REALITY_DERIVED_DOCUMENT_TYPES)).min(1).max(REALITY_DERIVED_DOCUMENT_TYPES.length).optional() }), object({ jobIds: array(string$2()) }), object({ cancelledJobIds: array(string$2()) }), strictObject({
27190
+ factoryId: uuid,
27191
+ stopLine: boolean().optional()
27192
+ }), object({
27193
+ changed: boolean(),
27194
+ previousFactoryId: string$2().nullable(),
27195
+ lineStopped: boolean(),
27196
+ failedDocuments,
27197
+ /** How many uploads kept the old factory; null while not known, never read as none. */
27198
+ failedCount: number$1().int().min(0).nullable()
27199
+ }), strictObject({ documentIds: array(uuid).min(1).max(100).optional() }), object({
27200
+ rescopedCount: number$1().int().min(0).nullable(),
27201
+ failedDocuments,
27202
+ failedCount: number$1().int().min(0).nullable()
27203
+ }), strictObject({ repositoryId: uuid }), strictObject({
27204
+ ecosystem: string$2().min(1).max(50),
27205
+ packageName: string$2().min(1).max(255),
27206
+ /** The installed version, for a package resolved at several in a monorepo. */
27207
+ currentVersion: string$2().min(1).max(100).nullish(),
27208
+ /** The linked repository the group belongs to: a fix touches one. */
27209
+ productRepositoryId: uuid.nullish(),
27210
+ /** Build the fix as soon as it is planned (PRO-949). */
27211
+ autoBuild: boolean().optional()
27212
+ }), object({
27213
+ backlogItemId: string$2(),
27214
+ findingCount: number$1()
27215
+ }), strictObject({
27216
+ ...policyFixOptions,
27217
+ controlId: string$2().min(1).max(40)
27218
+ }), strictObject({
27219
+ controlId: string$2().min(1).max(40),
27220
+ validMonths: union([
27221
+ literal(3),
27222
+ literal(6),
27223
+ literal(12)
27224
+ ]),
27225
+ note: string$2().trim().max(1e3).optional()
27226
+ }), strictObject({ environments: array(string$2()).default(["development"]) }), strictObject({
27227
+ connection: object({
27228
+ name: string$2().min(1).max(150),
27229
+ provider: _enum(CLOUD_PROVIDERS),
27230
+ providerConfig: cloudProviderConfig
27231
+ }).refine((connection) => connection.provider === connection.providerConfig.provider, {
27232
+ message: "connection.provider must match connection.providerConfig.provider",
27233
+ path: ["providerConfig", "provider"]
27234
+ }),
27235
+ environment: object({
27236
+ name: string$2().min(1).max(100),
27237
+ slug: string$2().min(1).max(63).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Slug must be lowercase letters, digits, and dashes"),
27238
+ type: _enum(ENVIRONMENT_TYPES),
27239
+ ordinal: number$1().int().min(0).default(0),
27240
+ isProtected: boolean().default(false)
27241
+ }).optional()
27242
+ });
27243
+ object({
27244
+ url: string$2().url(),
27245
+ expiresAt: string$2()
27246
+ });
27247
+ object({
27248
+ opens: number$1().int().nonnegative(),
27249
+ closes: number$1().int().nonnegative(),
27250
+ products: array(object({
27251
+ productId: string$2().uuid(),
27252
+ opens: number$1().int().nonnegative(),
27253
+ closes: number$1().int().nonnegative()
27254
+ }))
27255
+ });
27256
+ object({
27257
+ status: customDocumentStatus.nullable(),
27258
+ /** The retry itself threw; absent when it went through. */
27259
+ error: string$2().optional()
27260
+ });
27261
+ /** A factory document type, its legacy name read as the current one. */
27262
+ const factoryDocumentType = _enum([...FACTORY_DOCUMENT_TYPES, LEGACY_FACTORY_INSTRUCTIONS_TYPE]).transform((type) => type === "team_instructions" ? FACTORY_INSTRUCTIONS_TYPE : type);
27263
+ strictObject({ type: factoryDocumentType }), strictObject({ type: factoryDocumentType });
27264
+ strictObject({ type: _enum(TENANT_DOCUMENT_TYPES) }), strictObject({ type: _enum(TENANT_DOCUMENT_TYPES).refine((type) => type !== "design_standards", { message: "The design standards draft is not made through sync" }) }), strictObject({
27265
+ name: string$2().min(1).max(50),
27266
+ action: _enum(["accept", "decline"])
27267
+ }), object({
27268
+ applied: _enum([
27269
+ "draft",
27270
+ "published",
27271
+ "declined"
27272
+ ]),
27273
+ version: number$1().int().nullable()
27274
+ }), strictObject({
27275
+ fileName: string$2().trim().min(1).max(255),
27276
+ mime: string$2().min(1).max(255),
27277
+ sizeBytes: number$1().int().positive(),
27278
+ sha256,
27279
+ factoryId: string$2().optional(),
27280
+ productId: string$2().optional(),
27281
+ origin: string$2().optional()
27282
+ }), discriminatedUnion("deduplicated", [object({
27283
+ deduplicated: literal(true),
27284
+ documentId: string$2(),
27285
+ factoryId: string$2().nullable(),
27286
+ status: customDocumentStatus
27287
+ }), object({
27288
+ deduplicated: literal(false),
27289
+ upload: uploadGrant
27290
+ })]), strictObject({ uploadId: uuid }), object({
27291
+ documentId: string$2(),
27292
+ factoryId: string$2().nullable(),
27293
+ deduplicated: boolean(),
27294
+ status: customDocumentStatus.nullable(),
27295
+ /** Filing it threw; absent when it went through. */
27296
+ error: string$2().optional()
27297
+ }), strictObject({ ids: array(uuid).min(1).max(100) }), object({
27298
+ failedIds: array(string$2()).nullable(),
27299
+ deletedIds: array(string$2()).optional()
27300
+ }), strictObject({ ids: array(uuid).min(1).max(25) }), object({ referenced: array(string$2()) }), strictObject({
27301
+ approve: array(string$2().min(1).max(100)).max(200).default([]),
27302
+ remove: array(string$2().min(1).max(100)).max(200).default([])
27303
+ }).refine((b) => b.approve.length + b.remove.length > 0, { message: "Name a license to approve or remove" }), object({ version: number$1().nullable() }), strictObject({
27304
+ approve: string$2().trim().min(1).max(100).optional(),
27305
+ remove: string$2().trim().min(1).max(100).optional()
27306
+ }).refine((a) => Boolean(a.approve) !== Boolean(a.remove), { message: "Name one license to approve or remove" }), object({
27307
+ enrolled: number$1().int().nonnegative(),
27308
+ total: number$1().int().nonnegative()
27309
+ });
27310
+ strictObject({ kind: _enum(["sbom", "notices"]) }), object({
27311
+ url: string$2().url().nullable(),
27312
+ expiresAt: string$2().nullable()
27313
+ });
27314
+ strictObject({
27315
+ ignoreReason: _enum(MAINTENANCE_IGNORE_REASONS),
27316
+ ignoreNote,
27317
+ ignoreExpiresAt
27318
+ }).refine((args) => expiresInFuture(args.ignoreExpiresAt), EXPIRY_IN_FUTURE), strictObject({ autoBuild: boolean().optional() }), object({ backlogItemId: string$2() });
27319
+ strictObject(policyFixOptions);
27320
+ strictObject({ tier: _enum([
27321
+ "development",
27322
+ "staging",
27323
+ "production"
27324
+ ]) });
27325
+ object({
27326
+ url: string$2().url(),
27327
+ expiresAt: string$2(),
27328
+ written: number$1(),
27329
+ unwrittenCount: number$1(),
27330
+ summary: object({
27331
+ byTaiga: number$1(),
27332
+ byYou: number$1(),
27333
+ notApplicable: number$1(),
27334
+ blank: number$1()
27335
+ })
27336
+ }), strictObject({
27337
+ columnMap: object({ sheets: array(sheetMap).min(1).max(50) }),
27338
+ answerVocabulary: array(string$2().min(1).max(64)).max(16).nullable().optional(),
27339
+ answerLegend: string$2().max(4e3).nullable().optional()
27340
+ }), object({
27341
+ imported: number$1(),
27342
+ skippedRows: number$1()
27343
+ });
27344
+ discriminatedUnion("action", [
27345
+ strictObject({ action: literal("accept") }),
27346
+ strictObject({ action: literal("reject") }),
27347
+ strictObject({
27348
+ action: literal("edit"),
27349
+ answerValue: answerValue.optional(),
27350
+ justification: justification.optional()
27351
+ }),
27352
+ strictObject({
27353
+ action: literal("answer"),
27354
+ answerValue,
27355
+ justification: justification.optional()
27356
+ }),
27357
+ strictObject({
27358
+ action: literal("not_applicable"),
27359
+ /** The token the form uses for it, when it has one -- `NA`, `N/A`, `es`. */
27360
+ answerValue: answerValue.optional(),
27361
+ reason: notApplicableReason
27362
+ })
27363
+ ]);
27364
+ //#endregion
26439
27365
  //#region ../../packages/types/dist/auto-build.js
26440
27366
  /**
26441
27367
  * Auto-build: the review-plans gate (PRO-1305).
@@ -26520,30 +27446,6 @@ Object.freeze({
26520
27446
  applicationOwnership: true
26521
27447
  });
26522
27448
  //#endregion
26523
- //#region ../../packages/types/dist/product-network.js
26524
- /**
26525
- * Where an engineer build's sandbox may connect to (PRO-2793, part of
26526
- * PRO-1980).
26527
- *
26528
- * The engineer pod's credential gateway is the sandbox's only way out. By
26529
- * default it lets through a standard list: the public package registries,
26530
- * toolchain downloads, container registries, GitHub for reading public
26531
- * dependencies, and the tenant's own registries. A product adds to that:
26532
- *
26533
- * - `allowlist` (the default, also what `null` means): the standard list plus
26534
- * the product's own `networkHosts`.
26535
- * - `unrestricted`: any public host. For a product whose builds need hosts
26536
- * nobody can list in advance; the gateway still refuses private and
26537
- * metadata addresses, and still never hands the sandbox a credential.
26538
- *
26539
- * A host is one exact name (`api.example.com`). There are no wildcards: a
26540
- * wildcard over a multi-label public suffix (`*.co.uk`) or a provider's
26541
- * namespace would admit every tenant under it, and telling those apart needs
26542
- * the public suffix list. A build that needs many names under one domain is
26543
- * what `unrestricted` is for.
26544
- */
26545
- const PRODUCT_NETWORK_ACCESS = ["allowlist", "unrestricted"];
26546
- //#endregion
26547
27449
  //#region src/compat.ts
26548
27450
  /**
26549
27451
  * Why this server should not serve the API it reached, or null when it may
@@ -26611,6 +27513,7 @@ const TOOLSET_OF = {
26611
27513
  taiga_fix_finding: "operate",
26612
27514
  taiga_fix_policy_violation: "operate",
26613
27515
  taiga_ignore_finding: "operate",
27516
+ taiga_allow_license: "operate",
26614
27517
  taiga_resolve_action: "operate",
26615
27518
  taiga_create_factory: "setup",
26616
27519
  taiga_rename_factory: "setup",
@@ -26635,6 +27538,7 @@ const TOOLSET_OF = {
26635
27538
  taiga_pause_line: "setup",
26636
27539
  taiga_create_environment: "setup",
26637
27540
  taiga_update_environment: "setup",
27541
+ taiga_ship_notices: "setup",
26638
27542
  taiga_get_deployments: "observe",
26639
27543
  taiga_get_monitoring: "observe",
26640
27544
  taiga_get_delivery_metrics: "observe",
@@ -26664,6 +27568,40 @@ const TOOLSET_OF = {
26664
27568
  taiga_retry_knowledge: "documents"
26665
27569
  };
26666
27570
  /**
27571
+ * Deliver & Operate's tools (PRO-3834): initiatives, plans, runs and the line, fixes, environments, deployments,
27572
+ * monitoring and delivery metrics. An organization whose plan refuses delivery work does not see them, as its web app
27573
+ * shows none of their views; the API says which (`deliveryIncluded` at introspection), so the plan, not the key,
27574
+ * decides. Fixing a finding plans and builds, so it goes with them.
27575
+ */
27576
+ const DELIVERY_TOOLS = /* @__PURE__ */ new Set([
27577
+ "taiga_list_initiatives",
27578
+ "taiga_get_initiative",
27579
+ "taiga_get_plan",
27580
+ "taiga_list_runs",
27581
+ "taiga_get_run",
27582
+ "taiga_get_line",
27583
+ "taiga_queue_initiative",
27584
+ "taiga_move_initiative",
27585
+ "taiga_review_plan",
27586
+ "taiga_plan_again",
27587
+ "taiga_resume_initiative",
27588
+ "taiga_stop_run",
27589
+ "taiga_request_changes",
27590
+ "taiga_request_initiatives",
27591
+ "taiga_fix_finding",
27592
+ "taiga_fix_policy_violation",
27593
+ "taiga_ship_notices",
27594
+ "taiga_set_auto_build",
27595
+ "taiga_set_auto_merge",
27596
+ "taiga_pause_line",
27597
+ "taiga_create_environment",
27598
+ "taiga_update_environment",
27599
+ "taiga_get_deployments",
27600
+ "taiga_get_monitoring",
27601
+ "taiga_get_delivery_metrics",
27602
+ "taiga_sync_design_tokens"
27603
+ ]);
27604
+ /**
26667
27605
  * TAIGA_TOOLSETS as written: comma-separated names, or "all"; a name it does not know is said, not ignored, and so
26668
27606
  * is a value that names none.
26669
27607
  */
@@ -26894,7 +27832,7 @@ function setupNextStep(product) {
26894
27832
  if (isImportedProduct(product)) switch (importStatusOf(product)) {
26895
27833
  case "analyzing":
26896
27834
  case "generating_docs": return `The import is running and derives the documents from the code (taiga_wait (jobId) follows its job). Then review them, and ${FINISH_DISCOVERY}`;
26897
- case "complete": return "The import is done: review the reality documents (taiga_get_product_document without type), then taiga_finish_discovery opens the product and starts planning its first initiatives (paid agent work: ask the person first).";
27835
+ case "complete": return "The import is done: review the reality documents (taiga_get_product_document without type), then " + FINISH_DISCOVERY;
26898
27836
  case "failed":
26899
27837
  case "cancelled": return "The last import did not finish: taiga_start_import runs it again.";
26900
27838
  default: return "Link its repository if it has none (taiga_list_linkable_repositories, taiga_link_repository), then taiga_start_import: the import derives the documents from the code and runs by itself.";
@@ -26902,7 +27840,6 @@ function setupNextStep(product) {
26902
27840
  if (product.discoveryStep === void 0) return null;
26903
27841
  switch (product.discoveryStep) {
26904
27842
  case "documents": return `The specification is done; the other documents are generated from it (taiga_generate_product_documents starts the ones still missing). Once every required one is published, ${FINISH_DISCOVERY}`;
26905
- case "checks": return `The documents are published; the checks are stamped in the web, then ${FINISH_DISCOVERY}`;
26906
27843
  default: return `Discovery is a conversation held in the web: the person describes the product there and the specification comes out of it. The other documents follow, then ${FINISH_DISCOVERY}`;
26907
27844
  }
26908
27845
  }
@@ -26931,8 +27868,16 @@ const BY_ARGUMENT = {
26931
27868
  knowledgeId: "taiga_list_knowledge lists the knowledge files.",
26932
27869
  productId: "taiga_list_products lists the products the key can see; a name is not an ID (taiga_get_product takes a slug)."
26933
27870
  };
27871
+ /**
27872
+ * A licence a product does not hold, as against a product the key cannot see
27873
+ * (PRO-3879). Any other 404 (the routes do not exist where the licenses
27874
+ * feature is off) blames neither.
27875
+ */
27876
+ const licenseMiss = (hint) => (said) => /\blicen[cs]e\b/i.test(said) ? hint : /\b(product|project) not found/i.test(said) ? BY_ARGUMENT["productId"] : void 0;
26934
27877
  /** Where an argument means something else on one tool, or the tool's 404 needs its own word. */
26935
27878
  const BY_TOOL = {
27879
+ taiga_allow_license: licenseMiss("taiga_list_packages (productId) summarizes the product's licenses as its findings name them; a license with no finding there has nothing to allow or reopen."),
27880
+ taiga_list_licenses: licenseMiss("taiga_list_licenses without license lists the licenses in use, and taiga_list_packages (productId) one product's, spelled as their findings spell them."),
26936
27881
  taiga_resolve_action: () => "taiga_list_action_required lists the items with their IDs.",
26937
27882
  taiga_get_sbom: () => "An SBOM exists once a sweep has inventoried that repository, where the licenses feature is enabled; taiga_list_packages (linkId) says whether it has packages, and taiga_list_product_repositories lists the links.",
26938
27883
  taiga_get_notices: () => "Notices exist once a sweep has inventoried that repository, where the licenses feature is enabled; taiga_list_product_repositories lists the links.",
@@ -27409,6 +28354,8 @@ const PACKAGE_ROW_FIELDS = [
27409
28354
  "scopeReason",
27410
28355
  "parentReason",
27411
28356
  "sourcePath",
28357
+ "baseImage",
28358
+ "imageLayer",
27412
28359
  "productRepositoryId",
27413
28360
  "detectedAt"
27414
28361
  ];
@@ -27430,7 +28377,10 @@ const FINDING_ROW_FIELDS = [
27430
28377
  "tier",
27431
28378
  "kevListedAt",
27432
28379
  "malicious",
28380
+ "baseMove",
28381
+ "baseImageMove",
27433
28382
  "removal",
28383
+ "replaceable",
27434
28384
  "status",
27435
28385
  "title",
27436
28386
  "ecosystem",
@@ -27559,11 +28509,19 @@ function withFixability(row) {
27559
28509
  fixNote: reason === "not_shipped" ? "No longer applies: the package is only a build for platforms the repository does not deploy to. Nothing to do." : reason === "license_approved" ? "No longer applies: its license is on the organization's approved list. Nothing to do." : reason === "license_undecided" ? "No longer applies: its license can no longer be judged against the list (some of what it can stand for is approved). Nothing to do." : reason === "superseded" ? "Replaced by the repository's finding of its license, which lists every package under it. Nothing to do here." : "Resolved: the sweep no longer reports it. Nothing to do."
27560
28510
  };
27561
28511
  }
27562
- if (row["kind"] === "license_violation") return {
28512
+ if (row["kind"] === "license_violation" && row["replaceable"] === true) return {
27563
28513
  ...row,
27564
- fixable: false,
27565
- fixNote: "A license violation has no fix version: approve the license (taiga_change_license), ignore the finding, or replace the packages under it; taiga_fix_finding refuses it."
28514
+ fixable: true,
28515
+ fixNote: "taiga_fix_finding raises an initiative that replaces the packages under this license with ones whose license, and their own dependencies' licenses, are on the approved list. It never approves the license: that is taiga_change_license, or ignoring the finding."
27566
28516
  };
28517
+ if (row["kind"] === "license_violation") {
28518
+ const status = String(row["status"]);
28519
+ return {
28520
+ ...row,
28521
+ fixable: false,
28522
+ fixNote: status === "remediating" ? "Being fixed: an initiative replaces the packages under this license, and its plan may be waiting for a person's review. taiga_list_initiatives with query \"Replace the\" finds it; follow it there." : status === "open" ? "This Taiga does not replace the packages under a license yet, so taiga_fix_finding refuses it: approve the license (taiga_change_license) or ignore the finding." : `taiga_fix_finding takes only an open license finding, and this one is ${status}.`
28523
+ };
28524
+ }
27567
28525
  const dependency = row["kind"] === "vulnerable_dependency" || row["kind"] === "outdated_dependency";
27568
28526
  const signals = {
27569
28527
  malicious: typeof row["malicious"] === "boolean" ? row["malicious"] : null,
@@ -27587,14 +28545,37 @@ function withFixability(row) {
27587
28545
  fixable: true,
27588
28546
  fixNote: "A malicious package: taiga_fix_finding removes it (or replaces it), with every advisory on that version."
27589
28547
  } : row;
28548
+ if (typeof row["baseMove"] === "boolean" && isAgentRemediable({ remediableBy: typeof row["remediableBy"] === "string" ? row["remediableBy"] : null })) return row["baseMove"] === true && row["status"] === "open" ? {
28549
+ ...row,
28550
+ fixable: true,
28551
+ fixNote: "taiga_fix_finding moves the base images it is in to the builds their tags name now, the FROM line alone, with every other advisory that move removes."
28552
+ } : {
28553
+ ...row,
28554
+ fixable: false,
28555
+ fixNote: "Inside a base image no newer build of which removes it yet: it is fixed by a base move, not a package bump, and is held until one does."
28556
+ };
27590
28557
  if (dependency && !row["recommendedVersion"] && row["remediableBy"] === "coding_agent") return {
27591
28558
  ...row,
27592
28559
  fixable: false,
27593
- fixNote: "No fixed version is known yet: taiga_fix_finding refuses it until one is published."
28560
+ fixNote: noVersionNote(row["data"])
27594
28561
  };
27595
28562
  return row;
27596
28563
  }
27597
28564
  /**
28565
+ * Why a dependency has no version to move to. A language runtime declared as
28566
+ * a container image moves only to a release the image has published
28567
+ * (PRO-3729), so its vendor's fix can be out while Fix is refused: the
28568
+ * finding's runtime data says so where the row carries it (taiga_get_finding).
28569
+ */
28570
+ function noVersionNote(data) {
28571
+ const runtime = data?.runtime;
28572
+ const fixedIn = typeof runtime?.["fixedIn"] === "string" ? runtime["fixedIn"] : null;
28573
+ if (fixedIn) return `Fixed in ${fixedIn}, but there is no release to move to yet: the image the repository declares has not published it, or, where the line has ended, a supported line (the summary says which). taiga_fix_finding refuses it until there is one.`;
28574
+ if (runtime && (typeof runtime["newest"] === "string" || "endOfLife" in runtime)) return "A newer release is out, but the image the repository declares has not published one to move to yet (the summary names the image): taiga_fix_finding refuses it until it does.";
28575
+ if (runtime) return "No fixed release is known yet, or, where the line has ended, the image the repository declares has not published a supported line (the summary says which): taiga_fix_finding refuses it until there is one.";
28576
+ return "No version to move to yet: no fixed version is known, or for a language runtime the image the repository declares has not published one (taiga_get_finding says which). taiga_fix_finding refuses it until there is one.";
28577
+ }
28578
+ /**
27598
28579
  * Dev-only, runtime or unknown, said on a dependency row (PRO-2861) in place of the raw per-lockfile groups
27599
28580
  * the API carries: the groups need the lockfile name to read, which `dependencyScope` does once here.
27600
28581
  */
@@ -27650,7 +28631,10 @@ const ADVISORY_KEPT = [
27650
28631
  "summary",
27651
28632
  "details",
27652
28633
  "severity",
27653
- "published"
28634
+ "severitySource",
28635
+ "cvssScore",
28636
+ "published",
28637
+ "baseImage"
27654
28638
  ];
27655
28639
  const ADVISORY_TEXT_LIMIT = 1500;
27656
28640
  /** The packages of a licence finding shown without full: true (PRO-3394); the rest are counted. */
@@ -27691,6 +28675,36 @@ const SWEEP_FIELDS = [
27691
28675
  "createdAt",
27692
28676
  "completedAt"
27693
28677
  ];
28678
+ /**
28679
+ * The images a sweep's shipped images build on that it could not read, and
28680
+ * why (PRO-4073): a base nobody read is no clean base. Only where the API
28681
+ * returns the list (null with the containerImages flag off, or from a sweep
28682
+ * that listed none).
28683
+ */
28684
+ const BASE_IMAGES_SHOWN = 10;
28685
+ function notReadBases(sweep) {
28686
+ const bases = sweep.baseImages ?? [];
28687
+ const notRead = bases.filter((b) => b.state === "not_read" || b.packages?.state === "not_read");
28688
+ const read = bases.flatMap((b) => b.packages?.state === "read" ? [{
28689
+ image: b.image,
28690
+ path: b.path,
28691
+ os: b.os ?? null,
28692
+ vulnerabilities: b.packages.vulnerabilities,
28693
+ removedByMoving: b.packages.candidate?.removes ?? 0,
28694
+ noDistroFix: b.packages.noDistroFix ?? null,
28695
+ movesTo: b.packages.candidate?.image ?? null
28696
+ }] : []);
28697
+ return {
28698
+ ...read.length > 0 ? {
28699
+ baseImages: read.slice(0, BASE_IMAGES_SHOWN),
28700
+ ...read.length > BASE_IMAGES_SHOWN ? { baseImagesCount: read.length } : {}
28701
+ } : {},
28702
+ ...notRead.length > 0 ? {
28703
+ baseImagesNotRead: notRead.slice(0, BASE_IMAGES_SHOWN),
28704
+ ...notRead.length > BASE_IMAGES_SHOWN ? { baseImagesNotReadCount: notRead.length } : {}
28705
+ } : {}
28706
+ };
28707
+ }
27694
28708
  /** A policy violation in a list: what it is, how bad, and what is being done about it. */
27695
28709
  const VIOLATION_FIELDS = [
27696
28710
  "id",
@@ -27747,11 +28761,51 @@ function standingSummary(standing) {
27747
28761
  resolvedViolations: resolved
27748
28762
  };
27749
28763
  }
28764
+ /**
28765
+ * How the backlog covers the controls that need a feature (PRO-3860), one row
28766
+ * each: the live initiatives that build it, or why it is deferred, and whether
28767
+ * nothing covers it. Empty before a backlog answered for any. It says what is
28768
+ * planned, never that a control is met.
28769
+ */
28770
+ function controlCoverageList(controls) {
28771
+ return (Array.isArray(controls) ? controls : []).flatMap((control) => {
28772
+ const coverage = control["coverage"];
28773
+ if (!coverage || typeof coverage !== "object") return [];
28774
+ const initiatives = Array.isArray(coverage["initiatives"]) ? coverage["initiatives"] : [];
28775
+ return [{
28776
+ controlId: control["controlId"],
28777
+ state: control["state"],
28778
+ planned: plannedFor(coverage),
28779
+ uncovered: coverage["uncovered"] === true,
28780
+ ...initiatives.length > 0 ? { initiatives: initiatives.map((item) => ({
28781
+ id: item["id"],
28782
+ title: item["title"]
28783
+ })) } : {},
28784
+ ...coverage["deferredReason"] ? { deferredReason: coverage["deferredReason"] } : {}
28785
+ }];
28786
+ });
28787
+ }
28788
+ /**
28789
+ * Whether the backlog plans a control: yes when a live initiative builds it, later when it deferred it with a
28790
+ * reason, no otherwise. A deferred control reads "planned later" rather than only "not assessed" (PRO-3860).
28791
+ */
28792
+ function plannedFor(coverage) {
28793
+ if (Array.isArray(coverage["initiatives"]) && coverage["initiatives"].length > 0) return "yes";
28794
+ return coverage["deferredReason"] ? "later" : "no";
28795
+ }
28796
+ /** The coverage, when the backlog answered for any control, for the default and full answers. */
28797
+ function controlCoveragePart(controls) {
28798
+ const coverage = controlCoverageList(controls);
28799
+ return coverage.length > 0 ? { controlCoverage: coverage } : {};
28800
+ }
27750
28801
  const findingId = idInput("finding", "taiga_list_findings gives it");
27751
28802
  /** Open findings by severity and kind, and the latest sweeps, as the first page of findings carries them. */
27752
28803
  async function sweepOverview(client, productId) {
27753
28804
  const [summary, sweeps] = await Promise.all([client.getMaintenanceSummary(productId), client.listMaintenanceSweeps(productId, { limit: 3 })]);
27754
- const latest = pickFields(sweeps.data, SWEEP_FIELDS);
28805
+ const latest = sweeps.data.map((sweep) => ({
28806
+ ...pickFields([sweep], SWEEP_FIELDS)[0],
28807
+ ...notReadBases(sweep)
28808
+ }));
27755
28809
  const { lastRun: _lastRun, lastRuns: _lastRuns, topFindings: _top, violatedControls: _controls, ...counts } = summary.data;
27756
28810
  return {
27757
28811
  ...counts,
@@ -27759,6 +28813,20 @@ async function sweepOverview(client, productId) {
27759
28813
  ...latest.length === 0 ? { note: "This product has never been swept: no findings is not an all-clear." } : {}
27760
28814
  };
27761
28815
  }
28816
+ /** The organization's licence read takes an id this long at most, as `organizationLicenseQuery` does. */
28817
+ const ORGANIZATION_LICENSE_MAX = 200;
28818
+ /**
28819
+ * Why a license is allowed in a product, in a license's words, as the web's
28820
+ * Allow dialog offers them (PRO-3727); each is stored as the ignore reason
28821
+ * beside it.
28822
+ */
28823
+ const LICENSE_ALLOW_REASON_TO_IGNORE = {
28824
+ accepted: "accepted_risk",
28825
+ not_handed_out: "not_reachable",
28826
+ no_alternative: "wont_fix",
28827
+ read_wrong: "false_positive"
28828
+ };
28829
+ const LICENSE_ALLOW_REASONS = Object.keys(LICENSE_ALLOW_REASON_TO_IGNORE);
27762
28830
  function registerMaintenanceTools(server, client) {
27763
28831
  if (client.hasCapability("tenant:settings")) server.registerTool("taiga_change_license", {
27764
28832
  title: "Approve or remove a license",
@@ -27846,10 +28914,18 @@ function registerMaintenanceTools(server, client) {
27846
28914
  }));
27847
28915
  server.registerTool("taiga_list_licenses", {
27848
28916
  title: "List the organization's licenses",
27849
- description: "List the organization's approved license list and every license in use across its products, approved or not, with packages, products and repositories. With license, where that one is used: products, repositories and what brings its packages in. NOT_FOUND where the licenses feature is off.",
27850
- inputSchema: object({ license: string$2().trim().min(1).max(200).optional().describe("An SPDX license id: where it is used, instead of the list") }),
28917
+ description: "List the organization's approved license list and every license in use across its products, approved or not, with packages, products and repositories. With license, where that one is used: products, repositories, what brings its packages in, and its approved-list history. With license and productId, that license in one product: its standing against the list, what it asks, when to allow it there, how many packages ship, and what brings them in. Decide it there with taiga_allow_license. NOT_FOUND where the licenses feature is off.",
28918
+ inputSchema: object({
28919
+ license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX).optional().describe("An SPDX license id or expression: where it is used, instead of the list"),
28920
+ productId: productId.optional().describe("With license: that license in this product only (taiga_list_products gives it)")
28921
+ }),
27851
28922
  annotations: READ_ONLY
27852
- }, (params) => runTool(async () => toolResult(params.license ? (await client.getOrganizationLicense(params.license)).data : (await client.getOrganizationLicenses()).data)));
28923
+ }, (params) => runTool(async () => {
28924
+ if (params.productId !== void 0 && params.license === void 0) return toolError("VALIDATION_ERROR", "Name the license to read in this product (license).");
28925
+ if (params.license === void 0) return toolResult((await client.getOrganizationLicenses()).data);
28926
+ if (params.productId === void 0 && params.license.length > ORGANIZATION_LICENSE_MAX) return toolError("VALIDATION_ERROR", `A license id is at most ${ORGANIZATION_LICENSE_MAX} characters; name a product (productId) to read a longer expression.`);
28927
+ return toolResult(params.productId !== void 0 ? (await client.getProductLicense(params.productId, params.license)).data : (await client.getOrganizationLicense(params.license)).data);
28928
+ }));
27853
28929
  server.registerTool("taiga_get_sbom", {
27854
28930
  title: "Download a repository SBOM",
27855
28931
  description: "Download one repository's software bill of materials (CycloneDX 1.6). Large; taiga_list_packages summarizes. NOT_FOUND where the licenses feature is off, or before a sweep has read the repository.",
@@ -27916,7 +28992,8 @@ function registerMaintenanceTools(server, client) {
27916
28992
  const own = violations.filter((violation) => String(violation["controlId"]).toUpperCase() === wanted);
27917
28993
  return toolResult({
27918
28994
  ...control,
27919
- ...control.state === "no_evidence" ? { meaning: "Not assessed: no finding or violation is recorded against it, and the documents were not found to show it met. That is no evidence either way, not a pass." } : {},
28995
+ ...control.state === "no_evidence" ? { meaning: (control.coverage && plannedFor(control.coverage) === "later" ? `Planned later: the backlog deferred it (${String(control.coverage.deferredReason).replace(/\.$/, "")}). ` : "") + "Not assessed: no finding or violation is recorded against it, and the documents were not found to show it met. That is no evidence either way, not a pass." } : {},
28996
+ ...control.coverage ? { coverageNote: "coverage is how the backlog plans for it: the live initiatives that build it, or why it is deferred. It says what is planned, not that the control is met." } : {},
27920
28997
  ...control.assessment == null && (control.state === "breached" || control.state === "diverged") ? { assessmentNote: "No met or not-applicable verdict is shown: an open finding or violation against the control outranks one." } : {},
27921
28998
  ...Number(control.findingCount ?? 0) > 0 ? { findingsNote: `${String(control.findingCount)} open scanner finding${control.findingCount === 1 ? "" : "s"} breach it; taiga_list_findings (productId) lists them with the controls each breaches. violations below are the documents' only.` } : {},
27922
28999
  violations: pickFields(own, VIOLATION_FIELDS),
@@ -27939,6 +29016,7 @@ function registerMaintenanceTools(server, client) {
27939
29016
  ...wording,
27940
29017
  ...standing,
27941
29018
  controls: listed,
29019
+ ...controlCoveragePart(controls),
27942
29020
  divergences: cutLongText(violations),
27943
29021
  resolvedDivergences: cutLongText(resolvedAll.slice(0, FULL_RESOLVED)),
27944
29022
  ...resolvedAll.length > FULL_RESOLVED ? { resolvedNote: `The first ${FULL_RESOLVED} of ${resolvedAll.length} resolved violations.` } : {}
@@ -27962,7 +29040,8 @@ function registerMaintenanceTools(server, client) {
27962
29040
  "findingsByTier",
27963
29041
  "plannedCount"
27964
29042
  ]),
27965
- violations: pickFields(violations, VIOLATION_FIELDS)
29043
+ violations: pickFields(violations, VIOLATION_FIELDS),
29044
+ ...controlCoveragePart(controls)
27966
29045
  });
27967
29046
  }));
27968
29047
  if (!client.hasCapability("products:write")) return;
@@ -27988,9 +29067,31 @@ function registerMaintenanceTools(server, client) {
27988
29067
  const finding = (await client.updateFinding(params.findingId, body)).data;
27989
29068
  return toolResult(findingRows([finding], ["ignoreNote"])[0]);
27990
29069
  }));
29070
+ server.registerTool("taiga_allow_license", {
29071
+ title: "Allow or reopen a license in a product",
29072
+ description: "Allow a license in one product without approving it for the organization: every license finding of it there that is open, or whose ignore lapsed, is ignored with one reason, as the web's Allow in this product does; one already ignored keeps its reason and expiry, so reopen first to change them. allowed: false reopens them. Another product using the license still raises its finding; to approve it everywhere, use taiga_change_license. Allowing a license is the person's decision. NOT_FOUND when the product has no license finding for it.",
29073
+ inputSchema: object({
29074
+ productId,
29075
+ license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX).describe("The license as the product's license findings or taiga_list_packages name it, such as MPL-2.0"),
29076
+ allowed: boolean().describe("true allows the license in this product, false reopens its findings"),
29077
+ reason: _enum(LICENSE_ALLOW_REASONS).optional().describe("Why it is allowed; required when allowing. accepted: reviewed and allowed here, usually for a limited time; not_handed_out: the product runs only internally or on your own servers, its code is never given to anyone; no_alternative: no package under an approved license does the job yet; read_wrong: the packages are not actually under this license"),
29078
+ note: string$2().trim().max(280).optional().describe("A short justification, kept for audit"),
29079
+ expiresAt: string$2().datetime({ offset: true }).meta({ pattern: void 0 }).optional().describe("When the allowance lapses and the findings come back if still there (ISO 8601, in the future)")
29080
+ }),
29081
+ annotations: OPERATE_SETTING
29082
+ }, (params) => runTool(async () => {
29083
+ if (!params.allowed) return toolResult((await client.reopenProductLicense(params.productId, params.license)).data);
29084
+ if (params.reason === void 0) return toolError("VALIDATION_ERROR", "Say why the license is allowed (reason).");
29085
+ return toolResult((await client.allowProductLicense(params.productId, {
29086
+ license: params.license,
29087
+ ignoreReason: LICENSE_ALLOW_REASON_TO_IGNORE[params.reason],
29088
+ ...params.note !== void 0 ? { ignoreNote: params.note } : {},
29089
+ ...params.expiresAt !== void 0 ? { ignoreExpiresAt: params.expiresAt } : {}
29090
+ })).data);
29091
+ }));
27991
29092
  server.registerTool("taiga_fix_finding", {
27992
29093
  title: "Fix a finding",
27993
- description: "Raise an initiative that fixes a finding; it ends in a pull request, which merges on its own only where the product's auto-merge allows it. wholeGroup: true fixes, in that repository, every open advisory on that package version in one bump, or every place the rule flagged. A malicious package is removed, with every advisory on that version, whatever wholeGroup says. Refused when no fixed version is known (fixable: false), the code is in a submodule, or a secret is only in history (rotate it outside Taiga).",
29094
+ description: "Raise an initiative that fixes a finding; it ends in a pull request, which merges on its own only where the product's auto-merge allows it. wholeGroup: true fixes, in that repository, every open advisory on that package version in one bump, or every place the rule flagged. A malicious package is removed, with every advisory on that version, whatever wholeGroup says. A license finding the row marks fixable has its packages replaced by ones on the approved list. A language runtime moves every file declaring it together, in a container image only to a tag the image has published. Refused when there is no version to move to (fixable: false; for a runtime, also while its image has not published the fix), the code is in a submodule, or a secret is only in history (rotate it outside Taiga).",
27994
29095
  inputSchema: object({
27995
29096
  productId,
27996
29097
  findingId,
@@ -28029,6 +29130,13 @@ function registerMaintenanceTools(server, client) {
28029
29130
  const body = { refuseIfFixedSinceAssessment: params.fixAgain !== true };
28030
29131
  return toolResult((params.controlId ? await client.remediatePolicyControl(params.productId, params.controlId, body) : await client.remediatePolicyViolation(params.productId, params.violationId, body)).data);
28031
29132
  }));
29133
+ if (!client.hasCapability("backlog:write")) return;
29134
+ server.registerTool("taiga_ship_notices", {
29135
+ title: "Ship open-source notices",
29136
+ description: "Raise the initiative that builds open-source notices into a product's Vite web app: every production build writes the license texts and NOTICE files of the packages in its browser bundle, and the app shows them at /legal/open-source. Taiga writes the brief from the inventory; the plan waits for review before it builds. One that is already open is handed back (created: false) rather than raised again. Refused where no repository of the product declares Vite.",
29137
+ inputSchema: object({ productId }),
29138
+ annotations: OPERATE
29139
+ }, (params) => runTool(async () => toolResult((await client.shipNotices(params.productId)).data)));
28032
29140
  }
28033
29141
  //#endregion
28034
29142
  //#region src/tools/sections.ts
@@ -28354,20 +29462,17 @@ async function setupReadiness(client, product, links) {
28354
29462
  const settings = product["settings"] ?? {};
28355
29463
  const importState = imported ? IMPORT_STATE[String(settings.importStatus)] ?? "not started" : void 0;
28356
29464
  const missing = blockers ? blockers.missingDocuments : null;
28357
- const openChecks = blockers ? blockers.openChecks.map((check) => check.criterion) : null;
28358
29465
  return {
28359
- ready: blockers ? missing.length === 0 && openChecks.length === 0 : null,
29466
+ ready: blockers ? missing.length === 0 : null,
28360
29467
  repository,
28361
29468
  ...imported ? { import: typeof settings.importStatus === "string" ? settings.importStatus : "pending" } : {},
28362
29469
  ...imported ? {} : { discoveryStep: full["discoveryStep"] ?? null },
28363
29470
  missingDocuments: missing,
28364
- openChecks,
28365
29471
  nextStep: nextStepFor({
28366
29472
  imported,
28367
29473
  importState,
28368
29474
  linked: repository?.linked ?? null,
28369
29475
  missing,
28370
- openChecks,
28371
29476
  product: full
28372
29477
  })
28373
29478
  };
@@ -28389,7 +29494,6 @@ function nextStepFor(state) {
28389
29494
  if (!state.missing) return setupNextStep(state.product);
28390
29495
  if (state.missing.includes("specification")) return "The specification comes from the Discovery conversation, held in the web: the person describes the product there. The other documents follow from it." + (state.linked === false ? " Linking a repository (taiga_link_repository) can wait until before the first build." : "");
28391
29496
  if (state.missing.length > 0) return `Still to publish: ${named(state.missing)}. taiga_generate_product_documents generates the missing ones (paid agent work: ask first); a draft is published with taiga_publish_product_document.`;
28392
- if (state.openChecks && state.openChecks.length > 0) return `The documents are published; the checks are stamped in the web, then ${finish}`;
28393
29497
  return `Every required document is published: ${finish}`;
28394
29498
  }
28395
29499
  //#endregion
@@ -29473,7 +30577,8 @@ const READY_BLOCKER_SAID = {
29473
30577
  bar_lowering: "a fix lowered a check instead of fixing the code",
29474
30578
  bar_lowering_unchecked: "Taiga could not check whether a fix lowered a check",
29475
30579
  criticals_unresolved: "Taiga's own review left a critical issue unresolved",
29476
- clean_checkout_failed: "the branch does not install or pass its tests from a clean checkout"
30580
+ clean_checkout_failed: "the branch does not install or pass its tests from a clean checkout",
30581
+ license_not_approved: "a package it adds has a licence that is not on your approved list"
29477
30582
  };
29478
30583
  /** The first sentence of a run's error, short enough for a row. */
29479
30584
  function errorSaid(error) {
@@ -30231,6 +31336,10 @@ function standing(item) {
30231
31336
  const life = item["lifecycle"];
30232
31337
  const run = life?.latestBuild;
30233
31338
  const situation = situationOf(item);
31339
+ const open = (life?.openPullRequests ?? []).flatMap((pull) => typeof pull.repositoryFullName === "string" && typeof pull.prNumber === "number" ? [{
31340
+ repository: pull.repositoryFullName,
31341
+ prNumber: pull.prNumber
31342
+ }] : []);
30234
31343
  return {
30235
31344
  column: typeof item["state"] === "string" ? item["state"] : null,
30236
31345
  turn: typeof life?.turn === "string" ? life.turn : null,
@@ -30238,7 +31347,8 @@ function standing(item) {
30238
31347
  latestRun: run && typeof run.id === "string" ? {
30239
31348
  id: run.id,
30240
31349
  ...typeof run.prNumber === "number" ? { prNumber: run.prNumber } : {}
30241
- } : null
31350
+ } : null,
31351
+ ...open.length > 1 ? { pullRequests: open } : {}
30242
31352
  };
30243
31353
  }
30244
31354
  /** The move each call to action is, in the tools' words. */
@@ -30421,7 +31531,8 @@ const PLAN_SUMMARY_FIELDS = [
30421
31531
  "backlogItemId",
30422
31532
  "version",
30423
31533
  "status",
30424
- "createdAt"
31534
+ "createdAt",
31535
+ "controls"
30425
31536
  ];
30426
31537
  const PLAN_STEP_SUMMARY_FIELDS = [
30427
31538
  "id",
@@ -30568,10 +31679,12 @@ function registerBacklogReads(server, client) {
30568
31679
  ...revised
30569
31680
  });
30570
31681
  const steps = Array.isArray(plan.steps) ? plan.steps : [];
31682
+ const several = (plan.repositories?.length ?? 0) > 1;
30571
31683
  return toolResult({
30572
31684
  ...pickFields([plan], PLAN_SUMMARY_FIELDS)[0],
31685
+ ...several ? { repositories: plan.repositories } : {},
30573
31686
  ...revised,
30574
- steps: pickFields(steps, PLAN_STEP_SUMMARY_FIELDS)
31687
+ steps: several ? pickFields(steps, [...PLAN_STEP_SUMMARY_FIELDS, "repositoryName"]) : pickFields(steps, PLAN_STEP_SUMMARY_FIELDS)
30575
31688
  });
30576
31689
  }));
30577
31690
  }
@@ -31527,7 +32640,7 @@ async function licenseReviews(client, productId, now) {
31527
32640
  packages: data["packages"],
31528
32641
  ...Number.isNaN(created) ? {} : { waitingDays: Math.floor((now - created) / DAY_MS) },
31529
32642
  readWith: "taiga_list_licenses (license)",
31530
- decideWith: "taiga_change_license (license, action: 'approve'); dismissing it is only in the web"
32643
+ decideWith: "taiga_change_license (license, action: 'approve') for every product" + (client.hasCapability("products:write") ? ", or taiga_allow_license (productId, license) for this one" : "") + "; dismissing it is only in the web"
31531
32644
  };
31532
32645
  }).filter((r) => productId === void 0 || r.productId === productId),
31533
32646
  truncated: result.truncated === true
@@ -48294,7 +49407,7 @@ function registerSetupTools(server, client) {
48294
49407
  }, (params) => runTool(async () => toolResult((await client.refreshRealityDocuments(params.productId, params.docTypes)).data)));
48295
49408
  server.registerTool("taiga_finish_discovery", {
48296
49409
  title: "Finish Discovery",
48297
- description: "Finish a product's setup: lock its documents and start planning its first initiatives. Refused while documents are missing (the refusal lists them) or a check is not stamped in the web.",
49410
+ description: "Finish a product's setup: lock its documents and start planning its first initiatives. Refused while documents are missing (the refusal lists them).",
48298
49411
  inputSchema: object({ productId }),
48299
49412
  annotations: OPERATE
48300
49413
  }, (params) => runTool(async () => {
@@ -48338,6 +49451,7 @@ function withStrictInputs(server, client, registered, toolsets, left) {
48338
49451
  const inputs = /* @__PURE__ */ new Map();
48339
49452
  const register = server.registerTool.bind(server);
48340
49453
  server.registerTool = ((name, config, callback) => {
49454
+ if (DELIVERY_TOOLS.has(name) && client.getIntrospection()?.deliveryIncluded === false) return void 0;
48341
49455
  const toolset = TOOLSET_OF[name];
48342
49456
  if (name !== "taiga_whoami") {
48343
49457
  if (!toolset) throw new Error(`Tool ${name}: no toolset in toolsets.ts`);
@@ -48558,6 +49672,7 @@ const PAID_WORK = /* @__PURE__ */ new Set([
48558
49672
  "taiga_request_changes",
48559
49673
  "taiga_request_initiatives",
48560
49674
  "taiga_resume_initiative",
49675
+ "taiga_ship_notices",
48561
49676
  "taiga_retry_knowledge",
48562
49677
  "taiga_review_plan",
48563
49678
  "taiga_set_auto_build",