@odla-ai/cli 0.33.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -440,10 +440,10 @@ function isManagedDevVar(line) {
440
440
  const match = line.match(/^\s*(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/);
441
441
  return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
442
442
  }
443
- function writePrivateText(path, text2) {
443
+ function writePrivateText(path, text3) {
444
444
  (0, import_node_fs5.mkdirSync)((0, import_node_path3.dirname)(path), { recursive: true });
445
445
  const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
446
- (0, import_node_fs5.writeFileSync)(temporary, text2, { mode: 384 });
446
+ (0, import_node_fs5.writeFileSync)(temporary, text3, { mode: 384 });
447
447
  (0, import_node_fs5.chmodSync)(temporary, 384);
448
448
  (0, import_node_fs5.renameSync)(temporary, path);
449
449
  }
@@ -580,7 +580,7 @@ async function freshHandshake(ctx, waitMs) {
580
580
  }
581
581
  function cachedGrantCovers(cached, required) {
582
582
  if (required.optionalProjectCapabilities.length === 0) return true;
583
- return required.projectIds.every((id) => cached.projectIds?.includes(id)) && required.optionalProjectCapabilities.every(
583
+ return required.projectIds.every((id2) => cached.projectIds?.includes(id2)) && required.optionalProjectCapabilities.every(
584
584
  (capability) => cached.optionalProjectCapabilities?.includes(capability)
585
585
  );
586
586
  }
@@ -769,8 +769,8 @@ var init_admin_ai_auth = __esm({
769
769
  // src/principal-presentation.ts
770
770
  function unresolvedPrincipalLabel(credentialKind2, principalId) {
771
771
  const kind = typeof credentialKind2 === "string" ? credentialKind2.trim() : "";
772
- const id = typeof principalId === "string" ? principalId.trim() : "";
773
- const audit = kind && id ? `${kind}:${id}` : kind || id;
772
+ const id2 = typeof principalId === "string" ? principalId.trim() : "";
773
+ const audit = kind && id2 ? `${kind}:${id2}` : kind || id2;
774
774
  return `Unknown principal${audit ? ` [${audit}]` : ""}`;
775
775
  }
776
776
  var init_principal_presentation = __esm({
@@ -788,38 +788,38 @@ function adminAiAuditQuery(filters) {
788
788
  }
789
789
  return `?limit=${filters.limit}`;
790
790
  }
791
- async function readAdminAiAudit(request2) {
792
- const response2 = await request2.fetch(`${request2.platform}/registry/platform/ai-audit${request2.query}`, {
793
- headers: request2.headers
791
+ async function readAdminAiAudit(request3) {
792
+ const response2 = await request3.fetch(`${request3.platform}/registry/platform/ai-audit${request3.query}`, {
793
+ headers: request3.headers
794
794
  });
795
795
  const body = await responseBody(response2);
796
796
  if (!response2.ok) throw new Error(apiError(response2.status, body));
797
- if (request2.json) {
798
- request2.stdout.log(JSON.stringify(body, null, 2));
797
+ if (request3.json) {
798
+ request3.stdout.log(JSON.stringify(body, null, 2));
799
799
  return;
800
800
  }
801
801
  const events = isRecord(body) && Array.isArray(body.events) ? body.events.filter(isRecord) : [];
802
- request2.stdout.log("when change target before -> after actor");
802
+ request3.stdout.log("when change target before -> after actor");
803
803
  for (const event of events) {
804
804
  const before = isRecord(event.oldPolicy) ? event.oldPolicy : void 0;
805
805
  const after = isRecord(event.newPolicy) ? event.newPolicy : void 0;
806
- const route2 = before && after ? `${String(before.provider)}/${String(before.model)}@v${String(before.version)} -> ${String(after.provider)}/${String(after.model)}@v${String(after.version)}` : "value not retained";
807
- request2.stdout.log([
806
+ const route3 = before && after ? `${String(before.provider)}/${String(before.model)}@v${String(before.version)} -> ${String(after.provider)}/${String(after.model)}@v${String(after.version)}` : "value not retained";
807
+ request3.stdout.log([
808
808
  timestamp(event.createdAt),
809
809
  String(event.changeKind ?? ""),
810
810
  String(event.purpose ?? event.provider ?? ""),
811
- route2,
811
+ route3,
812
812
  unresolvedPrincipalLabel(event.actorType, event.actorId)
813
813
  ].join(" "));
814
814
  }
815
815
  }
816
816
  async function responseBody(response2) {
817
- const text2 = await response2.text();
818
- if (!text2) return {};
817
+ const text3 = await response2.text();
818
+ if (!text3) return {};
819
819
  try {
820
- return JSON.parse(text2);
820
+ return JSON.parse(text3);
821
821
  } catch {
822
- return { message: text2.slice(0, 300) };
822
+ return { message: text3.slice(0, 300) };
823
823
  }
824
824
  }
825
825
  function apiError(status, body) {
@@ -874,14 +874,14 @@ function adminAiUsageQuery(filters) {
874
874
  const query = params.toString();
875
875
  return query ? `?${query}` : "";
876
876
  }
877
- async function readAdminAiUsage(request2) {
878
- const res = await request2.fetch(`${request2.platform}/registry/platform/ai-usage${request2.query}`, {
879
- headers: request2.headers
877
+ async function readAdminAiUsage(request3) {
878
+ const res = await request3.fetch(`${request3.platform}/registry/platform/ai-usage${request3.query}`, {
879
+ headers: request3.headers
880
880
  });
881
881
  const body = await responseBody2(res);
882
882
  if (!res.ok) throw new Error(apiError2("read platform AI usage", res.status, body));
883
- if (request2.json) request2.stdout.log(JSON.stringify(body, null, 2));
884
- else printUsage(body, request2.stdout);
883
+ if (request3.json) request3.stdout.log(JSON.stringify(body, null, 2));
884
+ else printUsage(body, request3.stdout);
885
885
  }
886
886
  function usageLimit(value2) {
887
887
  if (!Number.isSafeInteger(value2) || value2 < 1 || value2 > 500) {
@@ -935,12 +935,12 @@ function timestamp2(value2) {
935
935
  return Number.isFinite(date.valueOf()) ? date.toISOString() : "";
936
936
  }
937
937
  async function responseBody2(res) {
938
- const text2 = await res.text();
939
- if (!text2) return {};
938
+ const text3 = await res.text();
939
+ if (!text3) return {};
940
940
  try {
941
- return JSON.parse(text2);
941
+ return JSON.parse(text3);
942
942
  } catch {
943
- return { message: text2.slice(0, 300) };
943
+ return { message: text3.slice(0, 300) };
944
944
  }
945
945
  }
946
946
  function apiError2(action2, status, body) {
@@ -1123,12 +1123,12 @@ function catalogModels(body) {
1123
1123
  return body.catalog.models.filter((value2) => isRecord3(value2) && typeof value2.id === "string" && typeof value2.provider === "string");
1124
1124
  }
1125
1125
  async function responseBody3(res) {
1126
- const text2 = await res.text();
1127
- if (!text2) return {};
1126
+ const text3 = await res.text();
1127
+ if (!text3) return {};
1128
1128
  try {
1129
- return JSON.parse(text2);
1129
+ return JSON.parse(text3);
1130
1130
  } catch {
1131
- return { message: text2.slice(0, 300) };
1131
+ return { message: text3.slice(0, 300) };
1132
1132
  }
1133
1133
  }
1134
1134
  function apiError3(action2, status, body) {
@@ -1276,7 +1276,7 @@ function calendarServiceConfig(cfg, env) {
1276
1276
  if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
1277
1277
  const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
1278
1278
  if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
1279
- const availability = unique(configured.map((id) => id.trim()));
1279
+ const availability = unique(configured.map((id2) => id2.trim()));
1280
1280
  return {
1281
1281
  provider: "google",
1282
1282
  access: "book",
@@ -1325,7 +1325,7 @@ function validateCalendarConfig(cfg, envs, services, path) {
1325
1325
  if (ids.length > 10) {
1326
1326
  throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
1327
1327
  }
1328
- if (ids.some((id) => !safeText2(id, 1024))) {
1328
+ if (ids.some((id2) => !safeText2(id2, 1024))) {
1329
1329
  throw new Error(`${path}: calendar.google.${availabilityKey}.${env} contains an invalid calendar id`);
1330
1330
  }
1331
1331
  }
@@ -1480,6 +1480,182 @@ var init_integration_validation = __esm({
1480
1480
  }
1481
1481
  });
1482
1482
 
1483
+ // src/monitoring-validation.ts
1484
+ function validateMonitoringConfig(cfg, envs, services, path) {
1485
+ if (!cfg.o11y) return;
1486
+ if (!record(cfg.o11y)) fail(path, "o11y must be an object");
1487
+ only(cfg.o11y, ["service", "endpoint", "version", "monitoring"], `${path}: o11y`);
1488
+ const monitoring = cfg.o11y.monitoring;
1489
+ if (!monitoring) return;
1490
+ if (!services.includes("o11y")) fail(path, 'o11y.monitoring requires "o11y" in services');
1491
+ if (!record(monitoring)) fail(path, "o11y.monitoring must be an object");
1492
+ only(monitoring, ["probes", "slos", "notifications"], `${path}: o11y.monitoring`);
1493
+ if (monitoring.probes !== void 0 && (!Array.isArray(monitoring.probes) || monitoring.probes.length > 50)) {
1494
+ fail(path, "o11y.monitoring.probes must contain at most 50 probes");
1495
+ }
1496
+ const probeIds = /* @__PURE__ */ new Set();
1497
+ (monitoring.probes ?? []).forEach((probe, index) => validateProbe(probe, index, envs, path, probeIds));
1498
+ if (!Array.isArray(monitoring.slos) || monitoring.slos.length < 1 || monitoring.slos.length > 50) {
1499
+ fail(path, "o11y.monitoring.slos must contain 1 through 50 SLOs");
1500
+ }
1501
+ const sloIds = /* @__PURE__ */ new Set();
1502
+ monitoring.slos.forEach((slo, index) => validateSlo(slo, index, path, probeIds, sloIds));
1503
+ if (monitoring.notifications !== void 0) validateNotifications(monitoring.notifications, envs, path);
1504
+ }
1505
+ function validateProbe(value2, index, envs, path, ids) {
1506
+ const label = `${path}: o11y.monitoring.probes[${index}]`;
1507
+ if (!record(value2)) fail(label, "must be an object");
1508
+ only(value2, ["id", "route", "envs", "every", "timeout", "ready", "expect", "enabled"], label);
1509
+ if (!id(value2.id)) fail(label, "id must be lowercase letters, numbers, and hyphens");
1510
+ if (ids.has(value2.id)) fail(label, `id duplicates ${value2.id}`);
1511
+ ids.add(value2.id);
1512
+ if (!route(value2.route)) fail(label, "route must be a relative absolute path without credentials or a fragment");
1513
+ if (!CADENCES.has(String(value2.every))) fail(label, `every must be one of ${[...CADENCES].join(", ")}`);
1514
+ if (value2.timeout !== void 0 && (!Number.isSafeInteger(value2.timeout) || Number(value2.timeout) < 1e3 || Number(value2.timeout) > 6e4)) fail(label, "timeout must be an integer from 1000 through 60000");
1515
+ if (value2.envs !== void 0 && (!Array.isArray(value2.envs) || value2.envs.length < 1 || value2.envs.some((env) => typeof env !== "string" || !envs.includes(env) && env !== "prod"))) fail(label, "envs must contain configured environment names");
1516
+ if (value2.ready !== void 0) {
1517
+ if (!record(value2.ready) || !text(value2.ready.selector, 300)) fail(label, "ready.selector is required");
1518
+ only(value2.ready, ["selector"], `${label}.ready`);
1519
+ }
1520
+ if (!record(value2.expect)) fail(label, "expect must be an object");
1521
+ only(value2.expect, ["status", "titleIncludes", "textIncludes", "accessibility"], `${label}.expect`);
1522
+ if (!Number.isSafeInteger(value2.expect.status) || Number(value2.expect.status) < 100 || Number(value2.expect.status) > 599) fail(label, "expect.status must be an HTTP status from 100 through 599");
1523
+ if (value2.expect.titleIncludes !== void 0 && !text(value2.expect.titleIncludes, 300)) fail(label, "expect.titleIncludes is invalid");
1524
+ if (value2.expect.textIncludes !== void 0 && (!Array.isArray(value2.expect.textIncludes) || value2.expect.textIncludes.length > 10 || value2.expect.textIncludes.some((item) => !text(item, 500)))) fail(label, "expect.textIncludes must contain at most 10 bounded strings");
1525
+ if (value2.expect.accessibility !== void 0) validateAccessibility(value2.expect.accessibility, label);
1526
+ }
1527
+ function validateAccessibility(value2, label) {
1528
+ if (!Array.isArray(value2) || value2.length > 10) fail(label, "expect.accessibility must contain at most 10 assertions");
1529
+ for (const item of value2) {
1530
+ if (!record(item) || !text(item.role, 80) || !text(item.name, 300)) fail(label, "expect.accessibility entries need role and name");
1531
+ only(item, ["role", "name"], `${label}.expect.accessibility`);
1532
+ }
1533
+ }
1534
+ function validateSlo(value2, index, path, probes, ids) {
1535
+ const label = `${path}: o11y.monitoring.slos[${index}]`;
1536
+ if (!record(value2)) fail(label, "must be an object");
1537
+ only(value2, ["id", "name", "indicator", "target", "window", "alerts", "enabled"], label);
1538
+ if (!id(value2.id) || ids.has(value2.id)) fail(label, "id must be unique lowercase letters, numbers, and hyphens");
1539
+ ids.add(value2.id);
1540
+ if (value2.name !== void 0 && !text(value2.name, 160)) fail(label, "name is invalid");
1541
+ validateIndicator(value2.indicator, label, probes);
1542
+ if (typeof value2.target !== "number" || !Number.isFinite(value2.target) || value2.target <= 0 || value2.target >= 1) fail(label, "target must be a fraction greater than 0 and less than 1");
1543
+ if (!WINDOWS.has(String(value2.window))) fail(label, `window must be one of ${[...WINDOWS].join(", ")}`);
1544
+ if (value2.alerts !== void 0) validateAlerts(value2.alerts, label);
1545
+ }
1546
+ function validateIndicator(value2, label, probes) {
1547
+ if (!record(value2)) fail(label, "indicator must be an object");
1548
+ if (value2.type === "probe-success") {
1549
+ only(value2, ["type", "probes"], `${label}.indicator`);
1550
+ if (!Array.isArray(value2.probes) || value2.probes.length < 1) fail(label, "probe-success must select at least one probe");
1551
+ if (value2.probes.some((probe) => typeof probe !== "string" || !probes.has(probe))) fail(label, "indicator references an unknown probe");
1552
+ return;
1553
+ }
1554
+ if (value2.type !== "o11y-metric") fail(label, "indicator.type must be probe-success or o11y-metric");
1555
+ only(value2, ["type", "metric", "comparator", "threshold", "every", "observationWindow", "route"], `${label}.indicator`);
1556
+ if (!O11Y_METRICS.has(String(value2.metric))) fail(label, "indicator.metric is unsupported");
1557
+ if (!COMPARATORS.has(String(value2.comparator))) fail(label, "indicator.comparator is unsupported");
1558
+ if (typeof value2.threshold !== "number" || !Number.isFinite(value2.threshold)) fail(label, "indicator.threshold must be finite");
1559
+ if (!CADENCES.has(String(value2.every)) || !CADENCES.has(String(value2.observationWindow))) fail(label, "indicator cadence and observationWindow must be supported durations");
1560
+ if (value2.route !== void 0 && !routePattern(value2.route)) fail(label, "indicator.route must be an exact route template or trailing-* prefix");
1561
+ if ((value2.metric === "synthetic_success" || value2.metric === "synthetic_publish_to_visible") && value2.route !== void 0) fail(label, "synthetic indicators cannot select a route");
1562
+ }
1563
+ function validateAlerts(value2, label) {
1564
+ if (!record(value2)) fail(label, "alerts must be an object");
1565
+ only(value2, ["spike", "trend"], `${label}.alerts`);
1566
+ if (value2.spike !== void 0) {
1567
+ if (!record(value2.spike)) fail(label, "alerts.spike must be an object");
1568
+ only(value2.spike, ["badChecks", "withinChecks", "recoverAfter"], `${label}.alerts.spike`);
1569
+ const bad = positive(value2.spike.badChecks, 2), within = positive(value2.spike.withinChecks, 3), recover = positive(value2.spike.recoverAfter, 2);
1570
+ if (bad > within || within > 20 || recover > 20) fail(label, "alerts.spike requires badChecks <= withinChecks <= 20 and recoverAfter <= 20");
1571
+ }
1572
+ if (value2.trend !== void 0) {
1573
+ if (!record(value2.trend)) fail(label, "alerts.trend must be an object");
1574
+ only(value2.trend, ["burnRate", "shortWindow", "longWindow", "minBadChecks"], `${label}.alerts.trend`);
1575
+ const burn = value2.trend.burnRate ?? 1;
1576
+ if (typeof burn !== "number" || !Number.isFinite(burn) || burn <= 0 || burn > 1e3) fail(label, "alerts.trend.burnRate must be greater than 0");
1577
+ if (value2.trend.shortWindow !== void 0 && !SHORT.has(String(value2.trend.shortWindow))) fail(label, "alerts.trend.shortWindow is unsupported");
1578
+ if (value2.trend.longWindow !== void 0 && !LONG.has(String(value2.trend.longWindow))) fail(label, "alerts.trend.longWindow is unsupported");
1579
+ if (positive(value2.trend.minBadChecks, 2) > 100) fail(label, "alerts.trend.minBadChecks must be at most 100");
1580
+ }
1581
+ }
1582
+ function validateNotifications(value2, envs, path) {
1583
+ if (!record(value2)) fail(path, "o11y.monitoring.notifications must map environments to policies");
1584
+ for (const [env, policy] of Object.entries(value2)) {
1585
+ const label = `${path}: o11y.monitoring.notifications.${env}`;
1586
+ if (!envs.includes(env) && env !== "prod") fail(label, "is not a configured environment");
1587
+ if (!record(policy)) fail(label, "must be an object");
1588
+ only(policy, ["email", "timezone", "daily", "weekly"], label);
1589
+ if (!Array.isArray(policy.email) || policy.email.length < 1 || policy.email.length > 10 || policy.email.some((email) => !emailAddress(email))) fail(label, "email must contain 1 through 10 email addresses");
1590
+ if (!timezone(policy.timezone)) fail(label, "timezone must be an IANA timezone");
1591
+ if (policy.daily !== void 0 && policy.daily !== false && !clock(policy.daily)) fail(label, "daily must be HH:MM or false");
1592
+ if (policy.weekly !== void 0 && policy.weekly !== false) {
1593
+ if (!record(policy.weekly) || !DAYS.has(String(policy.weekly.day)) || !clock(policy.weekly.at)) fail(label, "weekly needs a weekday and HH:MM time");
1594
+ only(policy.weekly, ["day", "at"], `${label}.weekly`);
1595
+ }
1596
+ }
1597
+ }
1598
+ function fail(label, message2) {
1599
+ throw new Error(`${label}: ${message2}`);
1600
+ }
1601
+ function record(value2) {
1602
+ return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
1603
+ }
1604
+ function only(value2, keys, label) {
1605
+ const extra = Object.keys(value2).find((key) => !keys.includes(key));
1606
+ if (extra) fail(label, `${extra} is not supported`);
1607
+ }
1608
+ function id(value2) {
1609
+ return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
1610
+ }
1611
+ function text(value2, max) {
1612
+ return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
1613
+ }
1614
+ function positive(value2, fallback) {
1615
+ return value2 === void 0 ? fallback : Number.isSafeInteger(value2) && Number(value2) > 0 ? Number(value2) : Infinity;
1616
+ }
1617
+ function route(value2) {
1618
+ if (typeof value2 !== "string" || value2.length > 2048 || !value2.startsWith("/") || value2.startsWith("//")) return false;
1619
+ try {
1620
+ const url = new URL(value2, "https://probe.invalid");
1621
+ return url.origin === "https://probe.invalid" && !url.hash;
1622
+ } catch {
1623
+ return false;
1624
+ }
1625
+ }
1626
+ function routePattern(value2) {
1627
+ return typeof value2 === "string" && value2.length <= 160 && /^\/[A-Za-z0-9_./:-]+\*?$/.test(value2) && !value2.slice(0, -1).includes("*");
1628
+ }
1629
+ function emailAddress(value2) {
1630
+ return typeof value2 === "string" && value2.length <= 254 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value2);
1631
+ }
1632
+ function clock(value2) {
1633
+ return typeof value2 === "string" && /^(?:[01]\d|2[0-3]):[0-5]\d$/.test(value2);
1634
+ }
1635
+ function timezone(value2) {
1636
+ if (typeof value2 !== "string" || value2.length > 100) return false;
1637
+ try {
1638
+ new Intl.DateTimeFormat("en", { timeZone: value2 }).format(0);
1639
+ return true;
1640
+ } catch {
1641
+ return false;
1642
+ }
1643
+ }
1644
+ var CADENCES, WINDOWS, SHORT, LONG, DAYS, O11Y_METRICS, COMPARATORS;
1645
+ var init_monitoring_validation = __esm({
1646
+ "src/monitoring-validation.ts"() {
1647
+ "use strict";
1648
+ init_cjs_shims();
1649
+ CADENCES = /* @__PURE__ */ new Set(["1m", "2m", "5m", "10m", "15m", "30m", "1h"]);
1650
+ WINDOWS = /* @__PURE__ */ new Set(["7d", "28d", "30d"]);
1651
+ SHORT = /* @__PURE__ */ new Set(["30m", "1h", "6h", "12h", "1d"]);
1652
+ LONG = /* @__PURE__ */ new Set(["1d", "3d", "7d"]);
1653
+ DAYS = /* @__PURE__ */ new Set(["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]);
1654
+ O11Y_METRICS = /* @__PURE__ */ new Set(["error_rate", "latency_p95", "synthetic_success", "synthetic_publish_to_visible"]);
1655
+ COMPARATORS = /* @__PURE__ */ new Set(["gt", "gte", "lt", "lte"]);
1656
+ }
1657
+ });
1658
+
1483
1659
  // src/config.ts
1484
1660
  async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
1485
1661
  const resolved = (0, import_node_path5.resolve)(configPath);
@@ -1495,6 +1671,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
1495
1671
  const services = unique3(raw.services?.length ? raw.services : DEFAULT_SERVICES);
1496
1672
  validateServices(services, resolved);
1497
1673
  validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
1674
+ validateMonitoringConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
1498
1675
  const local = {
1499
1676
  tokenFile: (0, import_node_path5.resolve)(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
1500
1677
  credentialsFile: (0, import_node_path5.resolve)(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
@@ -1618,6 +1795,7 @@ var init_config = __esm({
1618
1795
  init_ai_config_validation();
1619
1796
  init_calendar_config();
1620
1797
  init_integration_validation();
1798
+ init_monitoring_validation();
1621
1799
  init_calendar_config();
1622
1800
  DEFAULT_PLATFORM = "https://odla.ai";
1623
1801
  DEFAULT_ENVS = ["dev"];
@@ -1959,12 +2137,12 @@ function credentialKind(value2, machine, scopes) {
1959
2137
  function managerOf(value2) {
1960
2138
  if (!value2 || typeof value2 !== "object") return null;
1961
2139
  const row = value2;
1962
- const principalId = text(row.principalId);
2140
+ const principalId = text2(row.principalId);
1963
2141
  if (!principalId) return null;
1964
2142
  return {
1965
2143
  principalId,
1966
- displayName: text(row.displayName) ?? "Unnamed member",
1967
- handle: text(row.handle) ?? ""
2144
+ displayName: text2(row.displayName) ?? "Unnamed member",
2145
+ handle: text2(row.handle) ?? ""
1968
2146
  };
1969
2147
  }
1970
2148
  function unnamedPrincipal(kind) {
@@ -1978,14 +2156,14 @@ async function fetchIdentity(platformUrl, token, doFetch) {
1978
2156
  });
1979
2157
  if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
1980
2158
  const body = await res.json();
1981
- const developerId = text(body.developerId) ?? "";
2159
+ const developerId = text2(body.developerId) ?? "";
1982
2160
  const machine = body.machine === true;
1983
2161
  const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
1984
- const principalId = text(body.principalId) ?? developerId;
1985
- const email = text(body.email);
2162
+ const principalId = text2(body.principalId) ?? developerId;
2163
+ const email = text2(body.email);
1986
2164
  const kind = principalKind(body.principalKind, machine);
1987
- const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
1988
- const handle = text(body.handle) ?? "";
2165
+ const displayName = text2(body.displayName) ?? email ?? unnamedPrincipal(kind);
2166
+ const handle = text2(body.handle) ?? "";
1989
2167
  const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
1990
2168
  return {
1991
2169
  developerId,
@@ -1995,7 +2173,7 @@ async function fetchIdentity(platformUrl, token, doFetch) {
1995
2173
  handle,
1996
2174
  manager: managerOf(body.manager),
1997
2175
  credential: {
1998
- id: text(credential2.id),
2176
+ id: text2(credential2.id),
1999
2177
  kind: credentialKind(credential2.kind, machine, scopes)
2000
2178
  },
2001
2179
  email,
@@ -2081,7 +2259,7 @@ async function whoamiCommand(parsed, deps = {}) {
2081
2259
  }
2082
2260
  }
2083
2261
  }
2084
- var text;
2262
+ var text2;
2085
2263
  var init_whoami_command = __esm({
2086
2264
  "src/whoami-command.ts"() {
2087
2265
  "use strict";
@@ -2089,7 +2267,7 @@ var init_whoami_command = __esm({
2089
2267
  init_argv();
2090
2268
  init_operator_context();
2091
2269
  init_token();
2092
- text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
2270
+ text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
2093
2271
  }
2094
2272
  });
2095
2273
 
@@ -2220,13 +2398,13 @@ async function agentCommand(parsed, deps = {}) {
2220
2398
  const base = `${cfg.dbEndpoint}/app/${encodeURIComponent(tenant)}/admin/agent-jobs`;
2221
2399
  const headers = { authorization: `Bearer ${credential2}` };
2222
2400
  if (action2 === "retry") {
2223
- const id = parsed.positionals[2];
2224
- const res2 = await doFetch(`${base}/${encodeURIComponent(id)}/retry`, { method: "POST", headers });
2401
+ const id2 = parsed.positionals[2];
2402
+ const res2 = await doFetch(`${base}/${encodeURIComponent(id2)}/retry`, { method: "POST", headers });
2225
2403
  const body2 = await readJson(res2);
2226
2404
  if (!res2.ok) throw new Error(`agent retry failed (${res2.status}): ${errorMessage(body2)}`);
2227
2405
  const result2 = { v: 1, appId: cfg.app.id, env, tenant, ...body2 };
2228
2406
  if (parsed.options.json === true) out.log(JSON.stringify(result2, null, 2));
2229
- else out.log(`${tenant}: requeued ${id}`);
2407
+ else out.log(`${tenant}: requeued ${id2}`);
2230
2408
  return;
2231
2409
  }
2232
2410
  const state2 = stringOpt(parsed.options.state);
@@ -2328,8 +2506,8 @@ async function appImport(options) {
2328
2506
  const out = options.stdout ?? console;
2329
2507
  const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
2330
2508
  const { tenant } = resolveTenant(cfg, options.env);
2331
- const text2 = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs10.readFileSync)(0, "utf8")))() : (0, import_node_fs10.readFileSync)(options.file, "utf8");
2332
- const { format, sources } = (0, import_import.parseImport)(text2, options.ns);
2509
+ const text3 = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs10.readFileSync)(0, "utf8")))() : (0, import_node_fs10.readFileSync)(options.file, "utf8");
2510
+ const { format, sources } = (0, import_import.parseImport)(text3, options.ns);
2333
2511
  if (format === "namespace-map" && options.ns) {
2334
2512
  throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
2335
2513
  }
@@ -2666,7 +2844,7 @@ var init_brand_design_unpack = __esm({
2666
2844
  "text/html": "html",
2667
2845
  "application/json": "json"
2668
2846
  };
2669
- encode = (text2) => new TextEncoder().encode(text2);
2847
+ encode = (text3) => new TextEncoder().encode(text3);
2670
2848
  }
2671
2849
  });
2672
2850
 
@@ -2765,18 +2943,18 @@ async function readCalendarStatus(ctx) {
2765
2943
  }
2766
2944
  async function discoverGoogleCalendars(ctx) {
2767
2945
  const raw = await calendarJson(ctx, "/calendars", {});
2768
- const value2 = record(raw);
2946
+ const value2 = record2(raw);
2769
2947
  if (!value2 || !Array.isArray(value2.calendars)) throw new Error("calendar discovery returned an invalid response");
2770
2948
  return value2.calendars.map((item, index) => {
2771
- const calendar = record(item);
2772
- const id = textField(calendar?.id, 1024);
2773
- if (!calendar || !id) throw new Error(`calendar discovery returned an invalid calendar at index ${index}`);
2949
+ const calendar = record2(item);
2950
+ const id2 = textField(calendar?.id, 1024);
2951
+ if (!calendar || !id2) throw new Error(`calendar discovery returned an invalid calendar at index ${index}`);
2774
2952
  const role = calendar.accessRole;
2775
2953
  if (role !== void 0 && role !== "freeBusyReader" && role !== "reader" && role !== "writer" && role !== "owner") {
2776
2954
  throw new Error(`calendar discovery returned an invalid access role at index ${index}`);
2777
2955
  }
2778
2956
  return {
2779
- id,
2957
+ id: id2,
2780
2958
  ...optionalText("summary", calendar.summary, 500),
2781
2959
  ...typeof calendar.primary === "boolean" ? { primary: calendar.primary } : {},
2782
2960
  ...typeof calendar.selected === "boolean" ? { selected: calendar.selected } : {},
@@ -2804,10 +2982,10 @@ async function pollCalendarConnection(ctx, attemptId) {
2804
2982
  }
2805
2983
  function parseCalendarStatus(raw, env) {
2806
2984
  const outer = wrapped(raw, "calendar");
2807
- const value2 = record(outer.attempt) ?? record(outer.status) ?? outer;
2808
- const connection = record(value2.connection) ?? {};
2809
- const config = record(value2.config) ?? record(outer.config) ?? {};
2810
- const googleConfig = record(config.google) ?? config;
2985
+ const value2 = record2(outer.attempt) ?? record2(outer.status) ?? outer;
2986
+ const connection = record2(value2.connection) ?? {};
2987
+ const config = record2(value2.config) ?? record2(outer.config) ?? {};
2988
+ const googleConfig = record2(config.google) ?? config;
2811
2989
  const stateValue = calendarState(value2.status ?? value2.state ?? connection.status ?? connection.state);
2812
2990
  if (!stateValue) {
2813
2991
  throw new Error("calendar status returned an invalid connection state");
@@ -2820,7 +2998,7 @@ function parseCalendarStatus(raw, env) {
2820
2998
  if (accessValue !== void 0 && accessValue !== "book" && accessValue !== "read") {
2821
2999
  throw new Error("calendar status returned unsupported access");
2822
3000
  }
2823
- const errorValue = record(value2.error) ?? record(connection.error);
3001
+ const errorValue = record2(value2.error) ?? record2(connection.error);
2824
3002
  const errorCode2 = textField(value2.lastErrorCode, 128);
2825
3003
  const bookingPageValue = Object.hasOwn(value2, "bookingPageUrl") ? value2.bookingPageUrl : Object.hasOwn(config, "bookingPageUrl") ? config.bookingPageUrl : googleConfig.bookingPageUrl;
2826
3004
  const connected = typeof (value2.connected ?? connection.connected) === "boolean" ? Boolean(value2.connected ?? connection.connected) : ["healthy", "degraded"].includes(stateValue);
@@ -2890,11 +3068,11 @@ async function calendarJson(ctx, suffix, init) {
2890
3068
  return body;
2891
3069
  }
2892
3070
  function wrapped(raw, key) {
2893
- const outer = record(raw);
3071
+ const outer = record2(raw);
2894
3072
  if (!outer) throw new Error("calendar returned an invalid response");
2895
- return record(outer[key]) ?? outer;
3073
+ return record2(outer[key]) ?? outer;
2896
3074
  }
2897
- function record(value2) {
3075
+ function record2(value2) {
2898
3076
  return value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
2899
3077
  }
2900
3078
  function textField(value2, max) {
@@ -2907,9 +3085,9 @@ function calendarIds(value2) {
2907
3085
  if (!Array.isArray(value2)) return [];
2908
3086
  return [...new Set(value2.flatMap((item) => {
2909
3087
  if (typeof item === "string") return textField(item, 4096) ? [item] : [];
2910
- const calendar = record(item);
2911
- const id = textField(calendar?.id, 4096);
2912
- return id && calendar?.selected !== false ? [id] : [];
3088
+ const calendar = record2(item);
3089
+ const id2 = textField(calendar?.id, 4096);
3090
+ return id2 && calendar?.selected !== false ? [id2] : [];
2913
3091
  }))];
2914
3092
  }
2915
3093
  function timestamp3(value2) {
@@ -3203,6 +3381,7 @@ var init_capabilities = __esm({
3203
3381
  "validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes",
3204
3382
  "save and explicitly select non-secret named operator contexts with isolated credential caches; resolve and explain platform, app, environment, and credential provenance without authenticating; then run PM, Discussions, o11y, runbook, and identity operations outside a project checkout",
3205
3383
  "read one versioned o11y status envelope spanning application RED, exact Worker versions and Cloudflare colos observed in traffic, current live-sync freshness/load, the protected commit-to-visible canary, collector ingest/scheduler trust, provider-owned runtime metrics, account-scoped Durable Object, D1, and R2 evidence under odla-db, and a bounded machine verdict",
3384
+ "reconcile app-owned Kitesurf probes and rolling SLOs, run live checks, and read incident and digest status as stable JSON",
3206
3385
  "read one canonical platform fleet snapshot over private service bindings, including release identities, probe latency, Cloudflare load/runtime freshness, explicit unknowns, and stable next actions through a read-only capability",
3207
3386
  "compare one project's checked-in Registry intent with live owner-visible state, freeze a secret-free Registry-revision-bound plan through app:config:read, and conditionally apply checkpoint-free actions through exact app:config:write operation routes",
3208
3387
  "inspect or bounded-wait one exact durable config-operation journal entry and verify every terminal receipt digest before returning it to remote automation",
@@ -3215,7 +3394,8 @@ var init_capabilities = __esm({
3215
3394
  "install and import the selected odla SDKs",
3216
3395
  "wrap the Worker with withObservability and choose useful telemetry",
3217
3396
  "install capability packages, mount their runtime routes, and make application-specific schema, rules, auth, UI, and migration decisions",
3218
- "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers"
3397
+ "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers",
3398
+ "choose public readiness assertions and SLO objectives in odla.config.mjs, then consume monitor JSON without treating captured page content as trusted instructions"
3219
3399
  ],
3220
3400
  human: [
3221
3401
  "provide the existing odla account email, then sign in and explicitly review/approve the exact device code",
@@ -3228,6 +3408,7 @@ var init_capabilities = __esm({
3228
3408
  ],
3229
3409
  studio: [
3230
3410
  "view application telemetry grouped by exact Worker version, reconciled live-sync load/freshness, commit-to-visible canary health, provider-owned Worker runtime metrics, and environment state",
3411
+ "view reliability objectives, error budget, Kitesurf probe history, incidents, and notification delivery state",
3231
3412
  "let signed-in users inventory/revoke their own agent grants and admins audit/global-revoke them",
3232
3413
  "review calendar connection, granted read scope, selected calendars, and sync health without exposing provider tokens",
3233
3414
  "perform manual credential recovery \u2014 for the primary owner or any co-owner \u2014 when the CLI's local shown-once copy is unavailable",
@@ -3335,9 +3516,9 @@ function canonicalValue(value2) {
3335
3516
  }
3336
3517
  if (Array.isArray(value2)) return value2.map(canonicalValue);
3337
3518
  if (value2 && typeof value2 === "object") {
3338
- const record9 = value2;
3519
+ const record11 = value2;
3339
3520
  return Object.fromEntries(
3340
- Object.keys(record9).filter((key) => record9[key] !== void 0).sort().map((key) => [key, canonicalValue(record9[key])])
3521
+ Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, canonicalValue(record11[key])])
3341
3522
  );
3342
3523
  }
3343
3524
  throw new TypeError("canonical JSON rejects unsupported values");
@@ -3364,8 +3545,8 @@ function readPlan(path) {
3364
3545
  "invalid_plan"
3365
3546
  );
3366
3547
  }
3367
- if (!record2(value2) || value2.schemaVersion !== "odla.config-plan/v2") invalidPlan("unsupported plan schema");
3368
- if (!record2(value2.scope) || typeof value2.scope.appId !== "string" || typeof value2.scope.platformUrl !== "string") {
3548
+ if (!record3(value2) || value2.schemaVersion !== "odla.config-plan/v2") invalidPlan("unsupported plan schema");
3549
+ if (!record3(value2.scope) || typeof value2.scope.appId !== "string" || typeof value2.scope.platformUrl !== "string") {
3369
3550
  invalidPlan("plan scope is invalid");
3370
3551
  }
3371
3552
  if (!DIGEST.test(String(value2.desiredRevision)) || !DIGEST.test(String(value2.observedRevision))) {
@@ -3415,10 +3596,10 @@ function assertOperationId(value2) {
3415
3596
  function assertActions(actions) {
3416
3597
  const ids = /* @__PURE__ */ new Set();
3417
3598
  for (const action2 of actions) {
3418
- if (!record2(action2)) invalidPlan("every plan action must be an object");
3419
- const id = String(action2.id ?? "");
3420
- if (!ACTION_ID.test(id) || ids.has(id)) invalidPlan("plan action ids must be unique frozen ids");
3421
- ids.add(id);
3599
+ if (!record3(action2)) invalidPlan("every plan action must be an object");
3600
+ const id2 = String(action2.id ?? "");
3601
+ if (!ACTION_ID.test(id2) || ids.has(id2)) invalidPlan("plan action ids must be unique frozen ids");
3602
+ ids.add(id2);
3422
3603
  if (typeof action2.path !== "string" || typeof action2.reason !== "string" || !action2.reason || action2.reason.length > 500 || typeof action2.requiresApproval !== "boolean" || !["low", "medium", "high"].includes(String(action2.risk)) || !["provision", "command", "studio"].includes(String(action2.applySupport))) {
3423
3604
  invalidPlan("plan action metadata is invalid");
3424
3605
  }
@@ -3444,14 +3625,14 @@ function assertConditionalAction(action2) {
3444
3625
  if (action2.path !== (action2.kind === "configure_service" ? `${base}.config` : base)) {
3445
3626
  invalidPlan("service action path is invalid");
3446
3627
  }
3447
- if (action2.applySupport !== "provision" || !record2(action2.after)) {
3628
+ if (action2.applySupport !== "provision" || !record3(action2.after)) {
3448
3629
  invalidPlan("service action payload is invalid");
3449
3630
  }
3450
3631
  if (action2.kind === "enable_service") {
3451
- if (action2.after.enabled !== true || action2.before !== null && !record2(action2.before)) {
3632
+ if (action2.after.enabled !== true || action2.before !== null && !record3(action2.before)) {
3452
3633
  invalidPlan("service enable action is invalid");
3453
3634
  }
3454
- } else if (!record2(action2.before)) {
3635
+ } else if (!record3(action2.before)) {
3455
3636
  invalidPlan("service configure action is invalid");
3456
3637
  }
3457
3638
  }
@@ -3468,7 +3649,7 @@ function linkState(value2) {
3468
3649
  function invalidPlan(message2) {
3469
3650
  throw new ConfigOperationCommandError(message2, "invalid_plan");
3470
3651
  }
3471
- function record2(value2) {
3652
+ function record3(value2) {
3472
3653
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
3473
3654
  }
3474
3655
  var import_apps3, import_node_fs11, DIGEST, REVISION, OPERATION_ID, ACTION_ID, ENV, SERVICE;
@@ -3519,9 +3700,9 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
3519
3700
  }
3520
3701
  throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
3521
3702
  }
3522
- function errorCode(text2) {
3703
+ function errorCode(text3) {
3523
3704
  try {
3524
- const body = JSON.parse(text2);
3705
+ const body = JSON.parse(text3);
3525
3706
  return typeof body.error?.code === "string" ? body.error.code : null;
3526
3707
  } catch {
3527
3708
  return null;
@@ -3689,7 +3870,7 @@ async function configApply(options) {
3689
3870
  throw new ConfigOperationCommandError("--idempotency-key is not a safe 1-120 character key", "invalid_plan");
3690
3871
  }
3691
3872
  const client = await operationClient(cfg, options, "apply");
3692
- const request2 = {
3873
+ const request3 = {
3693
3874
  schemaVersion: "odla.config-operation-request/v1",
3694
3875
  expectedRevision: plan.registryRevision,
3695
3876
  desiredRevision: plan.desiredRevision,
@@ -3701,7 +3882,7 @@ async function configApply(options) {
3701
3882
  };
3702
3883
  let receipt;
3703
3884
  try {
3704
- receipt = await client.applyConfigOperation(cfg.app.id, request2);
3885
+ receipt = await client.applyConfigOperation(cfg.app.id, request3);
3705
3886
  } catch (error) {
3706
3887
  const retained = retainedReceipt(error);
3707
3888
  if (retained) {
@@ -3800,8 +3981,8 @@ function failureForReceipt(receipt) {
3800
3981
  return new ConfigOperationCommandError(receipt.error?.message ?? `config operation ${receipt.state}`, "config_operation_failed");
3801
3982
  }
3802
3983
  function retainedReceipt(error) {
3803
- if (!(error instanceof import_apps6.AppsError) || !record3(error.details)) return null;
3804
- return record3(error.details.operation) ? error.details.operation : null;
3984
+ if (!(error instanceof import_apps6.AppsError) || !record4(error.details)) return null;
3985
+ return record4(error.details.operation) ? error.details.operation : null;
3805
3986
  }
3806
3987
  function normalizeRequestError(error) {
3807
3988
  if (!(error instanceof import_apps6.AppsError)) return error instanceof Error ? error : new Error(String(error));
@@ -3814,7 +3995,7 @@ function normalizeRequestError(error) {
3814
3995
  }
3815
3996
  return new ConfigOperationCommandError(error.message, error.code || "config_operation_failed");
3816
3997
  }
3817
- function record3(value2) {
3998
+ function record4(value2) {
3818
3999
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
3819
4000
  }
3820
4001
  var import_apps6, import_node_path9, IDEMPOTENCY_KEY, DEFAULT_WAIT_SECONDS, DEFAULT_INTERVAL_SECONDS;
@@ -4307,15 +4488,15 @@ function readWranglerConfig(path) {
4307
4488
  return null;
4308
4489
  }
4309
4490
  }
4310
- function stripJsonComments(text2) {
4491
+ function stripJsonComments(text3) {
4311
4492
  let result = "";
4312
4493
  let inString = false;
4313
- for (let i = 0; i < text2.length; i++) {
4314
- const ch = text2[i];
4494
+ for (let i = 0; i < text3.length; i++) {
4495
+ const ch = text3[i];
4315
4496
  if (inString) {
4316
4497
  result += ch;
4317
4498
  if (ch === "\\") {
4318
- result += text2[i + 1] ?? "";
4499
+ result += text3[i + 1] ?? "";
4319
4500
  i++;
4320
4501
  } else if (ch === '"') {
4321
4502
  inString = false;
@@ -4327,14 +4508,14 @@ function stripJsonComments(text2) {
4327
4508
  result += ch;
4328
4509
  continue;
4329
4510
  }
4330
- if (ch === "/" && text2[i + 1] === "/") {
4331
- while (i < text2.length && text2[i] !== "\n") i++;
4511
+ if (ch === "/" && text3[i + 1] === "/") {
4512
+ while (i < text3.length && text3[i] !== "\n") i++;
4332
4513
  result += "\n";
4333
4514
  continue;
4334
4515
  }
4335
- if (ch === "/" && text2[i + 1] === "*") {
4516
+ if (ch === "/" && text3[i + 1] === "*") {
4336
4517
  i += 2;
4337
- while (i < text2.length && !(text2[i] === "*" && text2[i + 1] === "/")) i++;
4518
+ while (i < text3.length && !(text3[i] === "*" && text3[i + 1] === "/")) i++;
4338
4519
  i++;
4339
4520
  continue;
4340
4521
  }
@@ -4373,7 +4554,7 @@ async function wranglerRuntimeTarget(run, opts) {
4373
4554
  throw new Error(`wrangler is not logged in \u2014 run "wrangler login" (a browser step for the human)`);
4374
4555
  }
4375
4556
  const discovered = [...new Set(`${whoami.stdout}
4376
- ${whoami.stderr}`.match(/\b[a-f0-9]{32}\b/gi)?.map((id) => id.toLowerCase()) ?? [])];
4557
+ ${whoami.stderr}`.match(/\b[a-f0-9]{32}\b/gi)?.map((id2) => id2.toLowerCase()) ?? [])];
4377
4558
  const accountId = configuredAccount.toLowerCase() || (discovered.length === 1 ? discovered[0] : "");
4378
4559
  if (!/^[a-f0-9]{32}$/.test(accountId) || configuredAccount && discovered.length > 0 && !discovered.includes(accountId)) {
4379
4560
  throw new Error("Wrangler account is ambiguous or does not match account_id in the config");
@@ -4918,9 +5099,9 @@ function initProject(options) {
4918
5099
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
4919
5100
  out.log("updated .gitignore for local odla credentials");
4920
5101
  }
4921
- function writeIfMissing(path, text2) {
5102
+ function writeIfMissing(path, text3) {
4922
5103
  if ((0, import_node_fs14.existsSync)(path)) return;
4923
- (0, import_node_fs14.writeFileSync)(path, text2);
5104
+ (0, import_node_fs14.writeFileSync)(path, text3);
4924
5105
  }
4925
5106
  function configTemplate(input) {
4926
5107
  const calendar = input.services.includes("calendar") ? ` calendar: {
@@ -5150,13 +5331,13 @@ async function secretsSetClerkKey(options) {
5150
5331
  body: JSON.stringify({ value: value2 })
5151
5332
  });
5152
5333
  if (!res.ok) {
5153
- const text2 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
5154
- throw new Error(`store Clerk secret key failed (${res.status}): ${text2 || "request failed"}`);
5334
+ const text3 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
5335
+ throw new Error(`store Clerk secret key failed (${res.status}): ${text3 || "request failed"}`);
5155
5336
  }
5156
5337
  out.log(`Clerk secret key stored for ${tenantId} ($clerk_secret, reserved + write-only; the value was never echoed)`);
5157
5338
  }
5158
- function scrubValue(text2, value2) {
5159
- return redactSecrets(text2).split(value2).join("[value redacted]");
5339
+ function scrubValue(text3, value2) {
5340
+ return redactSecrets(text3).split(value2).join("[value redacted]");
5160
5341
  }
5161
5342
  async function resolveVaultWrite(options) {
5162
5343
  const out = options.stdout ?? console;
@@ -5261,8 +5442,8 @@ var init_secrets_status = __esm({
5261
5442
  });
5262
5443
 
5263
5444
  // src/skill-adapters.ts
5264
- function claudeAdapter(skill, canonical) {
5265
- const match = canonical.match(/^---\r?\n([\s\S]*?)\r?\n---/);
5445
+ function claudeAdapter(skill, canonical2) {
5446
+ const match = canonical2.match(/^---\r?\n([\s\S]*?)\r?\n---/);
5266
5447
  if (!match) throw new Error(`bundled skill ${skill} has no YAML frontmatter`);
5267
5448
  const lines = match[1].split(/\r?\n/);
5268
5449
  const frontmatter = [];
@@ -5386,8 +5567,8 @@ function installSkill(options = {}) {
5386
5567
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
5387
5568
  if (harnesses.includes("claude")) {
5388
5569
  for (const skill of skillNames(files)) {
5389
- const canonical = (0, import_node_fs15.readFileSync)((0, import_node_path14.join)(sourceDir, skill, "SKILL.md"), "utf8");
5390
- plan((0, import_node_path14.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
5570
+ const canonical2 = (0, import_node_fs15.readFileSync)((0, import_node_path14.join)(sourceDir, skill, "SKILL.md"), "utf8");
5571
+ plan((0, import_node_path14.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
5391
5572
  }
5392
5573
  rememberTarget("claude", claudeRoot);
5393
5574
  }
@@ -5676,7 +5857,7 @@ function assertCalendarHealthy(status, expected) {
5676
5857
  if (!status.writable) throw new Error('calendar grant does not cover booking writes; run "odla-ai calendar connect" to re-consent');
5677
5858
  const hasEventsScope = status.grantedScopes.some((scope) => scope === GOOGLE_CALENDAR_EVENTS_SCOPE);
5678
5859
  if (!hasEventsScope) throw new Error("calendar connection is missing calendar.events consent");
5679
- const missing = expected.availabilityCalendars.filter((id) => !status.calendars.includes(id));
5860
+ const missing = expected.availabilityCalendars.filter((id2) => !status.calendars.includes(id2));
5680
5861
  if (missing.length) throw new Error(`calendar connection is missing configured calendars: ${missing.join(", ")}`);
5681
5862
  }
5682
5863
  async function getJson(doFetch, url, bearer) {
@@ -6087,8 +6268,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
6087
6268
  const maxFiles = options.maxFiles ?? 2e4;
6088
6269
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
6089
6270
  const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
6090
- const entries = inventory.flatMap((record9) => {
6091
- const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record9);
6271
+ const entries = inventory.flatMap((record11) => {
6272
+ const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record11);
6092
6273
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
6093
6274
  });
6094
6275
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
@@ -6369,8 +6550,8 @@ function normalize(value2) {
6369
6550
  if (Array.isArray(value2)) return value2.map(normalize);
6370
6551
  if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
6371
6552
  if (typeof value2 === "object") {
6372
- const record9 = value2;
6373
- return Object.fromEntries(Object.keys(record9).filter((key) => record9[key] !== void 0).sort().map((key) => [key, normalize(record9[key])]));
6553
+ const record11 = value2;
6554
+ return Object.fromEntries(Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, normalize(record11[key])]));
6374
6555
  }
6375
6556
  throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
6376
6557
  }
@@ -6449,7 +6630,7 @@ function copyRef(ref) {
6449
6630
  function normalizeReaders(readers) {
6450
6631
  if (readers.kind === "public") return Object.freeze({ kind: "public" });
6451
6632
  const principalIds = [...new Set(readers.principalIds)].sort();
6452
- if (principalIds.some((id) => !id)) throw new CamelError("reader_mismatch", "Reader principal IDs must be non-empty.");
6633
+ if (principalIds.some((id2) => !id2)) throw new CamelError("reader_mismatch", "Reader principal IDs must be non-empty.");
6453
6634
  return Object.freeze({ kind: "principals", principalIds: Object.freeze(principalIds) });
6454
6635
  }
6455
6636
  var camelValueBrand, authenticCamelValues;
@@ -6466,7 +6647,7 @@ var init_chunk_4DQ6BIHP = __esm({
6466
6647
  // ../camel/dist/code.js
6467
6648
  async function digestCodeVerificationReceipt(fields) {
6468
6649
  validate(fields);
6469
- const canonical = {
6650
+ const canonical2 = {
6470
6651
  schemaVersion: fields.schemaVersion,
6471
6652
  verificationId: fields.verificationId,
6472
6653
  trustedBaseCommitSha: fields.trustedBaseCommitSha,
@@ -6493,7 +6674,7 @@ async function digestCodeVerificationReceipt(fields) {
6493
6674
  changedTestsRequireReview: fields.changedTestsRequireReview,
6494
6675
  outcome: fields.outcome
6495
6676
  };
6496
- return `sha256:${await sha256Hex(canonicalJson2(canonical))}`;
6677
+ return `sha256:${await sha256Hex(canonicalJson2(canonical2))}`;
6497
6678
  }
6498
6679
  function validate(fields) {
6499
6680
  if (fields.schemaVersion !== 1 || typeof fields.verificationId !== "string" || !ID.test(fields.verificationId) || typeof fields.trustedBaseCommitSha !== "string" || !SHA.test(fields.trustedBaseCommitSha) || !DIGEST2.test(fields.trustedBaseDigest) || !DIGEST2.test(fields.patchDigest) || !DIGEST2.test(fields.candidateDigest) || !DIGEST2.test(fields.sourceDigest) || !DIGEST2.test(fields.policyDigest) || !DIGEST2.test(fields.changedTestSetDigest) || !Number.isSafeInteger(fields.changedTestCount) || fields.changedTestCount < 0 || fields.changedTestCount > 1e4 || fields.changedTestsRequireReview !== fields.changedTestCount > 0 || fields.recipes.length < 1 || fields.recipes.length > 64) {
@@ -6702,7 +6883,7 @@ async function createConversionRegistry(config) {
6702
6883
  if (await conversionPolicyDigest(definition) !== policy.digest) throw new CamelError("state_conflict", "Conversion policy digest mismatch.");
6703
6884
  if (policy.output.kind === "registered_id") {
6704
6885
  const registry = config.registeredIds?.[policy.output.registryId];
6705
- const validValues = registry && Object.entries(registry.values).every(([candidate, id]) => candidate.length > 0 && typeof id === "string" && id.length > 0);
6886
+ const validValues = registry && Object.entries(registry.values).every(([candidate, id2]) => candidate.length > 0 && typeof id2 === "string" && id2.length > 0);
6706
6887
  if (!registry || !validValues || registry.digest !== policy.output.registryDigest || await registeredIdRegistryDigest(registry.values) !== registry.digest) {
6707
6888
  throw new CamelError("state_conflict", "Registered-ID registry digest mismatch.");
6708
6889
  }
@@ -6710,63 +6891,63 @@ async function createConversionRegistry(config) {
6710
6891
  policies.set(policy.conversionId, Object.freeze(policy));
6711
6892
  }
6712
6893
  const outputCounts = /* @__PURE__ */ new Map();
6713
- const get = (id, kind) => {
6714
- const policy = policies.get(id);
6894
+ const get = (id2, kind) => {
6895
+ const policy = policies.get(id2);
6715
6896
  if (!policy || policy.output.kind !== kind) throw new CamelError("conversion_rejected", "Conversion policy is missing or has the wrong output kind.");
6716
6897
  return policy;
6717
6898
  };
6718
- const checked = (source, id, kind) => {
6719
- const policy = get(id, kind);
6899
+ const checked = (source, id2, kind) => {
6900
+ const policy = get(id2, kind);
6720
6901
  if (utf8Length(source.value) > policy.maximumSourceBytes) throw new CamelError("limit_exceeded", "Unsafe conversion input exceeds its byte bound.");
6721
6902
  return policy;
6722
6903
  };
6723
- const emit3 = (source, policy, value2) => convert(source, policy, value2, outputCounts);
6904
+ const emit4 = (source, policy, value2) => convert(source, policy, value2, outputCounts);
6724
6905
  const operations = Object.freeze({
6725
- boolean: async (value2, id) => {
6726
- const policy = checked(value2, id, "boolean");
6727
- return emit3(value2, policy, requireBoolean(value2.value));
6906
+ boolean: async (value2, id2) => {
6907
+ const policy = checked(value2, id2, "boolean");
6908
+ return emit4(value2, policy, requireBoolean(value2.value));
6728
6909
  },
6729
- integer: async (value2, id) => {
6730
- const policy = checked(value2, id, "integer");
6731
- return emit3(value2, policy, boundedInteger(value2.value, policy.output));
6910
+ integer: async (value2, id2) => {
6911
+ const policy = checked(value2, id2, "integer");
6912
+ return emit4(value2, policy, boundedInteger(value2.value, policy.output));
6732
6913
  },
6733
- finiteNumber: async (value2, id) => {
6734
- const policy = checked(value2, id, "finite_number");
6735
- return emit3(value2, policy, boundedNumber(value2.value, policy.output));
6914
+ finiteNumber: async (value2, id2) => {
6915
+ const policy = checked(value2, id2, "finite_number");
6916
+ return emit4(value2, policy, boundedNumber(value2.value, policy.output));
6736
6917
  },
6737
- enum: async (value2, id) => {
6738
- const policy = checked(value2, id, "enum");
6739
- return emit3(value2, policy, enumMember(value2.value, policy.output));
6918
+ enum: async (value2, id2) => {
6919
+ const policy = checked(value2, id2, "enum");
6920
+ return emit4(value2, policy, enumMember(value2.value, policy.output));
6740
6921
  },
6741
- date: async (value2, id) => {
6742
- const policy = checked(value2, id, "date");
6743
- return emit3(value2, policy, canonicalDate(value2.value, policy.output));
6922
+ date: async (value2, id2) => {
6923
+ const policy = checked(value2, id2, "date");
6924
+ return emit4(value2, policy, canonicalDate(value2.value, policy.output));
6744
6925
  },
6745
- registeredId: async (value2, id) => {
6746
- const policy = checked(value2, id, "registered_id");
6926
+ registeredId: async (value2, id2) => {
6927
+ const policy = checked(value2, id2, "registered_id");
6747
6928
  const registry = config.registeredIds?.[policy.output.registryId];
6748
6929
  const output = typeof value2.value === "string" ? registry?.values[value2.value] : void 0;
6749
6930
  if (!output) throw new CamelError("conversion_rejected", "Registered-ID conversion rejected the candidate.");
6750
- return emit3(value2, policy, output);
6931
+ return emit4(value2, policy, output);
6751
6932
  },
6752
- digest: async (value2, id) => {
6753
- const policy = checked(value2, id, "digest");
6933
+ digest: async (value2, id2) => {
6934
+ const policy = checked(value2, id2, "digest");
6754
6935
  if (!(value2.value instanceof Uint8Array)) throw new CamelError("conversion_rejected", "Digest conversion requires bytes.");
6755
- return emit3(value2, policy, await sha256Hex(value2.value));
6936
+ return emit4(value2, policy, await sha256Hex(value2.value));
6756
6937
  },
6757
- measure: async (value2, metric, id) => {
6758
- const policy = checked(value2, id, "integer");
6938
+ measure: async (value2, metric, id2) => {
6939
+ const policy = checked(value2, id2, "integer");
6759
6940
  const measured = measure(value2.value, metric);
6760
- return emit3(value2, policy, boundedInteger(measured, policy.output));
6941
+ return emit4(value2, policy, boundedInteger(measured, policy.output));
6761
6942
  },
6762
- test: async (value2, predicateId, id) => {
6763
- const policy = checked(value2, id, "boolean");
6943
+ test: async (value2, predicateId, id2) => {
6944
+ const policy = checked(value2, id2, "boolean");
6764
6945
  const predicate = config.predicates?.[predicateId];
6765
6946
  if (!predicate) throw new CamelError("conversion_rejected", "Predicate is not registered.");
6766
- return emit3(value2, policy, evaluatePredicate(value2.value, predicate, config.registeredIds));
6947
+ return emit4(value2, policy, evaluatePredicate(value2.value, predicate, config.registeredIds));
6767
6948
  }
6768
6949
  });
6769
- return Object.freeze({ operations, policy: (id) => policies.get(id) ?? missingPolicy() });
6950
+ return Object.freeze({ operations, policy: (id2) => policies.get(id2) ?? missingPolicy() });
6770
6951
  }
6771
6952
  async function convert(source, policy, value2, counts) {
6772
6953
  const sourceKey = sourceIdentity(source);
@@ -6809,8 +6990,8 @@ function boundedInteger(value2, spec) {
6809
6990
  }
6810
6991
  function boundedNumber(value2, spec) {
6811
6992
  if (spec.kind !== "finite_number" || typeof value2 !== "number" || !Number.isFinite(value2) || value2 < spec.minimum || value2 > spec.maximum) throw new CamelError("conversion_rejected", "Finite-number conversion rejected the structured value.");
6812
- const text2 = String(value2);
6813
- if (/e/i.test(text2) || (text2.split(".")[1]?.length ?? 0) > spec.maximumDecimalPlaces) throw new CamelError("conversion_rejected", "Finite-number conversion rejected a non-canonical decimal.");
6993
+ const text3 = String(value2);
6994
+ if (/e/i.test(text3) || (text3.split(".")[1]?.length ?? 0) > spec.maximumDecimalPlaces) throw new CamelError("conversion_rejected", "Finite-number conversion rejected a non-canonical decimal.");
6814
6995
  return value2;
6815
6996
  }
6816
6997
  function enumMember(value2, spec) {
@@ -6870,10 +7051,10 @@ function createCamelIngress(constants2 = []) {
6870
7051
  const ingress = {
6871
7052
  userInstruction: (value2, input) => createSafeInternal(value2, "user_instruction", metadata("user_instruction", input.id, input.readers)),
6872
7053
  systemPolicy: (value2, input) => createSafeInternal(value2, "system_policy", metadata("system_policy", input.id, input.readers)),
6873
- control: (id) => {
6874
- const item = byId.get(id);
7054
+ control: (id2) => {
7055
+ const item = byId.get(id2);
6875
7056
  if (!item) throw new CamelError("permission_denied", "Unknown control constant.");
6876
- return createSafeInternal(item.value, "harness_constant", metadata("harness", id, item.readers));
7057
+ return createSafeInternal(item.value, "harness_constant", metadata("harness", id2, item.readers));
6877
7058
  },
6878
7059
  external: (value2, label) => createUnsafeInternal(value2, label),
6879
7060
  quarantinedOutput: (value2, input) => {
@@ -6897,9 +7078,9 @@ function assertNoUnsafeConstant(value2, seen = /* @__PURE__ */ new WeakSet()) {
6897
7078
  }
6898
7079
  for (const child of Object.values(value2)) assertNoUnsafeConstant(child, seen);
6899
7080
  }
6900
- function metadata(kind, id, readers) {
6901
- if (!id) throw new CamelError("state_conflict", "Provenance IDs must be non-empty.");
6902
- return { readers, provenance: [{ kind, id }] };
7081
+ function metadata(kind, id2, readers) {
7082
+ if (!id2) throw new CamelError("state_conflict", "Provenance IDs must be non-empty.");
7083
+ return { readers, provenance: [{ kind, id: id2 }] };
6903
7084
  }
6904
7085
  var init_chunk_VEAUXH4F = __esm({
6905
7086
  "../camel/dist/chunk-VEAUXH4F.js"() {
@@ -6981,7 +7162,7 @@ function isControlOwned(value2) {
6981
7162
  return value2.label.safeBasis === "system_policy" || value2.label.safeBasis === "harness_constant";
6982
7163
  }
6983
7164
  function copyRegistries(registries) {
6984
- return Object.freeze(Object.fromEntries(Object.entries(registries).map(([id, registry]) => [id, Object.freeze({ digest: registry.digest, values: Object.freeze([...registry.values]) })])));
7165
+ return Object.freeze(Object.fromEntries(Object.entries(registries).map(([id2, registry]) => [id2, Object.freeze({ digest: registry.digest, values: Object.freeze([...registry.values]) })])));
6985
7166
  }
6986
7167
  function validateUnsafeSelector(path, value2, tool) {
6987
7168
  const policy = tool.unsafeSelectorPolicy;
@@ -6993,8 +7174,8 @@ function validateUnsafeSelector(path, value2, tool) {
6993
7174
  return void 0;
6994
7175
  }
6995
7176
  function looksLikeDestination(value2) {
6996
- const text2 = value2.trim();
6997
- return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text2) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text2);
7177
+ const text3 = value2.trim();
7178
+ return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text3) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text3);
6998
7179
  }
6999
7180
  var init_policy = __esm({
7000
7181
  "../camel/dist/policy.js"() {
@@ -7006,9 +7187,9 @@ var init_policy = __esm({
7006
7187
  });
7007
7188
 
7008
7189
  // ../graph/dist/chunk-PS2SO4UP.js
7009
- function parseNodeId(id) {
7010
- const at = id.indexOf(":");
7011
- return at < 0 ? { kind: "", name: id } : { kind: id.slice(0, at), name: id.slice(at + 1) };
7190
+ function parseNodeId(id2) {
7191
+ const at = id2.indexOf(":");
7192
+ return at < 0 ? { kind: "", name: id2 } : { kind: id2.slice(0, at), name: id2.slice(at + 1) };
7012
7193
  }
7013
7194
  function nodesOfKind(graph, kind) {
7014
7195
  return [...graph.nodes.values()].filter((node) => node.kind === kind);
@@ -7025,14 +7206,14 @@ var init_chunk_PS2SO4UP = __esm({
7025
7206
  seen = /* @__PURE__ */ new Set();
7026
7207
  /** Add or enrich a node. Later attributes win; the kind never changes. */
7027
7208
  node(kind, name, attrs) {
7028
- const id = nodeId(kind, name);
7029
- const existing = this.byId.get(id);
7209
+ const id2 = nodeId(kind, name);
7210
+ const existing = this.byId.get(id2);
7030
7211
  if (existing) {
7031
- if (attrs) this.byId.set(id, { ...existing, attrs: { ...existing.attrs, ...attrs } });
7032
- return id;
7212
+ if (attrs) this.byId.set(id2, { ...existing, attrs: { ...existing.attrs, ...attrs } });
7213
+ return id2;
7033
7214
  }
7034
- this.byId.set(id, { id, kind, name, ...attrs ? { attrs } : {} });
7035
- return id;
7215
+ this.byId.set(id2, { id: id2, kind, name, ...attrs ? { attrs } : {} });
7216
+ return id2;
7036
7217
  }
7037
7218
  /**
7038
7219
  * Add a directed edge, minting either endpoint if it is not known yet.
@@ -7042,10 +7223,10 @@ var init_chunk_PS2SO4UP = __esm({
7042
7223
  * by how often someone repeated an import.
7043
7224
  */
7044
7225
  edge(from, kind, to, attrs) {
7045
- for (const id of [from, to]) {
7046
- if (!this.byId.has(id)) {
7047
- const parsed = parseNodeId(id);
7048
- this.byId.set(id, { id, kind: parsed.kind, name: parsed.name });
7226
+ for (const id2 of [from, to]) {
7227
+ if (!this.byId.has(id2)) {
7228
+ const parsed = parseNodeId(id2);
7229
+ this.byId.set(id2, { id: id2, kind: parsed.kind, name: parsed.name });
7049
7230
  }
7050
7231
  }
7051
7232
  const key = `${from} ${kind} ${to}`;
@@ -7076,17 +7257,17 @@ var init_chunk_PS2SO4UP = __esm({
7076
7257
  });
7077
7258
 
7078
7259
  // ../graph/dist/index.js
7079
- function incident(graph, id, traversal = {}) {
7260
+ function incident(graph, id2, traversal = {}) {
7080
7261
  const direction = traversal.direction ?? "out";
7081
- const forward = direction === "out" || direction === "both" ? graph.out.get(id) ?? [] : [];
7082
- const backward = direction === "in" || direction === "both" ? graph.in.get(id) ?? [] : [];
7262
+ const forward = direction === "out" || direction === "both" ? graph.out.get(id2) ?? [] : [];
7263
+ const backward = direction === "in" || direction === "both" ? graph.in.get(id2) ?? [] : [];
7083
7264
  return [...forward, ...backward].filter((edge) => follows(traversal.kinds, edge));
7084
7265
  }
7085
- function neighbors(graph, id, traversal = {}) {
7266
+ function neighbors(graph, id2, traversal = {}) {
7086
7267
  const seen = /* @__PURE__ */ new Set();
7087
- for (const edge of incident(graph, id, traversal)) {
7088
- const other = otherEnd(edge, id);
7089
- if (other !== id) seen.add(other);
7268
+ for (const edge of incident(graph, id2, traversal)) {
7269
+ const other = otherEnd(edge, id2);
7270
+ if (other !== id2) seen.add(other);
7090
7271
  }
7091
7272
  return [...seen];
7092
7273
  }
@@ -7168,9 +7349,9 @@ async function extractImports(builder, input) {
7168
7349
  const sources = input.paths.filter(isSourcePath);
7169
7350
  const known = new Set(sources);
7170
7351
  for (const path of sources) {
7171
- let text2;
7352
+ let text3;
7172
7353
  try {
7173
- text2 = await input.read(path);
7354
+ text3 = await input.read(path);
7174
7355
  } catch {
7175
7356
  continue;
7176
7357
  }
@@ -7178,13 +7359,13 @@ async function extractImports(builder, input) {
7178
7359
  const file = builder.node(FILE, path, pkg ? { pkg } : void 0);
7179
7360
  if (pkg) builder.edge(builder.node(PACKAGE, pkg), CONTAINS, file);
7180
7361
  const specifiers = /* @__PURE__ */ new Set();
7181
- for (const match of text2.matchAll(IMPORT_FROM)) specifiers.add(match[1]);
7182
- for (const match of text2.matchAll(BARE_IMPORT)) specifiers.add(match[1]);
7362
+ for (const match of text3.matchAll(IMPORT_FROM)) specifiers.add(match[1]);
7363
+ for (const match of text3.matchAll(BARE_IMPORT)) specifiers.add(match[1]);
7183
7364
  for (const specifier of specifiers) {
7184
7365
  const resolved = resolveImport(path, specifier, known);
7185
7366
  if (resolved) builder.edge(file, IMPORTS, nodeId(FILE, resolved));
7186
7367
  }
7187
- for (const name of exportedNames(text2)) {
7368
+ for (const name of exportedNames(text3)) {
7188
7369
  builder.edge(file, EXPORTS, builder.node(SYMBOL, name));
7189
7370
  }
7190
7371
  }
@@ -7197,16 +7378,16 @@ async function extractData(builder, input) {
7197
7378
  };
7198
7379
  for (const path of input.paths) {
7199
7380
  if (!SOURCE_FILE.test(path) || input.ignore?.(path)) continue;
7200
- let text2;
7381
+ let text3;
7201
7382
  try {
7202
- text2 = await input.read(path);
7383
+ text3 = await input.read(path);
7203
7384
  } catch {
7204
7385
  continue;
7205
7386
  }
7206
- for (const statement of text2.matchAll(STATEMENT)) {
7387
+ for (const statement of text3.matchAll(STATEMENT)) {
7207
7388
  const verb = statement[1].toUpperCase().replace(/\s+/g, " ");
7208
7389
  const start = statement.index ?? 0;
7209
- const rest = text2.slice(start + statement[0].length, start + STATEMENT_WINDOW);
7390
+ const rest = text3.slice(start + statement[0].length, start + STATEMENT_WINDOW);
7210
7391
  if (verb === "SELECT") {
7211
7392
  for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
7212
7393
  continue;
@@ -7222,16 +7403,16 @@ async function extractData(builder, input) {
7222
7403
  for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
7223
7404
  }
7224
7405
  }
7225
- for (const match of text2.matchAll(NS_CONST)) {
7226
- touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(text2, match.index ?? 0));
7406
+ for (const match of text3.matchAll(NS_CONST)) {
7407
+ touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(text3, match.index ?? 0));
7227
7408
  }
7228
- for (const match of text2.matchAll(NS_LITERAL)) {
7229
- touch(path, match[1], NAMESPACE, accessFor(text2, match.index ?? 0));
7409
+ for (const match of text3.matchAll(NS_LITERAL)) {
7410
+ touch(path, match[1], NAMESPACE, accessFor(text3, match.index ?? 0));
7230
7411
  }
7231
7412
  }
7232
7413
  }
7233
- function accessFor(text2, index) {
7234
- const window = text2.slice(Math.max(0, index - 160), index + 40);
7414
+ function accessFor(text3, index) {
7415
+ const window = text3.slice(Math.max(0, index - 160), index + 40);
7235
7416
  return /\b(?:transact|update|delete|create|insert|Ops)\b/.test(window) ? WRITES : READS;
7236
7417
  }
7237
7418
  async function buildCodeGraph(input) {
@@ -7368,13 +7549,13 @@ function createCodeRuntimeControlClient(options) {
7368
7549
  if (!Number.isSafeInteger(modelRequestTimeoutMs) || modelRequestTimeoutMs < 3e4 || modelRequestTimeoutMs > 30 * 6e4) {
7369
7550
  throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
7370
7551
  }
7371
- const request2 = options.fetch ?? fetch;
7552
+ const request3 = options.fetch ?? fetch;
7372
7553
  const call2 = async (path, body, timeoutMs = requestTimeoutMs) => {
7373
7554
  const timeout = AbortSignal.timeout(timeoutMs);
7374
7555
  const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
7375
7556
  let response2;
7376
7557
  try {
7377
- response2 = await request2(`${endpoint}${path}`, {
7558
+ response2 = await request3(`${endpoint}${path}`, {
7378
7559
  method: "POST",
7379
7560
  headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
7380
7561
  body: JSON.stringify(body),
@@ -7387,7 +7568,7 @@ function createCodeRuntimeControlClient(options) {
7387
7568
  }
7388
7569
  const value2 = await response2.json().catch(() => null);
7389
7570
  if (!response2.ok) {
7390
- const problem = record4(record4(value2)?.error);
7571
+ const problem = record5(record5(value2)?.error);
7391
7572
  throw new CodeRuntimeControlError(
7392
7573
  typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
7393
7574
  response2.status,
@@ -7409,12 +7590,12 @@ function createCodeRuntimeControlClient(options) {
7409
7590
  await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
7410
7591
  ),
7411
7592
  infer: async (sessionId, inference) => {
7412
- const value2 = record4(await call2(
7593
+ const value2 = record5(await call2(
7413
7594
  `/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
7414
7595
  inference,
7415
7596
  modelRequestTimeoutMs
7416
7597
  ));
7417
- if (!value2 || value2.requestId !== inference.requestId || !record4(value2.response) || !record4(value2.receipt)) {
7598
+ if (!value2 || value2.requestId !== inference.requestId || !record5(value2.response) || !record5(value2.receipt)) {
7418
7599
  throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
7419
7600
  }
7420
7601
  return value2;
@@ -7482,12 +7663,12 @@ function validateHeartbeat(version, capabilities) {
7482
7663
  }
7483
7664
  }
7484
7665
  function parseSnapshot(value2) {
7485
- const root = record4(value2);
7486
- const host = record4(root?.host);
7666
+ const root = record5(value2);
7667
+ const host = record5(root?.host);
7487
7668
  if (!host || typeof host.hostId !== "string" || typeof host.runtimeVersion !== "string" || !Number.isSafeInteger(host.lastSeenAt) || host.revokedAt !== null || !Array.isArray(root?.bindings) || root.bindings.length > 1024 || !Array.isArray(root?.commands) || root.commands.length > 64) throw invalid("heartbeat");
7488
7669
  const bindingIds = /* @__PURE__ */ new Set();
7489
7670
  const bindings = root.bindings.map((item) => {
7490
- const binding = record4(item);
7671
+ const binding = record5(item);
7491
7672
  if (!binding || typeof binding.bindingId !== "string" || typeof binding.appId !== "string" || binding.env !== "dev" && binding.env !== "prod" || typeof binding.offerId !== "string" || binding.hostId !== host.hostId || !Number.isSafeInteger(binding.generation) || Number(binding.generation) < 1 || binding.revokedAt !== null || bindingIds.has(binding.bindingId)) {
7492
7673
  throw invalid("binding");
7493
7674
  }
@@ -7497,10 +7678,10 @@ function parseSnapshot(value2) {
7497
7678
  const commandIds = /* @__PURE__ */ new Set();
7498
7679
  const commandSequences = /* @__PURE__ */ new Set();
7499
7680
  const commands = root.commands.map((item) => {
7500
- const command = record4(item);
7681
+ const command = record5(item);
7501
7682
  const binding = bindings.find((candidate) => candidate.bindingId === command?.bindingId);
7502
7683
  const sequenceKey = `${String(command?.instanceId)}:${String(command?.sequence)}`;
7503
- if (!command || typeof command.commandId !== "string" || !/^ccmd_[0-9a-f]{32}$/.test(command.commandId) || typeof command.instanceId !== "string" || typeof command.sessionId !== "string" || !/^csess_[0-9a-f]{32}$/.test(command.sessionId) || typeof command.appId !== "string" || command.env !== "dev" && command.env !== "prod" || command.hostId !== host.hostId || !binding || binding.appId !== command.appId || binding.generation !== command.bindingGeneration || !Number.isSafeInteger(command.sequence) || Number(command.sequence) < 1 || commandIds.has(command.commandId) || commandSequences.has(sequenceKey) || !["start", "prompt", "checkpoint_stop", "resume"].includes(String(command.kind)) || !record4(command.payload) || !Number.isSafeInteger(command.createdAt)) throw invalid("command");
7684
+ if (!command || typeof command.commandId !== "string" || !/^ccmd_[0-9a-f]{32}$/.test(command.commandId) || typeof command.instanceId !== "string" || typeof command.sessionId !== "string" || !/^csess_[0-9a-f]{32}$/.test(command.sessionId) || typeof command.appId !== "string" || command.env !== "dev" && command.env !== "prod" || command.hostId !== host.hostId || !binding || binding.appId !== command.appId || binding.generation !== command.bindingGeneration || !Number.isSafeInteger(command.sequence) || Number(command.sequence) < 1 || commandIds.has(command.commandId) || commandSequences.has(sequenceKey) || !["start", "prompt", "checkpoint_stop", "resume"].includes(String(command.kind)) || !record5(command.payload) || !Number.isSafeInteger(command.createdAt)) throw invalid("command");
7504
7685
  commandIds.add(command.commandId);
7505
7686
  commandSequences.add(sequenceKey);
7506
7687
  return command;
@@ -7508,10 +7689,10 @@ function parseSnapshot(value2) {
7508
7689
  return { host, bindings, commands };
7509
7690
  }
7510
7691
  async function parseSource(value2) {
7511
- const snapshot = record4(record4(value2)?.snapshot);
7692
+ const snapshot = record5(record5(value2)?.snapshot);
7512
7693
  if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
7513
7694
  const files = snapshot.files.map((value22) => {
7514
- const file = record4(value22);
7695
+ const file = record5(value22);
7515
7696
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
7516
7697
  return { path: file.path, content: file.content };
7517
7698
  });
@@ -7520,11 +7701,11 @@ async function parseSource(value2) {
7520
7701
  const aliases = /* @__PURE__ */ new Set();
7521
7702
  const references = [];
7522
7703
  for (const item of referencesValue) {
7523
- const reference = record4(item);
7704
+ const reference = record5(item);
7524
7705
  if (!reference || typeof reference.alias !== "string" || !/^[a-z][a-z0-9-]{0,39}$/.test(reference.alias) || aliases.has(reference.alias) || reference.alias === "primary" || typeof reference.repository !== "string" || typeof reference.commitSha !== "string" || typeof reference.treeDigest !== "string" || !Array.isArray(reference.files)) throw invalid("reference source");
7525
7706
  aliases.add(reference.alias);
7526
7707
  const referenceFiles = reference.files.map((entry) => {
7527
- const file = record4(entry);
7708
+ const file = record5(entry);
7528
7709
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
7529
7710
  return { path: file.path, content: file.content };
7530
7711
  });
@@ -7539,12 +7720,12 @@ async function parseSource(value2) {
7539
7720
  return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
7540
7721
  }
7541
7722
  function parseReview(value2) {
7542
- const review = record4(record4(value2)?.review);
7723
+ const review = record5(record5(value2)?.review);
7543
7724
  if (!review || !["approved", "rejected"].includes(String(review.verdict)) || typeof review.reviewDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(review.reviewDigest) || typeof review.provider !== "string" || !review.provider || typeof review.model !== "string" || !review.model || !Number.isSafeInteger(review.policyVersion) || Number(review.policyVersion) < 1) throw invalid("review");
7544
7725
  return review;
7545
7726
  }
7546
7727
  function parseCandidate(value2) {
7547
- const candidate = record4(record4(value2)?.candidate);
7728
+ const candidate = record5(record5(value2)?.candidate);
7548
7729
  if (!candidate || typeof candidate.candidateId !== "string" || !/^ccand_[0-9a-f]{32}$/.test(candidate.candidateId) || !["submitted", "approved", "published", "failed"].includes(String(candidate.status))) {
7549
7730
  throw invalid("candidate");
7550
7731
  }
@@ -7640,8 +7821,8 @@ function gitApply(cwd, patch2, check) {
7640
7821
  });
7641
7822
  let stderr = "";
7642
7823
  child.stderr.setEncoding("utf8");
7643
- child.stderr.on("data", (text2) => {
7644
- if (stderr.length < 4e3) stderr += text2.slice(0, 4e3);
7824
+ child.stderr.on("data", (text3) => {
7825
+ if (stderr.length < 4e3) stderr += text3.slice(0, 4e3);
7645
7826
  });
7646
7827
  child.once("error", reject);
7647
7828
  child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr.trim().slice(0, 500)))));
@@ -8412,7 +8593,7 @@ async function runCodeAgentAttempt(options) {
8412
8593
  }
8413
8594
  }
8414
8595
  async function handleCodeRuntimeInference(input) {
8415
- const { command, metadata: metadata2, request: request2, state: state2 } = input;
8596
+ const { command, metadata: metadata2, request: request3, state: state2 } = input;
8416
8597
  if (state2.tokens >= metadata2.maxTokensPerInteraction) {
8417
8598
  if (!state2.noticeEmitted) {
8418
8599
  state2.noticeEmitted = true;
@@ -8425,7 +8606,7 @@ async function handleCodeRuntimeInference(input) {
8425
8606
  return {
8426
8607
  protocolVersion: HARNESS_PROTOCOL_VERSION,
8427
8608
  type: "inference.response",
8428
- requestId: request2.requestId,
8609
+ requestId: request3.requestId,
8429
8610
  response: {
8430
8611
  id: `budget:${command.commandId}`,
8431
8612
  provider: "openai",
@@ -8439,9 +8620,9 @@ async function handleCodeRuntimeInference(input) {
8439
8620
  }
8440
8621
  const startedAt = Date.now();
8441
8622
  const response2 = await input.control.infer(command.sessionId, {
8442
- requestId: request2.requestId,
8623
+ requestId: request3.requestId,
8443
8624
  interactionId: command.commandId,
8444
- call: request2.call
8625
+ call: request3.call
8445
8626
  });
8446
8627
  state2.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
8447
8628
  await input.event({
@@ -8458,14 +8639,14 @@ async function handleCodeRuntimeInference(input) {
8458
8639
  return {
8459
8640
  protocolVersion: HARNESS_PROTOCOL_VERSION,
8460
8641
  type: "inference.response",
8461
- requestId: request2.requestId,
8642
+ requestId: request3.requestId,
8462
8643
  response: response2.response
8463
8644
  };
8464
8645
  }
8465
8646
  function createCodeRuntimeInference(options) {
8466
8647
  let seq = 0;
8467
8648
  return {
8468
- chat: async (request2) => {
8649
+ chat: async (request3) => {
8469
8650
  const requestId = `${options.command.commandId}:${++seq}`;
8470
8651
  const answer = await handleCodeRuntimeInference({
8471
8652
  command: options.command,
@@ -8477,7 +8658,7 @@ function createCodeRuntimeInference(options) {
8477
8658
  protocolVersion: HARNESS_PROTOCOL_VERSION,
8478
8659
  type: "inference.request",
8479
8660
  requestId,
8480
- call: request2
8661
+ call: request3
8481
8662
  }
8482
8663
  });
8483
8664
  if (answer.type !== "inference.response") throw new TypeError("brokered inference returned the wrong frame");
@@ -8640,9 +8821,9 @@ async function safePrefix(base, paths, prefix) {
8640
8821
  function descriptor(name, effect, argumentRoles) {
8641
8822
  return { name, version: 1, effect, inputSchema: { type: "object" }, argumentRoles, policyId: `odla.code.${name}.v1` };
8642
8823
  }
8643
- async function conversionPolicy(id, output) {
8824
+ async function conversionPolicy(id2, output) {
8644
8825
  const definition = {
8645
- conversionId: id,
8826
+ conversionId: id2,
8646
8827
  version: 1,
8647
8828
  output,
8648
8829
  maximumSourceBytes: 1e6,
@@ -8651,18 +8832,18 @@ async function conversionPolicy(id, output) {
8651
8832
  };
8652
8833
  return { ...definition, digest: await conversionPolicyDigest(definition) };
8653
8834
  }
8654
- async function registeredPolicy(id, registryId, values) {
8835
+ async function registeredPolicy(id2, registryId, values) {
8655
8836
  const mapping = Object.fromEntries(values.map((value2) => [value2, value2]));
8656
- return conversionPolicy(id, {
8837
+ return conversionPolicy(id2, {
8657
8838
  kind: "registered_id",
8658
8839
  registryId,
8659
8840
  registryDigest: await registeredIdRegistryDigest(mapping)
8660
8841
  });
8661
8842
  }
8662
8843
  async function conversionRegistry(policies, values) {
8663
- const registeredIds = Object.fromEntries(await Promise.all(Object.entries(values).map(async ([id, entries]) => {
8844
+ const registeredIds = Object.fromEntries(await Promise.all(Object.entries(values).map(async ([id2, entries]) => {
8664
8845
  const mapping = Object.fromEntries(entries.map((value2) => [value2, value2]));
8665
- return [id, { values: mapping, digest: await registeredIdRegistryDigest(mapping) }];
8846
+ return [id2, { values: mapping, digest: await registeredIdRegistryDigest(mapping) }];
8666
8847
  })));
8667
8848
  return createConversionRegistry({ policies, registeredIds });
8668
8849
  }
@@ -8716,10 +8897,10 @@ function decision(input, policy, approvalConsumed, tool, actionDigest) {
8716
8897
  actionDigest: actionDigest ?? (policy.outcome === "require_approval" ? policy.actionDigest : "")
8717
8898
  };
8718
8899
  }
8719
- function policyContext(context, request2, options, extra) {
8900
+ function policyContext(context, request3, options, extra) {
8720
8901
  return {
8721
8902
  lease: context.lease,
8722
- request: request2,
8903
+ request: request3,
8723
8904
  workspaceId: `workspace:${context.lease.task.attemptId}`,
8724
8905
  readers: { kind: "principals", principalIds: [options.readerId] },
8725
8906
  ...extra
@@ -8738,8 +8919,8 @@ function optionalInteger(value2) {
8738
8919
  if (!Number.isSafeInteger(value2) || value2 < 1) throw new TypeError("line bounds must be positive integers");
8739
8920
  return value2;
8740
8921
  }
8741
- function response(request2, ok, content2, details) {
8742
- return { requestId: request2.requestId, ok, content: content2, ...details ? { details } : {} };
8922
+ function response(request3, ok, content2, details) {
8923
+ return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
8743
8924
  }
8744
8925
  function workspaceGraphs(workspaceDir, paths) {
8745
8926
  const existing = cache.get(workspaceDir);
@@ -8762,19 +8943,19 @@ function renderOverview(graphs, prefix) {
8762
8943
  return [`${total} source files. Directories, largest first \u2014 read one with sandbox.list --prefix.`, ...lines].join("\n");
8763
8944
  }
8764
8945
  function renderWhereIs(graphs, symbol) {
8765
- const sites = neighbors(graphs.graph, nodeId(SYMBOL, symbol), { direction: "in", kinds: ["exports"] }).map((id) => ({
8766
- path: shortId(id),
8767
- pkg: neighbors(graphs.graph, id, { direction: "in", kinds: ["contains"] })[0],
8768
- dependents: incident(graphs.graph, id, { direction: "in", kinds: [IMPORTS] }).length
8946
+ const sites = neighbors(graphs.graph, nodeId(SYMBOL, symbol), { direction: "in", kinds: ["exports"] }).map((id2) => ({
8947
+ path: shortId(id2),
8948
+ pkg: neighbors(graphs.graph, id2, { direction: "in", kinds: ["contains"] })[0],
8949
+ dependents: incident(graphs.graph, id2, { direction: "in", kinds: [IMPORTS] }).length
8769
8950
  })).sort((left, right) => right.dependents - left.dependents || left.path.localeCompare(right.path));
8770
8951
  if (sites.length === 0) return `No exported symbol named "${symbol}". Try sandbox.search for a textual match.`;
8771
8952
  return sites.slice(0, 20).map((site) => `${site.path}${site.pkg ? ` [${shortId(site.pkg)}]` : ""} ${site.dependents} dependents`).join("\n");
8772
8953
  }
8773
8954
  function renderWhoImports(graphs, path) {
8774
- const id = nodeId(FILE, path);
8775
- const importers = neighbors(graphs.graph, id, { direction: "in", kinds: [IMPORTS] });
8955
+ const id2 = nodeId(FILE, path);
8956
+ const importers = neighbors(graphs.graph, id2, { direction: "in", kinds: [IMPORTS] });
8776
8957
  if (importers.length === 0) {
8777
- return graphs.graph.nodes.has(id) ? `Nothing imports ${path}. It is a leaf.` : `${path} is not a source file in this workspace.`;
8958
+ return graphs.graph.nodes.has(id2) ? `Nothing imports ${path}. It is a leaf.` : `${path} is not a source file in this workspace.`;
8778
8959
  }
8779
8960
  return importers.slice(0, 40).map(shortId).sort().join("\n");
8780
8961
  }
@@ -8791,11 +8972,11 @@ function renderWhoTouches(graphs, query) {
8791
8972
  ].join("\n");
8792
8973
  }).join("\n\n");
8793
8974
  }
8794
- async function read(context, request2, options, policy) {
8795
- exactKeys(request2.input, ["path", "startLine", "endLine"]);
8796
- const path = stringField(request2.input, "path");
8797
- const startLine = optionalInteger(request2.input.startLine) ?? 1;
8798
- const endLine = optionalInteger(request2.input.endLine) ?? startLine + (options.maxReadLines ?? 2e3) - 1;
8975
+ async function read(context, request3, options, policy) {
8976
+ exactKeys(request3.input, ["path", "startLine", "endLine"]);
8977
+ const path = stringField(request3.input, "path");
8978
+ const startLine = optionalInteger(request3.input.startLine) ?? 1;
8979
+ const endLine = optionalInteger(request3.input.endLine) ?? startLine + (options.maxReadLines ?? 2e3) - 1;
8799
8980
  if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
8800
8981
  throw new TypeError("requested line range exceeds its bound");
8801
8982
  }
@@ -8803,8 +8984,8 @@ async function read(context, request2, options, policy) {
8803
8984
  if (!paths.includes(path)) {
8804
8985
  throw new TypeError(`no such file in the staged workspace: "${path}". Use sandbox.overview, sandbox.where_is or sandbox.search to find the correct path.`);
8805
8986
  }
8806
- const allowed = await policy.read(policyContext(context, request2, options, { paths, path, startLine, endLine }));
8807
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
8987
+ const allowed = await policy.read(policyContext(context, request3, options, { paths, path, startLine, endLine }));
8988
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8808
8989
  const target = resolveCodePath(context.workspaceDir, path);
8809
8990
  const info = await (0, import_promises10.stat)(target);
8810
8991
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
@@ -8817,74 +8998,74 @@ async function read(context, request2, options, policy) {
8817
8998
  if (Buffer.byteLength(content2) > (options.maxReadBytes ?? 128 * 1024)) {
8818
8999
  throw new TypeError("read result exceeds its byte bound");
8819
9000
  }
8820
- return response(request2, true, content2, { path, startLine, endLine: Math.min(endLine, lines.length) });
9001
+ return response(request3, true, content2, { path, startLine, endLine: Math.min(endLine, lines.length) });
8821
9002
  }
8822
- async function list(context, request2, options, policy) {
8823
- exactKeys(request2.input, ["prefix", "maxEntries"]);
8824
- const raw = request2.input.prefix;
9003
+ async function list(context, request3, options, policy) {
9004
+ exactKeys(request3.input, ["prefix", "maxEntries"]);
9005
+ const raw = request3.input.prefix;
8825
9006
  const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
8826
- const maxEntries = optionalInteger(request2.input.maxEntries) ?? 1e3;
9007
+ const maxEntries = optionalInteger(request3.input.maxEntries) ?? 1e3;
8827
9008
  if (maxEntries > 5e3) throw new TypeError("maxEntries exceeds its bound");
8828
9009
  const paths = await registeredFiles(context.workspaceDir, 2e4);
8829
- const allowed = await policy.list(policyContext(context, request2, options, { paths, ...prefix ? { prefix } : {} }));
8830
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
9010
+ const allowed = await policy.list(policyContext(context, request3, options, { paths, ...prefix ? { prefix } : {} }));
9011
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8831
9012
  const entries = listWorkspace(paths, { ...prefix ? { prefix } : {}, maxEntries });
8832
9013
  if (!entries.length) {
8833
- return response(request2, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
9014
+ return response(request3, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
8834
9015
  }
8835
9016
  const truncated = entries.length < paths.length && entries.length === maxEntries;
8836
9017
  const hint = !prefix && paths.length > 500 ? `
8837
9018
  \u2026 ${paths.length} files total. sandbox.overview is far cheaper for orientation; use a prefix here once you know the area.` : "";
8838
9019
  return response(
8839
- request2,
9020
+ request3,
8840
9021
  true,
8841
9022
  `${entries.join("\n")}${truncated ? `
8842
9023
  \u2026 truncated at ${maxEntries} entries` : ""}${hint}`,
8843
9024
  { count: entries.length, truncated }
8844
9025
  );
8845
9026
  }
8846
- async function search(context, request2, options, policy) {
8847
- exactKeys(request2.input, ["query", "prefix", "maxResults", "caseSensitive"]);
8848
- const query = stringField(request2.input, "query");
9027
+ async function search(context, request3, options, policy) {
9028
+ exactKeys(request3.input, ["query", "prefix", "maxResults", "caseSensitive"]);
9029
+ const query = stringField(request3.input, "query");
8849
9030
  if (query.length > 512) throw new TypeError("search query exceeds its bound");
8850
- const raw = request2.input.prefix;
9031
+ const raw = request3.input.prefix;
8851
9032
  const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
8852
- const maxResults = optionalInteger(request2.input.maxResults) ?? 100;
9033
+ const maxResults = optionalInteger(request3.input.maxResults) ?? 100;
8853
9034
  if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
8854
- const caseSensitive = request2.input.caseSensitive === void 0 ? true : request2.input.caseSensitive === true;
9035
+ const caseSensitive = request3.input.caseSensitive === void 0 ? true : request3.input.caseSensitive === true;
8855
9036
  const paths = await registeredFiles(context.workspaceDir, 2e4);
8856
- const allowed = await policy.search(policyContext(context, request2, options, { paths, query, ...prefix ? { prefix } : {} }));
8857
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
9037
+ const allowed = await policy.search(policyContext(context, request3, options, { paths, query, ...prefix ? { prefix } : {} }));
9038
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8858
9039
  const matches = await searchWorkspace(context.workspaceDir, paths, {
8859
9040
  query,
8860
9041
  maxResults,
8861
9042
  caseSensitive,
8862
9043
  ...prefix ? { prefix } : {}
8863
9044
  });
8864
- if (!matches.length) return response(request2, true, `No match for "${query}".`, { count: 0 });
8865
- return response(request2, true, matches.map((match) => `${match.path}:${match.line}: ${match.text}`).join("\n"), {
9045
+ if (!matches.length) return response(request3, true, `No match for "${query}".`, { count: 0 });
9046
+ return response(request3, true, matches.map((match) => `${match.path}:${match.line}: ${match.text}`).join("\n"), {
8866
9047
  count: matches.length
8867
9048
  });
8868
9049
  }
8869
- async function graphQuery(context, request2, options, policy) {
8870
- exactKeys(request2.input, ["query"]);
8871
- const raw = request2.input.query;
9050
+ async function graphQuery(context, request3, options, policy) {
9051
+ exactKeys(request3.input, ["query"]);
9052
+ const raw = request3.input.query;
8872
9053
  const query = typeof raw === "string" ? raw : "";
8873
9054
  if (query.length > 512) throw new TypeError("query exceeds its bound");
8874
- const allowed = await policy.graph(policyContext(context, request2, options, {
8875
- tool: request2.tool,
9055
+ const allowed = await policy.graph(policyContext(context, request3, options, {
9056
+ tool: request3.tool,
8876
9057
  selector: query
8877
9058
  }));
8878
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
9059
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8879
9060
  const paths = await registeredFiles(context.workspaceDir, 2e4);
8880
9061
  const graphs = await workspaceGraphs(context.workspaceDir, paths);
8881
- if (request2.tool === "sandbox.overview") {
8882
- return response(request2, true, renderOverview(graphs, query || void 0));
9062
+ if (request3.tool === "sandbox.overview") {
9063
+ return response(request3, true, renderOverview(graphs, query || void 0));
8883
9064
  }
8884
- if (!query) throw new TypeError(`${request2.tool} requires a query`);
8885
- if (request2.tool === "sandbox.where_is") return response(request2, true, renderWhereIs(graphs, query));
8886
- if (request2.tool === "sandbox.who_imports") return response(request2, true, renderWhoImports(graphs, query));
8887
- return response(request2, true, renderWhoTouches(graphs, query));
9065
+ if (!query) throw new TypeError(`${request3.tool} requires a query`);
9066
+ if (request3.tool === "sandbox.where_is") return response(request3, true, renderWhereIs(graphs, query));
9067
+ if (request3.tool === "sandbox.who_imports") return response(request3, true, renderWhoImports(graphs, query));
9068
+ return response(request3, true, renderWhoTouches(graphs, query));
8888
9069
  }
8889
9070
  function createCodeToolBroker(options) {
8890
9071
  validateOptions(options);
@@ -8892,24 +9073,24 @@ function createCodeToolBroker(options) {
8892
9073
  const policy = createCodePolicyGate(options);
8893
9074
  let tail = Promise.resolve();
8894
9075
  return {
8895
- execute(context, request2) {
8896
- const result = tail.then(() => route(context, request2, options, recipes, policy));
9076
+ execute(context, request3) {
9077
+ const result = tail.then(() => route2(context, request3, options, recipes, policy));
8897
9078
  tail = result.then(() => void 0, () => void 0);
8898
9079
  return result;
8899
9080
  }
8900
9081
  };
8901
9082
  }
8902
- async function route(context, request2, options, recipes, policy) {
9083
+ async function route2(context, request3, options, recipes, policy) {
8903
9084
  try {
8904
9085
  if (context.signal?.aborted) throw new TypeError("tool request was cancelled");
8905
- if (request2.tool === "sandbox.read") return await read(context, request2, options, policy);
8906
- if (request2.tool === "sandbox.list") return await list(context, request2, options, policy);
8907
- if (request2.tool === "sandbox.search") return await search(context, request2, options, policy);
8908
- if (GRAPH_TOOLS.has(request2.tool)) return await graphQuery(context, request2, options, policy);
8909
- if (request2.tool === "sandbox.apply_patch") return await patch(context, request2, options, policy);
8910
- return await recipe(context, request2, options, recipes, policy);
9086
+ if (request3.tool === "sandbox.read") return await read(context, request3, options, policy);
9087
+ if (request3.tool === "sandbox.list") return await list(context, request3, options, policy);
9088
+ if (request3.tool === "sandbox.search") return await search(context, request3, options, policy);
9089
+ if (GRAPH_TOOLS.has(request3.tool)) return await graphQuery(context, request3, options, policy);
9090
+ if (request3.tool === "sandbox.apply_patch") return await patch(context, request3, options, policy);
9091
+ return await recipe(context, request3, options, recipes, policy);
8911
9092
  } catch (reason) {
8912
- return response(request2, false, toolFailureMessage(reason));
9093
+ return response(request3, false, toolFailureMessage(reason));
8913
9094
  }
8914
9095
  }
8915
9096
  function toolFailureMessage(reason) {
@@ -8921,34 +9102,34 @@ function toolFailureMessage(reason) {
8921
9102
  if (code === "EACCES" || code === "EPERM") return "that path is not readable through this tool";
8922
9103
  return "tool failed closed";
8923
9104
  }
8924
- async function patch(context, request2, options, policy) {
8925
- exactKeys(request2.input, ["patch"]);
8926
- const value2 = stringField(request2.input, "patch");
9105
+ async function patch(context, request3, options, policy) {
9106
+ exactKeys(request3.input, ["patch"]);
9107
+ const value2 = stringField(request3.input, "patch");
8927
9108
  const paths = validateCodePatch(value2, options.maxPatchBytes ?? 256 * 1024);
8928
9109
  if (paths.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
8929
9110
  throw new TypeError("patch targets a read-only reference source");
8930
9111
  }
8931
- const allowed = await policy.patch(policyContext(context, request2, options, { patch: value2 }));
8932
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
9112
+ const allowed = await policy.patch(policyContext(context, request3, options, { patch: value2 }));
9113
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8933
9114
  await applyCodePatch(context.workspaceDir, value2, paths);
8934
- return response(request2, true, `Applied patch to ${paths.length} file(s).`, { paths });
9115
+ return response(request3, true, `Applied patch to ${paths.length} file(s).`, { paths });
8935
9116
  }
8936
- async function recipe(context, request2, options, recipes, policy) {
8937
- exactKeys(request2.input, ["recipeId"]);
8938
- const recipeId = stringField(request2.input, "recipeId");
9117
+ async function recipe(context, request3, options, recipes, policy) {
9118
+ exactKeys(request3.input, ["recipeId"]);
9119
+ const recipeId = stringField(request3.input, "recipeId");
8939
9120
  const digestLimits = {
8940
9121
  maxFiles: options.maxRecipeWorkspaceFiles ?? 2e4,
8941
9122
  maxBytes: options.maxRecipeWorkspaceBytes ?? 512 * 1024 * 1024
8942
9123
  };
8943
9124
  const sourceDigest = await digestStagedWorkspace(context.workspaceDir, digestLimits);
8944
- const allowed = await policy.recipe(policyContext(context, request2, options, {
9125
+ const allowed = await policy.recipe(policyContext(context, request3, options, {
8945
9126
  recipeIds: [...recipes.keys()].sort(),
8946
9127
  recipeId,
8947
9128
  sourceDigest
8948
9129
  }));
8949
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
9130
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8950
9131
  const selected = recipes.get(recipeId);
8951
- if (!selected) return response(request2, false, "build recipe is not registered");
9132
+ if (!selected) return response(request3, false, "build recipe is not registered");
8952
9133
  const staged = await stageWorkspace(context.workspaceDir, {
8953
9134
  maxFiles: digestLimits.maxFiles,
8954
9135
  maxBytes: digestLimits.maxBytes
@@ -8965,7 +9146,7 @@ async function recipe(context, request2, options, recipes, policy) {
8965
9146
  const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
8966
9147
  const ok = result.exitCode === 0 && !result.outputLimitExceeded && !result.timedOut;
8967
9148
  const status = result.timedOut ? "timed out" : result.outputLimitExceeded ? "exceeded output limit" : ok ? "passed" : `failed with exit ${result.exitCode}`;
8968
- return response(request2, ok, `Recipe ${recipeId} ${status}.${output ? `
9149
+ return response(request3, ok, `Recipe ${recipeId} ${status}.${output ? `
8969
9150
  ${output}` : ""}`, {
8970
9151
  recipeId,
8971
9152
  exitCode: result.exitCode,
@@ -9014,7 +9195,7 @@ async function runGoal(spec, attempt) {
9014
9195
  const startedAt = now();
9015
9196
  const attempts = [];
9016
9197
  const boardErrors = [];
9017
- const emit3 = async (event) => {
9198
+ const emit4 = async (event) => {
9018
9199
  if (!spec.onEvent) return;
9019
9200
  try {
9020
9201
  await spec.onEvent(event);
@@ -9027,7 +9208,7 @@ async function runGoal(spec, attempt) {
9027
9208
  let costKnown = false;
9028
9209
  const finish2 = async (stoppedReason) => {
9029
9210
  const met = stoppedReason === "proof_passed";
9030
- await emit3(met ? { type: "goal_met", attempts: attempts.length, tokens, ...costKnown ? { costUsd } : {} } : {
9211
+ await emit4(met ? { type: "goal_met", attempts: attempts.length, tokens, ...costKnown ? { costUsd } : {} } : {
9031
9212
  type: "goal_abandoned",
9032
9213
  reason: stoppedReason,
9033
9214
  attempts: attempts.length,
@@ -9048,7 +9229,7 @@ async function runGoal(spec, attempt) {
9048
9229
  if (spec.signal?.aborted) return finish2("cancelled");
9049
9230
  if (spec.budget.deadline !== void 0 && now() >= spec.budget.deadline) return finish2("deadline");
9050
9231
  const prompt = index === 1 ? openingPrompt(spec) : retryPrompt(spec, attempts.at(-1));
9051
- await emit3({ type: "attempt_started", attempt: index, prompt });
9232
+ await emit4({ type: "attempt_started", attempt: index, prompt });
9052
9233
  const outcome = await attempt({
9053
9234
  attempt: index,
9054
9235
  prompt,
@@ -9068,7 +9249,7 @@ async function runGoal(spec, attempt) {
9068
9249
  ...outcome.error === void 0 ? {} : { error: outcome.error }
9069
9250
  });
9070
9251
  if (outcome.gatePassed) return finish2("proof_passed");
9071
- await emit3({
9252
+ await emit4({
9072
9253
  type: "attempt_failed",
9073
9254
  attempt: index,
9074
9255
  feedback: outcome.feedback,
@@ -9117,7 +9298,7 @@ function createCodeRuntimeToolBroker(input, lease, role) {
9117
9298
  readerId: `code-session:${lease.task.taskId}`,
9118
9299
  readOnlyPrefixes: [".odla-references"]
9119
9300
  });
9120
- return role === "coding" ? broker : { execute: (context, request2) => request2.tool === "sandbox.read" ? broker.execute(context, request2) : Promise.resolve({ requestId: request2.requestId, ok: false, content: "review sessions are read-only" }) };
9301
+ return role === "coding" ? broker : { execute: (context, request3) => request3.tool === "sandbox.read" ? broker.execute(context, request3) : Promise.resolve({ requestId: request3.requestId, ok: false, content: "review sessions are read-only" }) };
9121
9302
  }
9122
9303
  function codeGoalSpec(payload) {
9123
9304
  const goal = payload.goal;
@@ -9275,7 +9456,7 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
9275
9456
  const bounded = event.type === "message" ? { ...event, body: event.body.trim().slice(0, 2e4) || `${event.actor} event` } : event;
9276
9457
  await control.appendSessionEvent(command.sessionId, eventId, bounded);
9277
9458
  }
9278
- var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record4, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, CodePiRuntimeEngine;
9459
+ var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record5, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, CodePiRuntimeEngine;
9279
9460
  var init_chunk_ANNX7VGK = __esm({
9280
9461
  "../harness/dist/chunk-ANNX7VGK.js"() {
9281
9462
  "use strict";
@@ -9353,7 +9534,7 @@ var init_chunk_ANNX7VGK = __esm({
9353
9534
  code;
9354
9535
  name = "CodeRuntimeControlError";
9355
9536
  };
9356
- record4 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
9537
+ record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
9357
9538
  invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
9358
9539
  RESERVED = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
9359
9540
  SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
@@ -9500,7 +9681,7 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
9500
9681
  sourceDigest: "payload"
9501
9682
  });
9502
9683
  cache = /* @__PURE__ */ new Map();
9503
- shortId = (id) => id.slice(id.indexOf(":") + 1);
9684
+ shortId = (id2) => id2.slice(id2.indexOf(":") + 1);
9504
9685
  GRAPH_TOOLS = /* @__PURE__ */ new Set([
9505
9686
  "sandbox.overview",
9506
9687
  "sandbox.where_is",
@@ -9738,18 +9919,18 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
9738
9919
  /** Report every brokered effect as it starts and finishes. */
9739
9920
  #observed(command, active, broker) {
9740
9921
  return {
9741
- execute: async (context, request2) => {
9922
+ execute: async (context, request3) => {
9742
9923
  const startedAt = Date.now();
9743
9924
  await this.#event(
9744
9925
  command,
9745
- { type: "tool", phase: "started", tool: request2.tool },
9926
+ { type: "tool", phase: "started", tool: request3.tool },
9746
9927
  active.conversationRefs
9747
9928
  ).catch(() => void 0);
9748
- const response2 = await broker.execute(context, request2);
9929
+ const response2 = await broker.execute(context, request3);
9749
9930
  await this.#event(command, {
9750
9931
  type: "tool",
9751
9932
  phase: "completed",
9752
- tool: request2.tool,
9933
+ tool: request3.tool,
9753
9934
  ok: response2.ok,
9754
9935
  durationMs: Date.now() - startedAt
9755
9936
  }, active.conversationRefs).catch(() => void 0);
@@ -9884,7 +10065,7 @@ async function waitForHostedPoll(milliseconds, signal) {
9884
10065
  }
9885
10066
  function isValidHostedSecurityPlan(value2, env) {
9886
10067
  if (!value2 || value2.env !== env || !/^sha256:[a-f0-9]{64}$/.test(value2.planDigest) || value2.consentContract !== "odla.hosted-security-consent.v1" || value2.reportProjection !== "odla.hosted-security-report.v1" || value2.redactionContract !== "odla.best-effort-credential-pattern-redaction.v1" || typeof value2.promptBundle !== "string" || value2.promptBundle.length < 1 || value2.promptBundle.length > 100 || typeof value2.ready !== "boolean" || typeof value2.independent !== "boolean" || value2.sourceDisclosure !== "redacted" || value2.targetExecution !== false || !Number.isSafeInteger(value2.reportRetentionDays) || value2.reportRetentionDays < 1) return false;
9887
- const validRoute = (route2, purpose) => !!route2 && route2.purpose === purpose && typeof route2.enabled === "boolean" && typeof route2.credentialReady === "boolean" && typeof route2.provider === "string" && route2.provider.length > 0 && route2.provider.length <= 100 && typeof route2.model === "string" && route2.model.length > 0 && route2.model.length <= 200 && Number.isSafeInteger(route2.policyVersion) && route2.policyVersion >= 1 && Number.isSafeInteger(route2.maxCallsPerRun) && route2.maxCallsPerRun >= 1 && Number.isSafeInteger(route2.maxInputBytes) && route2.maxInputBytes >= 1 && Number.isSafeInteger(route2.maxOutputTokens) && route2.maxOutputTokens >= 1;
10068
+ const validRoute = (route3, purpose) => !!route3 && route3.purpose === purpose && typeof route3.enabled === "boolean" && typeof route3.credentialReady === "boolean" && typeof route3.provider === "string" && route3.provider.length > 0 && route3.provider.length <= 100 && typeof route3.model === "string" && route3.model.length > 0 && route3.model.length <= 200 && Number.isSafeInteger(route3.policyVersion) && route3.policyVersion >= 1 && Number.isSafeInteger(route3.maxCallsPerRun) && route3.maxCallsPerRun >= 1 && Number.isSafeInteger(route3.maxInputBytes) && route3.maxInputBytes >= 1 && Number.isSafeInteger(route3.maxOutputTokens) && route3.maxOutputTokens >= 1;
9888
10069
  return validRoute(value2.routes?.discovery, "security.discovery") && validRoute(value2.routes?.validation, "security.validation");
9889
10070
  }
9890
10071
  function hostedSecurityCredential(value2) {
@@ -10210,20 +10391,20 @@ async function runCodeRuntime(input) {
10210
10391
  }
10211
10392
  }
10212
10393
  function parseConnection(value2, appId, appEnv) {
10213
- const root = record5(value2);
10214
- const host = record5(root?.host);
10215
- const offer = record5(root?.offer);
10216
- const binding = record5(root?.binding);
10394
+ const root = record6(value2);
10395
+ const host = record6(root?.host);
10396
+ const offer = record6(root?.offer);
10397
+ const binding = record6(root?.binding);
10217
10398
  if (!root || typeof root.token !== "string" || !/^odla_code_host_[0-9a-f]{64}$/.test(root.token) || typeof root.resumed !== "boolean" || !host || !/^chost_[0-9a-f]{32}$/.test(String(host.hostId)) || typeof host.name !== "string" || !offer || !Number.isSafeInteger(offer.slots) || !binding || typeof binding.appId !== "string" || !binding.appId || appId && binding.appId !== appId || binding.env !== appEnv || !Number.isSafeInteger(binding.generation)) {
10218
10399
  throw new Error("connect Code host returned an invalid response");
10219
10400
  }
10220
10401
  return root;
10221
10402
  }
10222
10403
  function apiFailure(action2, status, value2) {
10223
- const message2 = record5(record5(value2)?.error)?.message;
10404
+ const message2 = record6(record6(value2)?.error)?.message;
10224
10405
  return `${action2} failed (${status})${typeof message2 === "string" ? `: ${message2}` : ""}`;
10225
10406
  }
10226
- function record5(value2) {
10407
+ function record6(value2) {
10227
10408
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
10228
10409
  }
10229
10410
  var import_node_fs16, import_node_os3, import_node_path15;
@@ -10486,9 +10667,9 @@ async function credentialCommand(parsed, deps = {}) {
10486
10667
  }, doFetch, out, { optionalProjectCapabilities: ["app.manage"] });
10487
10668
  const base = `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials`;
10488
10669
  if (action2 === "revoke") {
10489
- const id = parsed.positionals[2];
10490
- if (!id) throw new Error("credentials revoke requires the exact receipt id from credentials list");
10491
- const response3 = await doFetch(`${base}/${encodeURIComponent(id)}`, {
10670
+ const id2 = parsed.positionals[2];
10671
+ if (!id2) throw new Error("credentials revoke requires the exact receipt id from credentials list");
10672
+ const response3 = await doFetch(`${base}/${encodeURIComponent(id2)}`, {
10492
10673
  method: "DELETE",
10493
10674
  headers: { authorization: `Bearer ${token}` }
10494
10675
  });
@@ -10611,6 +10792,12 @@ Usage:
10611
10792
  odla-ai context save <name> [--platform <url>] [--app <id>] [--env <name>] [--json]
10612
10793
  odla-ai context remove <name> --yes [--json]
10613
10794
  odla-ai o11y status [--app <id>] [--context <name>] [--platform https://odla.ai] [--env prod] [--minutes 60] [--json]
10795
+ odla-ai monitor plan [--config odla.config.mjs] [--env prod] [--json]
10796
+ odla-ai monitor apply [--config odla.config.mjs] [--env prod] [--json] [--yes]
10797
+ odla-ai monitor run <probe-id> [--app <id>] [--env prod] [--json]
10798
+ odla-ai monitor status [--app <id>] [--context <name>] [--env prod] [--json]
10799
+ odla-ai monitor incidents [--app <id>] [--env prod] [--limit 100] [--runs] [--json]
10800
+ odla-ai monitor report [--app <id>] [--env prod] [--period daily|weekly] [--json]
10614
10801
  odla-ai platform status [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
10615
10802
  odla-ai whoami [--context <name>] [--platform https://odla.ai] [--json]
10616
10803
  odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
@@ -10750,6 +10937,9 @@ Commands:
10750
10937
  canary, collector ingest/scheduler trust, Cloudflare-owned
10751
10938
  runtime metrics, and a machine verdict.
10752
10939
  --json keeps auth progress on stderr for unattended agents.
10940
+ monitor Reconcile checked-in Kitesurf routes, rolling SLOs, spike/trend
10941
+ policies, and email digests; run probes manually and expose
10942
+ stable status, incident, and report JSON to agents and CI.
10753
10943
  platform Read canonical fleet health, releases, provider load/freshness,
10754
10944
  explicit unknowns, and next actions through a read-only grant.
10755
10945
  provision Register services, compose integrations, persist credentials, optionally push secrets.
@@ -10972,7 +11162,7 @@ async function discussList(ctx, parsed) {
10972
11162
  }
10973
11163
  });
10974
11164
  }
10975
- async function discussRead(ctx, id, parsed) {
11165
+ async function discussRead(ctx, id2, parsed) {
10976
11166
  const requestedLimit = stringOpt(parsed.options.limit);
10977
11167
  const requestedOffset = stringOpt(parsed.options.offset);
10978
11168
  if (requestedLimit !== void 0 || requestedOffset !== void 0) {
@@ -10980,7 +11170,7 @@ async function discussRead(ctx, id, parsed) {
10980
11170
  limit: requestedLimit ?? "200",
10981
11171
  offset: requestedOffset ?? "0"
10982
11172
  });
10983
- const page2 = await request(ctx, "GET", `/topics/${encodeURIComponent(id)}?${query}`);
11173
+ const page2 = await request(ctx, "GET", `/topics/${encodeURIComponent(id2)}?${query}`);
10984
11174
  emit(
10985
11175
  ctx,
10986
11176
  page2,
@@ -11000,7 +11190,7 @@ async function discussRead(ctx, id, parsed) {
11000
11190
  const page2 = await request(
11001
11191
  ctx,
11002
11192
  "GET",
11003
- `/topics/${encodeURIComponent(id)}?limit=200&offset=${offset}`
11193
+ `/topics/${encodeURIComponent(id2)}?limit=200&offset=${offset}`
11004
11194
  );
11005
11195
  topic = page2.topic;
11006
11196
  for (const post of page2.posts) posts.set(post.id, post);
@@ -11042,20 +11232,20 @@ async function discussPost(ctx, parsed) {
11042
11232
  });
11043
11233
  emit(ctx, created, () => ctx.out.log(`opened topic ${created.id}`));
11044
11234
  }
11045
- async function discussReply(ctx, id, parsed) {
11235
+ async function discussReply(ctx, id2, parsed) {
11046
11236
  const created = await request(
11047
11237
  ctx,
11048
11238
  "POST",
11049
- `/topics/${encodeURIComponent(id)}/replies`,
11239
+ `/topics/${encodeURIComponent(id2)}/replies`,
11050
11240
  { ...content(parsed), mutationId: writeMutationId(parsed) }
11051
11241
  );
11052
11242
  emit(ctx, created, () => ctx.out.log(`replied ${created.id}`));
11053
11243
  }
11054
- async function discussResolve(ctx, id, resolved, parsed) {
11244
+ async function discussResolve(ctx, id2, resolved, parsed) {
11055
11245
  const result = await request(
11056
11246
  ctx,
11057
11247
  "PATCH",
11058
- `/topics/${encodeURIComponent(id)}`,
11248
+ `/topics/${encodeURIComponent(id2)}`,
11059
11249
  { resolved, mutationId: writeMutationId(parsed) }
11060
11250
  );
11061
11251
  emit(ctx, result, () => ctx.out.log(`${resolved ? "resolved" : "reopened"} ${result.id}`));
@@ -11333,9 +11523,9 @@ var init_discuss_watch = __esm({
11333
11523
  });
11334
11524
 
11335
11525
  // src/discuss-command.ts
11336
- function requireId(id, action2) {
11337
- if (!id) throw new Error(`"discuss ${action2}" needs a topic id`);
11338
- return id;
11526
+ function requireId(id2, action2) {
11527
+ if (!id2) throw new Error(`"discuss ${action2}" needs a topic id`);
11528
+ return id2;
11339
11529
  }
11340
11530
  async function buildContext(parsed, deps) {
11341
11531
  const context = await resolveOperatorContext(parsed, {
@@ -11370,7 +11560,7 @@ async function buildContext(parsed, deps) {
11370
11560
  async function discussCommand(parsed, deps = {}) {
11371
11561
  assertArgs(parsed, ALLOWED, 3);
11372
11562
  const action2 = parsed.positionals[1];
11373
- const id = parsed.positionals[2];
11563
+ const id2 = parsed.positionals[2];
11374
11564
  if (!action2) throw new Error('"discuss" needs an action. Run "odla-ai help".');
11375
11565
  const ctx = await buildContext(parsed, deps);
11376
11566
  switch (action2) {
@@ -11380,17 +11570,17 @@ async function discussCommand(parsed, deps = {}) {
11380
11570
  case "topics":
11381
11571
  return discussList(ctx, parsed);
11382
11572
  case "read":
11383
- return discussRead(ctx, requireId(id, "read"), parsed);
11573
+ return discussRead(ctx, requireId(id2, "read"), parsed);
11384
11574
  case "post":
11385
11575
  return discussPost(ctx, parsed);
11386
11576
  case "reply":
11387
- return discussReply(ctx, requireId(id, "reply"), parsed);
11577
+ return discussReply(ctx, requireId(id2, "reply"), parsed);
11388
11578
  case "resolve":
11389
- return discussResolve(ctx, requireId(id, "resolve"), parsed.options.reopen !== true, parsed);
11579
+ return discussResolve(ctx, requireId(id2, "resolve"), parsed.options.reopen !== true, parsed);
11390
11580
  case "who":
11391
11581
  return discussWho(ctx, parsed);
11392
11582
  case "watch": {
11393
- const result = await discussWatch(ctx, id, parsed);
11583
+ const result = await discussWatch(ctx, id2, parsed);
11394
11584
  if (!result.found) throw new WatchTimeoutError(result.cursor);
11395
11585
  return;
11396
11586
  }
@@ -11472,8 +11662,8 @@ function collectFields(parsed, allowClear) {
11472
11662
  if (allowClear) out[spec.key] = null;
11473
11663
  continue;
11474
11664
  }
11475
- const text2 = stringOpt(value2);
11476
- out[spec.key] = spec.num ? Number(text2) : text2;
11665
+ const text3 = stringOpt(value2);
11666
+ out[spec.key] = spec.num ? Number(text3) : text3;
11477
11667
  }
11478
11668
  return out;
11479
11669
  }
@@ -11486,31 +11676,31 @@ function collectEntityFields(entity, parsed, allowClear) {
11486
11676
  if (entity === "task" && fields.column === "ready") fields.column = "todo";
11487
11677
  return fields;
11488
11678
  }
11489
- function statusCol(entity, record9) {
11490
- if (entity === "bug") return `${record9.status ?? ""}/${record9.severity ?? ""}`;
11679
+ function statusCol(entity, record11) {
11680
+ if (entity === "bug") return `${record11.status ?? ""}/${record11.severity ?? ""}`;
11491
11681
  if (entity === "task") {
11492
- const state2 = record9.column === "todo" ? "ready" : String(record9.column ?? "");
11493
- return record9.revision ? `${state2}; r${record9.revision}` : state2;
11682
+ const state2 = record11.column === "todo" ? "ready" : String(record11.column ?? "");
11683
+ return record11.revision ? `${state2}; r${record11.revision}` : state2;
11494
11684
  }
11495
- return String(record9.status ?? "");
11685
+ return String(record11.status ?? "");
11496
11686
  }
11497
- function referenceMarkup(entity, record9) {
11498
- const label = (record9.title?.trim() || `${entity} ${record9.id}`).replaceAll("]", ")");
11499
- return `@[${label}](pm:${entity}/${record9.id})`;
11687
+ function referenceMarkup(entity, record11) {
11688
+ const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
11689
+ return `@[${label}](pm:${entity}/${record11.id})`;
11500
11690
  }
11501
- function studioRecordUrl(ctx, entity, id) {
11691
+ function studioRecordUrl(ctx, entity, id2) {
11502
11692
  return new URL(
11503
- `/studio/pm/${STUDIO_SECTION[entity]}/${encodeURIComponent(id)}`,
11693
+ `/studio/pm/${STUDIO_SECTION[entity]}/${encodeURIComponent(id2)}`,
11504
11694
  ctx.platformUrl
11505
11695
  ).href;
11506
11696
  }
11507
- function studioRecordLink(ctx, entity, record9) {
11508
- const label = (record9.title?.trim() || `${entity} ${record9.id}`).replaceAll("]", ")");
11509
- return `[${label}](${studioRecordUrl(ctx, entity, record9.id)})`;
11697
+ function studioRecordLink(ctx, entity, record11) {
11698
+ const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
11699
+ return `[${label}](${studioRecordUrl(ctx, entity, record11.id)})`;
11510
11700
  }
11511
- function printRecord(ctx, entity, record9) {
11701
+ function printRecord(ctx, entity, record11) {
11512
11702
  ctx.out.log(
11513
- `${record9.id} [${statusCol(entity, record9)}] ${record9.appId} ${studioRecordLink(ctx, entity, record9)}`
11703
+ `${record11.id} [${statusCol(entity, record11)}] ${record11.appId} ${studioRecordLink(ctx, entity, record11)}`
11514
11704
  );
11515
11705
  }
11516
11706
  function emit2(ctx, value2, human) {
@@ -11605,52 +11795,52 @@ async function pmAdd(ctx, entity, parsed) {
11605
11795
  input,
11606
11796
  mutationId: writeMutationId2(parsed)
11607
11797
  });
11608
- const record9 = { id: res.id, appId, title: String(input.title) };
11609
- emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record9)}`));
11798
+ const record11 = { id: res.id, appId, title: String(input.title) };
11799
+ emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record11)}`));
11610
11800
  }
11611
- async function pmGet(ctx, entity, id) {
11612
- const { record: record9 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
11613
- emit2(ctx, record9, () => printRecord(ctx, entity, record9));
11801
+ async function pmGet(ctx, entity, id2) {
11802
+ const { record: record11 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id2)}`);
11803
+ emit2(ctx, record11, () => printRecord(ctx, entity, record11));
11614
11804
  }
11615
- async function pmReference(ctx, entity, id) {
11616
- const { record: record9 } = await pmRequest(
11805
+ async function pmReference(ctx, entity, id2) {
11806
+ const { record: record11 } = await pmRequest(
11617
11807
  ctx,
11618
11808
  "GET",
11619
- `/${entity}/${encodeURIComponent(id)}`
11809
+ `/${entity}/${encodeURIComponent(id2)}`
11620
11810
  );
11621
- const markup = referenceMarkup(entity, record9);
11622
- emit2(ctx, { kind: `pm:${entity}`, id: record9.id, label: record9.title ?? "", markup }, () => {
11811
+ const markup = referenceMarkup(entity, record11);
11812
+ emit2(ctx, { kind: `pm:${entity}`, id: record11.id, label: record11.title ?? "", markup }, () => {
11623
11813
  ctx.out.log(markup);
11624
11814
  });
11625
11815
  }
11626
- async function pmSet(ctx, entity, id, parsed) {
11816
+ async function pmSet(ctx, entity, id2, parsed) {
11627
11817
  const patch2 = collectEntityFields(entity, parsed, true);
11628
11818
  if (Object.keys(patch2).length === 0)
11629
11819
  throw new Error("pm set needs at least one field flag (e.g. --status doing, --assignee me, --no-assignee)");
11630
- const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
11820
+ const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id2)}`, {
11631
11821
  patch: patch2,
11632
11822
  mutationId: writeMutationId2(parsed)
11633
11823
  });
11634
11824
  emit2(ctx, res, () => {
11635
- if (!res.record) return ctx.out.log(`updated ${entity} ${id}`);
11825
+ if (!res.record) return ctx.out.log(`updated ${entity} ${id2}`);
11636
11826
  ctx.out.log(`${entity}: ${studioRecordLink(ctx, entity, res.record)} \u2192 ${statusCol(entity, res.record)}`);
11637
11827
  });
11638
11828
  }
11639
- async function pmDone(ctx, entity, id, parsed) {
11829
+ async function pmDone(ctx, entity, id2, parsed) {
11640
11830
  const decisionId = stringOpt(parsed.options.decision);
11641
11831
  if (decisionId && entity !== "bug") throw new Error("--decision is only valid when completing a bug");
11642
11832
  const patch2 = { ...DONE[entity], ...decisionId ? { decisionId } : {} };
11643
- const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
11833
+ const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id2)}`, {
11644
11834
  patch: patch2,
11645
11835
  mutationId: writeMutationId2(parsed)
11646
11836
  });
11647
11837
  emit2(ctx, res, () => {
11648
- const label = res.record ? studioRecordLink(ctx, entity, res.record) : id;
11838
+ const label = res.record ? studioRecordLink(ctx, entity, res.record) : id2;
11649
11839
  const state2 = res.record ? statusCol(entity, res.record) : "done";
11650
11840
  ctx.out.log(`${entity}: ${label} \u2192 ${state2}`);
11651
11841
  });
11652
11842
  }
11653
- async function pmTaskLifecycle(ctx, id, action2, parsed) {
11843
+ async function pmTaskLifecycle(ctx, id2, action2, parsed) {
11654
11844
  const rawRevision = stringOpt(parsed.options["expected-revision"]);
11655
11845
  const expectedRevision = Number(rawRevision);
11656
11846
  if (!rawRevision || !Number.isSafeInteger(expectedRevision) || expectedRevision < 1) {
@@ -11660,7 +11850,7 @@ async function pmTaskLifecycle(ctx, id, action2, parsed) {
11660
11850
  const res = action2 === "ready" ? await pmRequest(
11661
11851
  ctx,
11662
11852
  "PATCH",
11663
- `/task/${encodeURIComponent(id)}`,
11853
+ `/task/${encodeURIComponent(id2)}`,
11664
11854
  {
11665
11855
  patch: {
11666
11856
  ...collectEntityFields("task", parsed, true),
@@ -11672,12 +11862,12 @@ async function pmTaskLifecycle(ctx, id, action2, parsed) {
11672
11862
  ) : await pmRequest(
11673
11863
  ctx,
11674
11864
  "POST",
11675
- `/task/${encodeURIComponent(id)}/${action2}`,
11865
+ `/task/${encodeURIComponent(id2)}/${action2}`,
11676
11866
  { expectedRevision, mutationId }
11677
11867
  );
11678
11868
  emit2(ctx, res, () => {
11679
11869
  const state2 = res.record ? statusCol("task", res.record) : action2;
11680
- const label = res.record ? studioRecordLink(ctx, "task", res.record) : id;
11870
+ const label = res.record ? studioRecordLink(ctx, "task", res.record) : id2;
11681
11871
  ctx.out.log(`task: ${label} \u2192 ${state2}`);
11682
11872
  });
11683
11873
  }
@@ -11706,9 +11896,9 @@ async function pmNext(ctx, parsed) {
11706
11896
  const result = {
11707
11897
  appId,
11708
11898
  projectId,
11709
- openGoals: goals.filter((record9) => record9.status === "open"),
11710
- doing: tasks.filter((record9) => record9.column === "doing"),
11711
- ready: tasks.filter((record9) => record9.column === "todo")
11899
+ openGoals: goals.filter((record11) => record11.status === "open"),
11900
+ doing: tasks.filter((record11) => record11.column === "doing"),
11901
+ ready: tasks.filter((record11) => record11.column === "todo")
11712
11902
  };
11713
11903
  emit2(ctx, result, () => {
11714
11904
  ctx.out.log(`${appId}: goal-aligned work intake (read only)`);
@@ -11719,10 +11909,10 @@ async function pmNext(ctx, parsed) {
11719
11909
  ]) {
11720
11910
  ctx.out.log(`${label}:`);
11721
11911
  if (!records.length) ctx.out.log("- (none)");
11722
- else for (const record9 of records) printRecord(
11912
+ else for (const record11 of records) printRecord(
11723
11913
  ctx,
11724
11914
  label === "open goals" ? "goal" : "task",
11725
- record9
11915
+ record11
11726
11916
  );
11727
11917
  }
11728
11918
  if (!result.openGoals.length) {
@@ -11746,9 +11936,9 @@ async function pmHandoff(ctx, parsed) {
11746
11936
  const handoff = {
11747
11937
  appId,
11748
11938
  projectId,
11749
- unmetGoals: goals.filter((record9) => record9.status !== "met"),
11750
- activeTasks: tasks.filter((record9) => record9.column !== "done"),
11751
- openBugs: bugs.filter((record9) => record9.status !== "fixed" && record9.status !== "wontfix")
11939
+ unmetGoals: goals.filter((record11) => record11.status !== "met"),
11940
+ activeTasks: tasks.filter((record11) => record11.column !== "done"),
11941
+ openBugs: bugs.filter((record11) => record11.status !== "fixed" && record11.status !== "wontfix")
11752
11942
  };
11753
11943
  const result = {
11754
11944
  ...handoff,
@@ -11767,17 +11957,17 @@ async function pmHandoff(ctx, parsed) {
11767
11957
  ]) {
11768
11958
  ctx.out.log(`${label}:`);
11769
11959
  if (!records.length) ctx.out.log("- (none)");
11770
- else for (const record9 of records) printRecord(
11960
+ else for (const record11 of records) printRecord(
11771
11961
  ctx,
11772
11962
  label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
11773
- record9
11963
+ record11
11774
11964
  );
11775
11965
  }
11776
11966
  });
11777
11967
  }
11778
- async function pmRemove(ctx, entity, id) {
11779
- await pmRequest(ctx, "DELETE", `/${entity}/${encodeURIComponent(id)}`);
11780
- ctx.out.log(`deleted ${entity} ${id}`);
11968
+ async function pmRemove(ctx, entity, id2) {
11969
+ await pmRequest(ctx, "DELETE", `/${entity}/${encodeURIComponent(id2)}`);
11970
+ ctx.out.log(`deleted ${entity} ${id2}`);
11781
11971
  }
11782
11972
  var init_pm_actions = __esm({
11783
11973
  "src/pm-actions.ts"() {
@@ -11789,15 +11979,15 @@ var init_pm_actions = __esm({
11789
11979
  });
11790
11980
 
11791
11981
  // src/pm-links.ts
11792
- async function pmLink(ctx, entity, id) {
11793
- const { record: record9 } = await pmRequest(
11982
+ async function pmLink(ctx, entity, id2) {
11983
+ const { record: record11 } = await pmRequest(
11794
11984
  ctx,
11795
11985
  "GET",
11796
- `/${entity}/${encodeURIComponent(id)}`
11986
+ `/${entity}/${encodeURIComponent(id2)}`
11797
11987
  );
11798
- const url = studioRecordUrl(ctx, entity, record9.id);
11799
- const markdown = studioRecordLink(ctx, entity, record9);
11800
- emit2(ctx, { kind: entity, id: record9.id, label: record9.title ?? "", url, markdown }, () => {
11988
+ const url = studioRecordUrl(ctx, entity, record11.id);
11989
+ const markdown = studioRecordLink(ctx, entity, record11);
11990
+ emit2(ctx, { kind: entity, id: record11.id, label: record11.title ?? "", url, markdown }, () => {
11801
11991
  ctx.out.log(markdown);
11802
11992
  });
11803
11993
  }
@@ -11810,17 +12000,17 @@ var init_pm_links = __esm({
11810
12000
  });
11811
12001
 
11812
12002
  // src/pm-comments.ts
11813
- async function pmComment(ctx, entity, id, parsed) {
12003
+ async function pmComment(ctx, entity, id2, parsed) {
11814
12004
  const body = stringOpt(parsed.options.body);
11815
12005
  if (!body) throw new Error('pm comment needs --body "..."');
11816
- await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id)}/comments`, {
12006
+ await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id2)}/comments`, {
11817
12007
  body,
11818
12008
  mutationId: writeMutationId2(parsed)
11819
12009
  });
11820
- ctx.out.log(`commented on ${entity} ${id}`);
12010
+ ctx.out.log(`commented on ${entity} ${id2}`);
11821
12011
  }
11822
- async function pmComments(ctx, entity, id) {
11823
- const { messages } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}/comments`);
12012
+ async function pmComments(ctx, entity, id2) {
12013
+ const { messages } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id2)}/comments`);
11824
12014
  emit2(ctx, messages, () => {
11825
12015
  if (messages.length === 0) ctx.out.log("(no comments)");
11826
12016
  else for (const message2 of messages) {
@@ -11845,12 +12035,12 @@ function fieldLine(change) {
11845
12035
  const before = change.before.length > 60 ? `${change.before.slice(0, 60)}\u2026` : change.before;
11846
12036
  return `${change.field} (was: ${before.replace(/\s+/g, " ")})`;
11847
12037
  }
11848
- async function pmHistory(ctx, entity, id, parsed) {
12038
+ async function pmHistory(ctx, entity, id2, parsed) {
11849
12039
  const limit = numberOpt(parsed.options.limit, "--limit");
11850
12040
  const page2 = await pmRequest(
11851
12041
  ctx,
11852
12042
  "GET",
11853
- `/${entity}/${encodeURIComponent(id)}/history${limit === void 0 ? "" : `?limit=${limit}`}`
12043
+ `/${entity}/${encodeURIComponent(id2)}/history${limit === void 0 ? "" : `?limit=${limit}`}`
11854
12044
  );
11855
12045
  emit2(ctx, page2, () => {
11856
12046
  if (!page2.entries.length) {
@@ -11952,16 +12142,16 @@ async function page(ctx, appId, cursor) {
11952
12142
  }
11953
12143
  return data;
11954
12144
  }
11955
- function recordState(record9) {
11956
- if (record9.column) return record9.column === "todo" ? "ready" : record9.column;
11957
- return String(record9.status ?? "");
12145
+ function recordState(record11) {
12146
+ if (record11.column) return record11.column === "todo" ? "ready" : record11.column;
12147
+ return String(record11.status ?? "");
11958
12148
  }
11959
12149
  function eventRecord(event) {
11960
12150
  return event.payload.payload;
11961
12151
  }
11962
12152
  function eventLabel(event) {
11963
- const record9 = eventRecord(event);
11964
- if (record9) return String(record9.title ?? event.payload.entityId);
12153
+ const record11 = eventRecord(event);
12154
+ if (record11) return String(record11.title ?? event.payload.entityId);
11965
12155
  const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
11966
12156
  return body || event.payload.entityId;
11967
12157
  }
@@ -11969,10 +12159,10 @@ function report2(ctx, parsed, result) {
11969
12159
  if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
11970
12160
  else if (parsed.options.jsonl !== true && result.found) {
11971
12161
  for (const event of result.events ?? []) {
11972
- const record9 = eventRecord(event);
11973
- const state2 = record9 ? recordState(record9) : "comment";
12162
+ const record11 = eventRecord(event);
12163
+ const state2 = record11 ? recordState(record11) : "comment";
11974
12164
  ctx.out.log(
11975
- `${event.id} ${event.type} ${state2}${record9?.revision ? `; r${record9.revision}` : ""} ${eventLabel(event)}`
12165
+ `${event.id} ${event.type} ${state2}${record11?.revision ? `; r${record11.revision}` : ""} ${eventLabel(event)}`
11976
12166
  );
11977
12167
  }
11978
12168
  }
@@ -12046,8 +12236,8 @@ async function pmWatch(ctx, parsed) {
12046
12236
  }
12047
12237
  firstSuccess = false;
12048
12238
  const matching = current.events.filter((event) => {
12049
- const record9 = eventRecord(event);
12050
- const state2 = record9 ? recordState(record9).toLowerCase() : "";
12239
+ const record11 = eventRecord(event);
12240
+ const state2 = record11 ? recordState(record11).toLowerCase() : "";
12051
12241
  return (!entity || event.payload.entityKind === entity) && (!action2 || event.payload.action === action2) && (!wantedState || state2 === wantedState || wantedState === "todo" && state2 === "ready") && (!by || event.actor.id === by) && (!self || event.actor.id !== self);
12052
12242
  });
12053
12243
  for (const event of matching) {
@@ -12155,9 +12345,9 @@ async function pmProjectAdd(ctx, parsed) {
12155
12345
  });
12156
12346
  emit2(ctx, result, () => ctx.out.log(`created project: ${result.project.name} (${result.project.id})`));
12157
12347
  }
12158
- async function pmProjectUse(ctx, id) {
12348
+ async function pmProjectUse(ctx, id2) {
12159
12349
  if (!ctx.rootDir) throw new Error("pm project use needs a local project directory");
12160
- const { project } = await pmRequest(ctx, "GET", `/project/${encodeURIComponent(id)}`);
12350
+ const { project } = await pmRequest(ctx, "GET", `/project/${encodeURIComponent(id2)}`);
12161
12351
  if (project.status !== "active") throw new Error(`project ${project.name} is ${project.status}, not active`);
12162
12352
  writePmProjectContext(ctx.rootDir, { appId: project.appId, projectId: project.id });
12163
12353
  emit2(ctx, project, () => ctx.out.log(`using ${project.appId} / ${project.name} (${project.id}) in this worktree`));
@@ -12183,9 +12373,9 @@ function allowedOptions(entity, action2) {
12183
12373
  const entityOptions = action2 === "list" || action2 === "add" || action2 === "set" || action2 === "done" ? ENTITY_OPTIONS[entity][action2] : [];
12184
12374
  return [...COMMON_OPTIONS, ...ACTION_OPTIONS[action2], ...entityOptions];
12185
12375
  }
12186
- function requireId2(id, action2) {
12187
- if (!id) throw new Error(`"pm ... ${action2}" needs an item id`);
12188
- return id;
12376
+ function requireId2(id2, action2) {
12377
+ if (!id2) throw new Error(`"pm ... ${action2}" needs an item id`);
12378
+ return id2;
12189
12379
  }
12190
12380
  async function buildContext2(parsed, deps) {
12191
12381
  const context = await resolveOperatorContext(parsed, {
@@ -12276,34 +12466,34 @@ async function pmCommand(parsed, deps = {}) {
12276
12466
  throw new Error(`pm ${action2} is only valid for tasks`);
12277
12467
  }
12278
12468
  const ctx = await buildContext2(parsed, deps);
12279
- const id = parsed.positionals[3];
12469
+ const id2 = parsed.positionals[3];
12280
12470
  switch (action2) {
12281
12471
  case "list":
12282
12472
  return pmList(ctx, entity, parsed);
12283
12473
  case "add":
12284
12474
  return pmAdd(ctx, entity, parsed);
12285
12475
  case "get":
12286
- return pmGet(ctx, entity, requireId2(id, action2));
12476
+ return pmGet(ctx, entity, requireId2(id2, action2));
12287
12477
  case "set":
12288
- return pmSet(ctx, entity, requireId2(id, action2), parsed);
12478
+ return pmSet(ctx, entity, requireId2(id2, action2), parsed);
12289
12479
  case "done":
12290
- return pmDone(ctx, entity, requireId2(id, action2), parsed);
12480
+ return pmDone(ctx, entity, requireId2(id2, action2), parsed);
12291
12481
  case "comment":
12292
- return pmComment(ctx, entity, requireId2(id, action2), parsed);
12482
+ return pmComment(ctx, entity, requireId2(id2, action2), parsed);
12293
12483
  case "comments":
12294
- return pmComments(ctx, entity, requireId2(id, action2));
12484
+ return pmComments(ctx, entity, requireId2(id2, action2));
12295
12485
  case "history":
12296
- return pmHistory(ctx, entity, requireId2(id, action2), parsed);
12486
+ return pmHistory(ctx, entity, requireId2(id2, action2), parsed);
12297
12487
  case "rm":
12298
- return pmRemove(ctx, entity, requireId2(id, action2));
12488
+ return pmRemove(ctx, entity, requireId2(id2, action2));
12299
12489
  case "link":
12300
- return pmLink(ctx, entity, requireId2(id, action2));
12490
+ return pmLink(ctx, entity, requireId2(id2, action2));
12301
12491
  case "ref":
12302
- return pmReference(ctx, entity, requireId2(id, action2));
12492
+ return pmReference(ctx, entity, requireId2(id2, action2));
12303
12493
  case "ready":
12304
12494
  case "claim":
12305
12495
  case "release":
12306
- return pmTaskLifecycle(ctx, requireId2(id, action2), action2, parsed);
12496
+ return pmTaskLifecycle(ctx, requireId2(id2, action2), action2, parsed);
12307
12497
  }
12308
12498
  }
12309
12499
  var ALIASES, COMMON_OPTIONS, ACTION_OPTIONS, ENTITY_OPTIONS;
@@ -12480,17 +12670,17 @@ async function platformStatus(parsed, deps) {
12480
12670
  }
12481
12671
  }
12482
12672
  function isPlatformStatus(value2) {
12483
- if (!record6(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
12484
- if (!record6(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
12485
- if (!record6(value2.catalog) || !record6(value2.summary)) return false;
12673
+ if (!record7(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
12674
+ if (!record7(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
12675
+ if (!record7(value2.catalog) || !record7(value2.summary)) return false;
12486
12676
  return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
12487
12677
  }
12488
12678
  function apiMessage(value2) {
12489
- if (!record6(value2)) return "request failed";
12490
- const error = record6(value2.error) ? value2.error : value2;
12679
+ if (!record7(value2)) return "request failed";
12680
+ const error = record7(value2.error) ? value2.error : value2;
12491
12681
  return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
12492
12682
  }
12493
- function record6(value2) {
12683
+ function record7(value2) {
12494
12684
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
12495
12685
  }
12496
12686
  var init_platform_command = __esm({
@@ -12541,7 +12731,7 @@ function statusVerdict(reads) {
12541
12731
  severity: "degraded"
12542
12732
  });
12543
12733
  }
12544
- const performance = record7(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
12734
+ const performance = record8(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
12545
12735
  if (performance?.status === "unavailable") {
12546
12736
  reasons.push({
12547
12737
  source: "liveSync",
@@ -12622,7 +12812,7 @@ function statusVerdict(reads) {
12622
12812
  reasons
12623
12813
  };
12624
12814
  }
12625
- function record7(value2) {
12815
+ function record8(value2) {
12626
12816
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
12627
12817
  }
12628
12818
  function numeric2(value2) {
@@ -12656,7 +12846,7 @@ function printO11yStatus(status, out) {
12656
12846
  out.log(
12657
12847
  `o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
12658
12848
  );
12659
- const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record8) : [];
12849
+ const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record9) : [];
12660
12850
  const requests = routes.reduce(
12661
12851
  (total, row) => total + numeric3(row.requests),
12662
12852
  0
@@ -12668,39 +12858,39 @@ function printO11yStatus(status, out) {
12668
12858
  out.log(
12669
12859
  `application ${status.application.httpStatus} ${requests} requests ${errors} errors`
12670
12860
  );
12671
- const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record8) : [];
12861
+ const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record9) : [];
12672
12862
  out.log(
12673
12863
  `application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
12674
12864
  (row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
12675
12865
  ).join(", ") : "none observed"}`
12676
12866
  );
12677
12867
  out.log(liveSyncLine(status.liveSync));
12678
- const canaryDurations = record8(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
12868
+ const canaryDurations = record9(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
12679
12869
  out.log(
12680
12870
  `canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
12681
12871
  );
12682
- const collectorIngest = record8(status.collector.body.ingest) ? status.collector.body.ingest : {};
12683
- const collectorStorage = record8(collectorIngest.storage) ? collectorIngest.storage : {};
12872
+ const collectorIngest = record9(status.collector.body.ingest) ? status.collector.body.ingest : {};
12873
+ const collectorStorage = record9(collectorIngest.storage) ? collectorIngest.storage : {};
12684
12874
  out.log(
12685
12875
  `collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
12686
12876
  );
12687
- const providerMetrics = record8(status.provider.body.metrics) ? status.provider.body.metrics : {};
12688
- const providerCapacity = record8(status.provider.body.capacity) ? status.provider.body.capacity : {};
12689
- const workerMemory = record8(providerCapacity.memory) ? providerCapacity.memory : {};
12877
+ const providerMetrics = record9(status.provider.body.metrics) ? status.provider.body.metrics : {};
12878
+ const providerCapacity = record9(status.provider.body.capacity) ? status.provider.body.capacity : {};
12879
+ const workerMemory = record9(providerCapacity.memory) ? providerCapacity.memory : {};
12690
12880
  out.log(
12691
12881
  `cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${numeric3(providerMetrics.requests)} invocations ${numeric3(providerMetrics.errors)} runtime errors ${optionalBytes(workerMemory.headroomBytes)} isolate memory headroom`
12692
12882
  );
12693
12883
  for (const line of providerCapacityLines(status.providerCapacity)) {
12694
12884
  out.log(line);
12695
12885
  }
12696
- const coverage = record8(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
12697
- const coverageCounts = record8(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
12698
- const coverageBudget = record8(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
12886
+ const coverage = record9(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
12887
+ const coverageCounts = record9(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
12888
+ const coverageBudget = record9(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
12699
12889
  out.log(
12700
12890
  `request-coverage ${status.providerReconciliation.httpStatus} ${String(status.providerReconciliation.body.status ?? status.providerReconciliation.body.error ?? "unavailable")} ${optionalPercent(coverage.applicationCoverage)} application/provider ${numeric3(coverageCounts.applicationRequests)}/${numeric3(coverageCounts.providerRequests)} requests \xB1${optionalPercent(coverageBudget.maxRelativeError)} budget`
12701
12891
  );
12702
12892
  const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
12703
- const providerFreshness = record8(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
12893
+ const providerFreshness = record9(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
12704
12894
  out.log(
12705
12895
  `cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
12706
12896
  );
@@ -12709,17 +12899,17 @@ function printO11yStatus(status, out) {
12709
12899
  );
12710
12900
  }
12711
12901
  function providerCapacityLines(read3) {
12712
- const resources = record8(read3.body.resources) ? read3.body.resources : {};
12713
- const durableObjects = record8(resources.durableObjects) ? resources.durableObjects : {};
12714
- const periodic = record8(durableObjects.periodic) ? durableObjects.periodic : {};
12715
- const storage = record8(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
12716
- const d1 = record8(resources.d1) ? resources.d1 : {};
12717
- const d1Activity = record8(d1.activity) ? d1.activity : {};
12718
- const d1Storage = record8(d1.storage) ? d1.storage : {};
12719
- const d1Latency = record8(d1Activity.latency) ? d1Activity.latency : {};
12720
- const r2 = record8(resources.r2) ? resources.r2 : {};
12721
- const r2Operations = record8(r2.operations) ? r2.operations : {};
12722
- const r2Storage = record8(r2.storage) ? r2.storage : {};
12902
+ const resources = record9(read3.body.resources) ? read3.body.resources : {};
12903
+ const durableObjects = record9(resources.durableObjects) ? resources.durableObjects : {};
12904
+ const periodic = record9(durableObjects.periodic) ? durableObjects.periodic : {};
12905
+ const storage = record9(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
12906
+ const d1 = record9(resources.d1) ? resources.d1 : {};
12907
+ const d1Activity = record9(d1.activity) ? d1.activity : {};
12908
+ const d1Storage = record9(d1.storage) ? d1.storage : {};
12909
+ const d1Latency = record9(d1Activity.latency) ? d1Activity.latency : {};
12910
+ const r2 = record9(resources.r2) ? resources.r2 : {};
12911
+ const r2Operations = record9(r2.operations) ? r2.operations : {};
12912
+ const r2Storage = record9(r2.storage) ? r2.storage : {};
12723
12913
  const status = String(
12724
12914
  read3.body.status ?? read3.body.error ?? "unavailable"
12725
12915
  );
@@ -12730,11 +12920,11 @@ function providerCapacityLines(read3) {
12730
12920
  ];
12731
12921
  }
12732
12922
  function liveSyncLine(read3) {
12733
- const performance = record8(read3.body.performance) ? read3.body.performance : {};
12734
- const commitToSend = record8(performance.commitToSend) ? performance.commitToSend : {};
12923
+ const performance = record9(read3.body.performance) ? read3.body.performance : {};
12924
+ const commitToSend = record9(performance.commitToSend) ? performance.commitToSend : {};
12735
12925
  return `live-sync ${read3.httpStatus} ${String(read3.body.status ?? read3.body.error ?? "unavailable")} ${numeric3(read3.body.activeConnections)} active ${optionalNumeric(commitToSend.p95)} commit-to-send p95 ${numeric3(performance.sendFailures)} send failures`;
12736
12926
  }
12737
- function record8(value2) {
12927
+ function record9(value2) {
12738
12928
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
12739
12929
  }
12740
12930
  function numeric3(value2) {
@@ -12911,14 +13101,14 @@ function statusMinutes(value2) {
12911
13101
  }
12912
13102
  async function read2(url, headers, doFetch) {
12913
13103
  const response2 = await doFetch(url, { headers });
12914
- const text2 = await response2.text();
13104
+ const text3 = await response2.text();
12915
13105
  let body = {};
12916
- if (text2) {
13106
+ if (text3) {
12917
13107
  try {
12918
- const value2 = JSON.parse(text2);
13108
+ const value2 = JSON.parse(text3);
12919
13109
  body = value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : { value: value2 };
12920
13110
  } catch {
12921
- body = { message: text2.slice(0, 300) };
13111
+ body = { message: text3.slice(0, 300) };
12922
13112
  }
12923
13113
  }
12924
13114
  return { httpStatus: response2.status, body };
@@ -12935,6 +13125,297 @@ var init_o11y_command = __esm({
12935
13125
  }
12936
13126
  });
12937
13127
 
13128
+ // src/monitoring-config.ts
13129
+ function monitoringWireConfig(cfg, env) {
13130
+ const monitoring = cfg.o11y?.monitoring;
13131
+ if (!monitoring) throw new Error("o11y.monitoring is not configured");
13132
+ if (!cfg.services.includes("o11y")) throw new Error('o11y.monitoring requires "o11y" in services');
13133
+ const authoredLink = cfg.links?.[env];
13134
+ if (!authoredLink) throw new Error(`links.${env} is required for live monitoring`);
13135
+ const baseUrl = new URL(authoredLink).toString();
13136
+ const selectedProbes = (monitoring.probes ?? []).filter((probe) => !probe.envs || probe.envs.includes(env));
13137
+ const probeIds = new Set(selectedProbes.map((probe) => probe.id));
13138
+ const selectedSlos = monitoring.slos.filter(
13139
+ (slo) => slo.indicator.type === "o11y-metric" || slo.indicator.probes.some((id2) => probeIds.has(id2))
13140
+ );
13141
+ if (selectedSlos.length === 0) throw new Error(`o11y.monitoring has no SLOs for env "${env}"`);
13142
+ for (const slo of selectedSlos) {
13143
+ if (slo.indicator.type !== "probe-success") continue;
13144
+ const unavailable = slo.indicator.probes.filter((id2) => !probeIds.has(id2));
13145
+ if (unavailable.length) throw new Error(`SLO "${slo.id}" mixes probes unavailable in env "${env}": ${unavailable.join(", ")}`);
13146
+ }
13147
+ const payload = {
13148
+ environment: env,
13149
+ baseUrl,
13150
+ probes: selectedProbes.map(normalizeProbe),
13151
+ slos: selectedSlos.map(normalizeSlo),
13152
+ ...notification(cfg.o11y.monitoring.notifications?.[env])
13153
+ };
13154
+ const revision = `sha256:${(0, import_node_crypto4.createHash)("sha256").update(canonical(payload)).digest("hex")}`;
13155
+ return { revision, ...payload };
13156
+ }
13157
+ function normalizeProbe(probe) {
13158
+ return {
13159
+ id: probe.id,
13160
+ route: probe.route,
13161
+ cadenceMinutes: durationMinutes(probe.every),
13162
+ timeoutMs: probe.timeout ?? 2e4,
13163
+ ...probe.ready?.selector ? { readySelector: probe.ready.selector } : {},
13164
+ expect: {
13165
+ status: probe.expect.status,
13166
+ ...probe.expect.titleIncludes ? { titleIncludes: probe.expect.titleIncludes } : {},
13167
+ textIncludes: probe.expect.textIncludes ?? [],
13168
+ accessibility: probe.expect.accessibility ?? []
13169
+ },
13170
+ enabled: probe.enabled !== false
13171
+ };
13172
+ }
13173
+ function normalizeSlo(slo) {
13174
+ return {
13175
+ id: slo.id,
13176
+ name: slo.name ?? slo.id,
13177
+ indicator: normalizeIndicator(slo.indicator),
13178
+ target: slo.target,
13179
+ windowMinutes: durationMinutes(slo.window),
13180
+ spike: {
13181
+ badChecks: slo.alerts?.spike?.badChecks ?? 2,
13182
+ withinChecks: slo.alerts?.spike?.withinChecks ?? 3,
13183
+ recoverAfter: slo.alerts?.spike?.recoverAfter ?? 2
13184
+ },
13185
+ trend: {
13186
+ burnRate: slo.alerts?.trend?.burnRate ?? 1,
13187
+ shortMinutes: durationMinutes(slo.alerts?.trend?.shortWindow ?? "6h"),
13188
+ longMinutes: durationMinutes(slo.alerts?.trend?.longWindow ?? "3d"),
13189
+ minBadChecks: slo.alerts?.trend?.minBadChecks ?? 2
13190
+ },
13191
+ enabled: slo.enabled !== false
13192
+ };
13193
+ }
13194
+ function normalizeIndicator(indicator) {
13195
+ if (indicator.type === "probe-success") {
13196
+ return { type: "probe-success", probes: [...new Set(indicator.probes)] };
13197
+ }
13198
+ return {
13199
+ type: "o11y-metric",
13200
+ metric: indicator.metric,
13201
+ comparator: indicator.comparator,
13202
+ threshold: indicator.threshold,
13203
+ cadenceMinutes: durationMinutes(indicator.every),
13204
+ observationWindowMinutes: durationMinutes(indicator.observationWindow),
13205
+ ...indicator.route ? { route: indicator.route } : {}
13206
+ };
13207
+ }
13208
+ function notification(policy) {
13209
+ if (!policy) return {};
13210
+ return {
13211
+ notifications: {
13212
+ email: [...new Set(policy.email.map((email) => email.trim().toLowerCase()))],
13213
+ timezone: policy.timezone,
13214
+ daily: policy.daily === void 0 ? "08:00" : policy.daily,
13215
+ weekly: policy.weekly === void 0 ? { day: "monday", at: "08:00" } : policy.weekly
13216
+ }
13217
+ };
13218
+ }
13219
+ function durationMinutes(value2) {
13220
+ const match = /^(\d+)(m|h|d)$/.exec(value2);
13221
+ if (!match) throw new Error(`unsupported duration ${value2}`);
13222
+ const amount = Number(match[1]);
13223
+ return amount * (match[2] === "d" ? 1440 : match[2] === "h" ? 60 : 1);
13224
+ }
13225
+ function canonical(value2) {
13226
+ if (Array.isArray(value2)) return `[${value2.map(canonical).join(",")}]`;
13227
+ if (value2 && typeof value2 === "object") {
13228
+ return `{${Object.entries(value2).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(",")}}`;
13229
+ }
13230
+ return JSON.stringify(value2);
13231
+ }
13232
+ var import_node_crypto4;
13233
+ var init_monitoring_config = __esm({
13234
+ "src/monitoring-config.ts"() {
13235
+ "use strict";
13236
+ init_cjs_shims();
13237
+ import_node_crypto4 = require("crypto");
13238
+ }
13239
+ });
13240
+
13241
+ // src/monitor-command.ts
13242
+ async function monitorCommand(parsed, deps = {}) {
13243
+ assertArgs(parsed, OPTIONS, 3);
13244
+ const action2 = parsed.positionals[1] ?? "status";
13245
+ if (!["plan", "apply", "run", "status", "incidents", "report"].includes(action2)) {
13246
+ throw new Error(`unknown monitor action "${action2}". Try "odla-ai monitor status --json".`);
13247
+ }
13248
+ const context = await resolveOperatorContext(parsed, {
13249
+ allowMissingConfig: action2 !== "plan" && action2 !== "apply",
13250
+ requireApp: true
13251
+ });
13252
+ if ((action2 === "plan" || action2 === "apply") && context.config.status !== "loaded") {
13253
+ throw new Error(`monitor ${action2} requires odla.config.mjs`);
13254
+ }
13255
+ const env = context.environment.value ?? context.cfg.envs[0] ?? "prod";
13256
+ const appId = context.app.value;
13257
+ const doFetch = deps.fetch ?? fetch;
13258
+ const out = deps.stdout ?? console;
13259
+ const token = await getDeveloperToken(
13260
+ context.cfg,
13261
+ {
13262
+ configPath: context.cfg.configPath,
13263
+ token: stringOpt(parsed.options.token),
13264
+ email: stringOpt(parsed.options.email),
13265
+ open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
13266
+ openApprovalUrl: deps.openUrl
13267
+ },
13268
+ doFetch,
13269
+ out,
13270
+ action2 === "apply" || action2 === "run" ? { optionalProjectCapabilities: ["app.manage"] } : {}
13271
+ );
13272
+ const base = `${context.cfg.platformUrl}/o11y/${encodeURIComponent(appId)}/monitoring`;
13273
+ const headers = { authorization: `Bearer ${token}`, "content-type": "application/json" };
13274
+ const jsonOutput = parsed.options.json === true;
13275
+ if (action2 === "plan" || action2 === "apply") {
13276
+ const desired = monitoringWireConfig(context.cfg, env);
13277
+ const live = await request2(`${base}?env=${encodeURIComponent(env)}`, { headers }, doFetch);
13278
+ const currentRevision = record10(live.config) ? string(live.config.revision) : null;
13279
+ const changed = currentRevision !== desired.revision;
13280
+ const plan = {
13281
+ schemaVersion: 1,
13282
+ appId,
13283
+ env,
13284
+ currentRevision,
13285
+ desiredRevision: desired.revision,
13286
+ changed,
13287
+ probes: desired.probes.map((probe) => ({ id: probe.id, route: probe.route, cadenceMinutes: probe.cadenceMinutes })),
13288
+ slos: desired.slos.map((slo) => ({ id: slo.id, indicator: slo.indicator, target: slo.target, windowMinutes: slo.windowMinutes })),
13289
+ notifications: desired.notifications ? { recipients: desired.notifications.email.length, timezone: desired.notifications.timezone, daily: desired.notifications.daily, weekly: desired.notifications.weekly } : null
13290
+ };
13291
+ if (action2 === "plan") {
13292
+ emit3(plan, jsonOutput, out, () => {
13293
+ out.log(`monitor plan ${appId}/${env}: ${changed ? "changes pending" : "in sync"}`);
13294
+ out.log(`revision ${currentRevision ?? "not configured"} -> ${desired.revision}`);
13295
+ for (const probe of desired.probes) out.log(`probe ${probe.id} ${probe.route} every ${probe.cadenceMinutes}m`);
13296
+ for (const slo of desired.slos) out.log(`slo ${slo.id} ${slo.indicator.type} ${(slo.target * 100).toFixed(3)}% ${slo.windowMinutes}m`);
13297
+ });
13298
+ return;
13299
+ }
13300
+ if ((env === "prod" || env === "production") && parsed.options.yes !== true) {
13301
+ throw new Error(`refusing to apply live monitoring for "${env}" without --yes; run monitor plan first`);
13302
+ }
13303
+ if (!changed) {
13304
+ emit3({ ...plan, applied: false }, jsonOutput, out, () => out.log(`monitor apply ${appId}/${env}: already in sync`));
13305
+ return;
13306
+ }
13307
+ const applied = await request2(`${base}?env=${encodeURIComponent(env)}`, {
13308
+ method: "PUT",
13309
+ headers,
13310
+ body: JSON.stringify(desired)
13311
+ }, doFetch);
13312
+ emit3({ schemaVersion: 1, appId, env, ...applied }, jsonOutput, out, () => out.log(`monitor apply ${appId}/${env}: ${applied.changed === true ? "applied" : "unchanged"} ${desired.revision}`));
13313
+ return;
13314
+ }
13315
+ if (action2 === "run") {
13316
+ const probeId = parsed.positionals[2];
13317
+ if (!probeId) throw new Error("monitor run requires a probe id");
13318
+ const result2 = await request2(`${base}/probes/${encodeURIComponent(probeId)}/run?env=${encodeURIComponent(env)}`, {
13319
+ method: "POST",
13320
+ headers
13321
+ }, doFetch);
13322
+ emit3(result2, jsonOutput, out, () => {
13323
+ const run = record10(result2.run) ? result2.run : {};
13324
+ out.log(`monitor run ${appId}/${env}/${probeId}: ${string(run.outcome) ?? "unknown"}${run.failure_code ? ` (${String(run.failure_code)})` : ""}`);
13325
+ });
13326
+ return;
13327
+ }
13328
+ let path = action2;
13329
+ if (action2 === "report") {
13330
+ const period = stringOpt(parsed.options.period) ?? "daily";
13331
+ if (period !== "daily" && period !== "weekly") throw new Error("--period must be daily or weekly");
13332
+ path = `report?period=${period}`;
13333
+ } else if (action2 === "incidents") {
13334
+ const params = new URLSearchParams({ limit: String(numberOpt(parsed.options.limit, "--limit") ?? 100) });
13335
+ if (boolOpt(parsed.options.runs) === true) params.set("runs", "true");
13336
+ path = `incidents?${params}`;
13337
+ }
13338
+ const separator = path.includes("?") ? "&" : "?";
13339
+ const result = await request2(`${base}/${path}${separator}env=${encodeURIComponent(env)}`, { headers }, doFetch);
13340
+ emit3(result, jsonOutput, out, () => printRead(action2, appId, env, result, out));
13341
+ }
13342
+ async function request2(url, init, doFetch) {
13343
+ const response2 = await doFetch(url, init);
13344
+ const text3 = await response2.text();
13345
+ let body = {};
13346
+ try {
13347
+ const parsed = text3 ? JSON.parse(text3) : {};
13348
+ body = record10(parsed) ? parsed : { value: parsed };
13349
+ } catch {
13350
+ body = { message: text3.slice(0, 500) };
13351
+ }
13352
+ if (!response2.ok) {
13353
+ const error = record10(body.error) ? body.error : body;
13354
+ throw new Error(string(error.message) ?? string(error.code) ?? `monitor request failed (${response2.status})`);
13355
+ }
13356
+ return body;
13357
+ }
13358
+ function printRead(action2, appId, env, result, out) {
13359
+ if (action2 === "status") {
13360
+ out.log(`monitor status ${appId}/${env}: ${String(result.overall ?? (result.configured === false ? "not configured" : "unknown"))}`);
13361
+ const slos = Array.isArray(result.slos) ? result.slos.filter(record10) : [];
13362
+ for (const slo of slos) out.log(`slo ${String(slo.id)} ${String(slo.state)} ${percent(slo.observed)} observed ${percent(slo.budgetRemaining)} budget remaining`);
13363
+ const incidents = Array.isArray(result.openIncidents) ? result.openIncidents.length : 0;
13364
+ const gaps = Array.isArray(result.monitoringGaps) ? result.monitoringGaps.length : 0;
13365
+ out.log(`open incidents ${incidents}`);
13366
+ out.log(`monitoring gaps ${gaps}`);
13367
+ return;
13368
+ }
13369
+ if (action2 === "incidents") {
13370
+ const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record10) : [];
13371
+ out.log(`monitor incidents ${appId}/${env}: ${incidents.length}`);
13372
+ for (const incident2 of incidents) out.log(`${String(incident2.state)} ${String(incident2.kind)} ${String(incident2.slo_id)} ${new Date(Number(incident2.opened_at)).toISOString()}`);
13373
+ return;
13374
+ }
13375
+ out.log(`monitor report ${appId}/${env}: ${String(result.period)} ${String(result.overall)}`);
13376
+ const probes = Array.isArray(result.probes) ? result.probes.filter(record10) : [];
13377
+ for (const probe of probes) out.log(`probe ${String(probe.id)} ${Number(probe.good)} good ${Number(probe.bad)} bad ${Number(probe.unknown)} unknown`);
13378
+ }
13379
+ function emit3(value2, json, out, human) {
13380
+ if (json) out.log(JSON.stringify(value2, null, 2));
13381
+ else human();
13382
+ }
13383
+ function record10(value2) {
13384
+ return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
13385
+ }
13386
+ function string(value2) {
13387
+ return typeof value2 === "string" ? value2 : null;
13388
+ }
13389
+ function percent(value2) {
13390
+ return typeof value2 === "number" && Number.isFinite(value2) ? `${(value2 * 100).toFixed(2)}%` : "unknown";
13391
+ }
13392
+ var OPTIONS;
13393
+ var init_monitor_command = __esm({
13394
+ "src/monitor-command.ts"() {
13395
+ "use strict";
13396
+ init_cjs_shims();
13397
+ init_argv();
13398
+ init_monitoring_config();
13399
+ init_operator_context();
13400
+ init_token();
13401
+ OPTIONS = [
13402
+ "config",
13403
+ "context",
13404
+ "platform",
13405
+ "token",
13406
+ "email",
13407
+ "json",
13408
+ "app",
13409
+ "env",
13410
+ "open",
13411
+ "yes",
13412
+ "period",
13413
+ "limit",
13414
+ "runs"
13415
+ ];
13416
+ }
13417
+ });
13418
+
12938
13419
  // src/integration-provision.ts
12939
13420
  async function provisionIntegrationSeeds(doFetch, endpoint, tenantId, dbKey, integrations, env, out) {
12940
13421
  const base = `${endpoint}/app/${encodeURIComponent(tenantId)}`;
@@ -13104,8 +13585,8 @@ function runtimeUrl(cfg, suffix = "") {
13104
13585
  return `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials${suffix}`;
13105
13586
  }
13106
13587
  async function safeError(response2) {
13107
- const text2 = await response2.text();
13108
- return redactSecrets(text2.slice(0, 1e3));
13588
+ const text3 = await response2.text();
13589
+ return redactSecrets(text3.slice(0, 1e3));
13109
13590
  }
13110
13591
  async function finish(doFetch, cfg, token, sessionId, method) {
13111
13592
  return doFetch(runtimeUrl(cfg, `/${encodeURIComponent(sessionId)}`), {
@@ -13129,7 +13610,7 @@ async function deliverRuntimeCredentials(cfg, options) {
13129
13610
  },
13130
13611
  body: JSON.stringify({
13131
13612
  env: options.env,
13132
- idempotencyKey: `wrangler:${(0, import_node_crypto4.randomUUID)()}`,
13613
+ idempotencyKey: `wrangler:${(0, import_node_crypto5.randomUUID)()}`,
13133
13614
  target
13134
13615
  })
13135
13616
  });
@@ -13179,12 +13660,12 @@ async function deliverRuntimeCredentials(cfg, options) {
13179
13660
  ...values.ODLA_O11Y_TOKEN ? { o11yToken: values.ODLA_O11Y_TOKEN } : {}
13180
13661
  };
13181
13662
  }
13182
- var import_node_crypto4;
13663
+ var import_node_crypto5;
13183
13664
  var init_runtime_credentials = __esm({
13184
13665
  "src/runtime-credentials.ts"() {
13185
13666
  "use strict";
13186
13667
  init_cjs_shims();
13187
- import_node_crypto4 = require("crypto");
13668
+ import_node_crypto5 = require("crypto");
13188
13669
  init_redact();
13189
13670
  init_wrangler();
13190
13671
  }
@@ -13584,6 +14065,7 @@ var init_surface = __esm({
13584
14065
  doctor: {},
13585
14066
  help: {},
13586
14067
  init: {},
14068
+ monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
13587
14069
  o11y: { status: {} },
13588
14070
  operations: { get: {}, wait: {} },
13589
14071
  platform: {
@@ -13798,12 +14280,12 @@ var init_runbook_actions = __esm({
13798
14280
  });
13799
14281
 
13800
14282
  // src/runbook-import.ts
13801
- function parseRunbook(text2, slug) {
13802
- let rest = text2;
14283
+ function parseRunbook(text3, slug) {
14284
+ let rest = text3;
13803
14285
  const meta = {};
13804
- const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text2);
14286
+ const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text3);
13805
14287
  if (fm) {
13806
- rest = text2.slice(fm[0].length);
14288
+ rest = text3.slice(fm[0].length);
13807
14289
  for (const line of fm[1].split(/\r?\n/)) {
13808
14290
  const pair = /^(\w+)\s*:\s*(.+)$/.exec(line.trim());
13809
14291
  if (!pair) continue;
@@ -14657,9 +15139,9 @@ function printHostedSecurityIntent(out, intent) {
14657
15139
  }
14658
15140
  function assertHostedSecurityPlanReady(plan) {
14659
15141
  const reasons = [];
14660
- for (const [label, route2] of Object.entries(plan.routes)) {
14661
- if (!route2.enabled) reasons.push(`${label} is disabled`);
14662
- if (!route2.credentialReady) reasons.push(`${label} provider credential is unavailable`);
15142
+ for (const [label, route3] of Object.entries(plan.routes)) {
15143
+ if (!route3.enabled) reasons.push(`${label} is disabled`);
15144
+ if (!route3.credentialReady) reasons.push(`${label} provider credential is unavailable`);
14663
15145
  }
14664
15146
  if (!plan.independent) reasons.push("discovery and validation are not independently routed");
14665
15147
  if (plan.ready && reasons.length === 0) return;
@@ -14713,20 +15195,20 @@ function enforceHostedReportGate(report4, parsed, out, emitSuccess) {
14713
15195
  out.log(`security gate passed: 0 confirmed >= ${failOn}; 0 leads >= ${failOnCandidates ?? "disabled"}; coverage ${report4.coverageStatus}. This is not proof that the application is secure.`);
14714
15196
  }
14715
15197
  }
14716
- function printHostedSecurityPlanRoute(out, label, route2) {
14717
- const readiness = route2.enabled && route2.credentialReady ? "ready" : [
14718
- route2.enabled ? void 0 : "disabled",
14719
- route2.credentialReady ? void 0 : "credential unavailable"
15198
+ function printHostedSecurityPlanRoute(out, label, route3) {
15199
+ const readiness = route3.enabled && route3.credentialReady ? "ready" : [
15200
+ route3.enabled ? void 0 : "disabled",
15201
+ route3.credentialReady ? void 0 : "credential unavailable"
14720
15202
  ].filter(Boolean).join(", ");
14721
- out.log(` ${label}: ${route2.provider}/${route2.model} \xB7 policy v${route2.policyVersion} \xB7 ${readiness}`);
14722
- out.log(` bounds: ${route2.maxCallsPerRun} calls/run \xB7 ${route2.maxInputBytes} input bytes/call \xB7 ${route2.maxOutputTokens} output tokens/call`);
15203
+ out.log(` ${label}: ${route3.provider}/${route3.model} \xB7 policy v${route3.policyVersion} \xB7 ${readiness}`);
15204
+ out.log(` bounds: ${route3.maxCallsPerRun} calls/run \xB7 ${route3.maxInputBytes} input bytes/call \xB7 ${route3.maxOutputTokens} output tokens/call`);
14723
15205
  }
14724
15206
  function printHostedCoverage(out, job) {
14725
15207
  const coverage = job.coverage;
14726
15208
  out.log(` coverage: ${job.coverageStatus ?? "pending"}${coverage?.completeCells !== void 0 ? ` ${coverage.completeCells}/${coverage.totalCells ?? "?"}` : ""}${coverage?.shallowCells ? ` shallow=${coverage.shallowCells}` : ""}${coverage?.blockedCells ? ` blocked=${coverage.blockedCells}` : ""}${coverage?.unscheduledCells ? ` unscheduled=${coverage.unscheduledCells}` : ""}${coverage?.budgetExhaustedCells ? ` budget_exhausted=${coverage.budgetExhaustedCells}` : ""}`);
14727
15209
  }
14728
- function routeLabel(route2) {
14729
- return `${route2.provider}/${route2.model}${route2.policyVersion ? ` policy v${route2.policyVersion}` : ""}`;
15210
+ function routeLabel(route3) {
15211
+ return `${route3.provider}/${route3.model}${route3.policyVersion ? ` policy v${route3.policyVersion}` : ""}`;
14730
15212
  }
14731
15213
  function hostedSeverity(value2, flag) {
14732
15214
  if (HOSTED_SEVERITIES.includes(value2)) {
@@ -14815,11 +15297,11 @@ function selectEnv(requested, declared, configPath, rootDir) {
14815
15297
  }
14816
15298
  return env;
14817
15299
  }
14818
- async function injectedToken(options, request2) {
14819
- const value2 = options.token ?? await options.getToken?.(Object.freeze({ ...request2 }));
15300
+ async function injectedToken(options, request3) {
15301
+ const value2 = options.token ?? await options.getToken?.(Object.freeze({ ...request3 }));
14820
15302
  if (typeof value2 !== "string" || value2.length < 8 || value2.length > 8192 || /\s|[\u0000-\u001f\u007f]/.test(value2)) {
14821
15303
  throw new Error(
14822
- request2.selfAudit ? "Self-audit requires an injected, scoped platform security token" : "Hosted security requires an injected app developer token or getToken callback"
15304
+ request3.selfAudit ? "Self-audit requires an injected, scoped platform security token" : "Hosted security requires an injected app developer token or getToken callback"
14823
15305
  );
14824
15306
  }
14825
15307
  return value2;
@@ -15157,11 +15639,11 @@ async function runLocalSecurityCommand(parsed, dependencies) {
15157
15639
  sourceDisclosureAck: parsed.options["ack-redacted-source"] === true ? "redacted" : void 0,
15158
15640
  fetch: doFetch,
15159
15641
  stdout: out,
15160
- getToken: async (request2) => {
15161
- if (request2.scope === "platform:security:self") {
15642
+ getToken: async (request3) => {
15643
+ if (request3.scope === "platform:security:self") {
15162
15644
  return getScopedPlatformToken({
15163
- platform: request2.platform,
15164
- scope: request2.scope,
15645
+ platform: request3.platform,
15646
+ scope: request3.scope,
15165
15647
  email: stringOpt(parsed.options.email),
15166
15648
  open,
15167
15649
  fetch: doFetch,
@@ -15170,7 +15652,7 @@ async function runLocalSecurityCommand(parsed, dependencies) {
15170
15652
  });
15171
15653
  }
15172
15654
  const cfg = await loadProjectConfig(configPath);
15173
- if (platformAudience(cfg.platformUrl) !== platformAudience(request2.platform)) {
15655
+ if (platformAudience(cfg.platformUrl) !== platformAudience(request3.platform)) {
15174
15656
  throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
15175
15657
  }
15176
15658
  return getDeveloperToken(
@@ -15408,10 +15890,10 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
15408
15890
  }
15409
15891
  if (command === "bug") {
15410
15892
  const action2 = parsed.positionals[1] ?? "list";
15411
- const canonical = action2 === "report" || action2 === "create" ? "add" : action2;
15893
+ const canonical2 = action2 === "report" || action2 === "create" ? "add" : action2;
15412
15894
  await pmCommand({
15413
15895
  ...parsed,
15414
- positionals: ["pm", "bug", canonical, ...parsed.positionals.slice(2)]
15896
+ positionals: ["pm", "bug", canonical2, ...parsed.positionals.slice(2)]
15415
15897
  }, runtime);
15416
15898
  return;
15417
15899
  }
@@ -15423,6 +15905,10 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
15423
15905
  await o11yCommand(parsed, runtime);
15424
15906
  return;
15425
15907
  }
15908
+ if (command === "monitor") {
15909
+ await monitorCommand(parsed, runtime);
15910
+ return;
15911
+ }
15426
15912
  if (command === "platform") {
15427
15913
  await platformCommand(parsed, runtime);
15428
15914
  return;
@@ -15519,6 +16005,7 @@ var init_cli = __esm({
15519
16005
  init_pm_command();
15520
16006
  init_platform_command();
15521
16007
  init_o11y_command();
16008
+ init_monitor_command();
15522
16009
  init_provision();
15523
16010
  init_record();
15524
16011
  init_redact();