@odla-ai/cli 0.33.0 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +204 -112
  2. package/REQUIREMENTS.md +6 -0
  3. package/dist/bin.cjs +1027 -527
  4. package/dist/bin.cjs.map +1 -1
  5. package/dist/bin.js +1 -1
  6. package/dist/{chunk-YSQORU5J.js → chunk-RUEM7ZTA.js} +998 -523
  7. package/dist/chunk-RUEM7ZTA.js.map +1 -0
  8. package/dist/{cli-U436OLYW.js → cli-NKNQLWOM.js} +2 -2
  9. package/dist/index.cjs +1001 -524
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +173 -4
  12. package/dist/index.d.ts +173 -4
  13. package/dist/index.js +5 -1
  14. package/package.json +2 -2
  15. package/skills/odla/SKILL.md +65 -35
  16. package/skills/odla/references/agent-identity.md +5 -5
  17. package/skills/odla/references/build.md +16 -15
  18. package/skills/odla/references/co-owners.md +1 -1
  19. package/skills/odla/references/pm-work-intake.md +12 -12
  20. package/skills/odla/references/pm.md +17 -17
  21. package/skills/odla/references/sdks.md +2 -2
  22. package/skills/odla-migrate/SKILL.md +23 -5
  23. package/skills/odla-migrate/references/phase-2-chapter.md +1 -1
  24. package/skills/odla-migrate/references/phase-2-db.md +11 -10
  25. package/skills/odla-migrate/references/phase-3-auth.md +2 -2
  26. package/skills/odla-migrate/references/phase-3b-user-sync.md +2 -2
  27. package/skills/odla-migrate/references/phase-4-ai.md +3 -3
  28. package/skills/odla-migrate/references/phase-5-cutover.md +5 -5
  29. package/skills/odla-migrate/references/project-state.md +4 -4
  30. package/skills/odla-migrate/references/secrets-map.md +6 -6
  31. package/skills/odla-migrate/references/troubleshooting.md +23 -23
  32. package/skills/odla-o11y-debug/SKILL.md +3 -3
  33. package/dist/chunk-YSQORU5J.js.map +0 -1
  34. /package/dist/{cli-U436OLYW.js.map → cli-NKNQLWOM.js.map} +0 -0
@@ -40,9 +40,10 @@ function approvalLines(prompt) {
40
40
  lines.push(` No browser was opened (${prompt.browserSkipped}).`);
41
41
  }
42
42
  lines.push("");
43
- lines.push(" AGENTS: use browser control to open the URL above now; do not wait silently.");
44
- lines.push(" If browser control is unavailable, give the exact URL to the human verbatim.");
45
- lines.push(" You cannot approve it yourself, retry it away, or start a substitute handshake.");
43
+ lines.push(" AGENTS: immediately give the human this URL as a clickable approval action and repeat the code.");
44
+ lines.push(" Keep this CLI process running and wait on this same process; the CLI owns protocol polling.");
45
+ lines.push(" Do not use OS open, browser control, curl, a shell wait loop, detached execution, or a substitute handshake.");
46
+ lines.push(" You cannot approve it yourself. If this process exits, a later invocation creates a new code.");
46
47
  lines.push("");
47
48
  return lines;
48
49
  }
@@ -257,10 +258,10 @@ function isManagedDevVar(line) {
257
258
  const match = line.match(/^\s*(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/);
258
259
  return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
259
260
  }
260
- function writePrivateText(path, text2) {
261
+ function writePrivateText(path, text3) {
261
262
  mkdirSync(dirname2(path), { recursive: true });
262
263
  const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
263
- writeFileSync(temporary, text2, { mode: 384 });
264
+ writeFileSync(temporary, text3, { mode: 384 });
264
265
  chmodSync(temporary, 384);
265
266
  renameSync(temporary, path);
266
267
  }
@@ -375,7 +376,7 @@ async function freshHandshake(ctx, waitMs) {
375
376
  }
376
377
  function cachedGrantCovers(cached, required) {
377
378
  if (required.optionalProjectCapabilities.length === 0) return true;
378
- return required.projectIds.every((id) => cached.projectIds?.includes(id)) && required.optionalProjectCapabilities.every(
379
+ return required.projectIds.every((id2) => cached.projectIds?.includes(id2)) && required.optionalProjectCapabilities.every(
379
380
  (capability) => cached.optionalProjectCapabilities?.includes(capability)
380
381
  );
381
382
  }
@@ -534,8 +535,8 @@ async function scopedToken(platform, scope, options, doFetch, out) {
534
535
  // src/principal-presentation.ts
535
536
  function unresolvedPrincipalLabel(credentialKind2, principalId) {
536
537
  const kind = typeof credentialKind2 === "string" ? credentialKind2.trim() : "";
537
- const id = typeof principalId === "string" ? principalId.trim() : "";
538
- const audit = kind && id ? `${kind}:${id}` : kind || id;
538
+ const id2 = typeof principalId === "string" ? principalId.trim() : "";
539
+ const audit = kind && id2 ? `${kind}:${id2}` : kind || id2;
539
540
  return `Unknown principal${audit ? ` [${audit}]` : ""}`;
540
541
  }
541
542
 
@@ -547,38 +548,38 @@ function adminAiAuditQuery(filters) {
547
548
  }
548
549
  return `?limit=${filters.limit}`;
549
550
  }
550
- async function readAdminAiAudit(request2) {
551
- const response2 = await request2.fetch(`${request2.platform}/registry/platform/ai-audit${request2.query}`, {
552
- headers: request2.headers
551
+ async function readAdminAiAudit(request3) {
552
+ const response2 = await request3.fetch(`${request3.platform}/registry/platform/ai-audit${request3.query}`, {
553
+ headers: request3.headers
553
554
  });
554
555
  const body = await responseBody(response2);
555
556
  if (!response2.ok) throw new Error(apiError(response2.status, body));
556
- if (request2.json) {
557
- request2.stdout.log(JSON.stringify(body, null, 2));
557
+ if (request3.json) {
558
+ request3.stdout.log(JSON.stringify(body, null, 2));
558
559
  return;
559
560
  }
560
561
  const events = isRecord(body) && Array.isArray(body.events) ? body.events.filter(isRecord) : [];
561
- request2.stdout.log("when change target before -> after actor");
562
+ request3.stdout.log("when change target before -> after actor");
562
563
  for (const event of events) {
563
564
  const before = isRecord(event.oldPolicy) ? event.oldPolicy : void 0;
564
565
  const after = isRecord(event.newPolicy) ? event.newPolicy : void 0;
565
- 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";
566
- request2.stdout.log([
566
+ 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";
567
+ request3.stdout.log([
567
568
  timestamp(event.createdAt),
568
569
  String(event.changeKind ?? ""),
569
570
  String(event.purpose ?? event.provider ?? ""),
570
- route2,
571
+ route3,
571
572
  unresolvedPrincipalLabel(event.actorType, event.actorId)
572
573
  ].join(" "));
573
574
  }
574
575
  }
575
576
  async function responseBody(response2) {
576
- const text2 = await response2.text();
577
- if (!text2) return {};
577
+ const text3 = await response2.text();
578
+ if (!text3) return {};
578
579
  try {
579
- return JSON.parse(text2);
580
+ return JSON.parse(text3);
580
581
  } catch {
581
- return { message: text2.slice(0, 300) };
582
+ return { message: text3.slice(0, 300) };
582
583
  }
583
584
  }
584
585
  function apiError(status, body) {
@@ -619,14 +620,14 @@ function adminAiUsageQuery(filters) {
619
620
  const query = params.toString();
620
621
  return query ? `?${query}` : "";
621
622
  }
622
- async function readAdminAiUsage(request2) {
623
- const res = await request2.fetch(`${request2.platform}/registry/platform/ai-usage${request2.query}`, {
624
- headers: request2.headers
623
+ async function readAdminAiUsage(request3) {
624
+ const res = await request3.fetch(`${request3.platform}/registry/platform/ai-usage${request3.query}`, {
625
+ headers: request3.headers
625
626
  });
626
627
  const body = await responseBody2(res);
627
628
  if (!res.ok) throw new Error(apiError2("read platform AI usage", res.status, body));
628
- if (request2.json) request2.stdout.log(JSON.stringify(body, null, 2));
629
- else printUsage(body, request2.stdout);
629
+ if (request3.json) request3.stdout.log(JSON.stringify(body, null, 2));
630
+ else printUsage(body, request3.stdout);
630
631
  }
631
632
  function usageLimit(value2) {
632
633
  if (!Number.isSafeInteger(value2) || value2 < 1 || value2 > 500) {
@@ -680,12 +681,12 @@ function timestamp2(value2) {
680
681
  return Number.isFinite(date.valueOf()) ? date.toISOString() : "";
681
682
  }
682
683
  async function responseBody2(res) {
683
- const text2 = await res.text();
684
- if (!text2) return {};
684
+ const text3 = await res.text();
685
+ if (!text3) return {};
685
686
  try {
686
- return JSON.parse(text2);
687
+ return JSON.parse(text3);
687
688
  } catch {
688
- return { message: text2.slice(0, 300) };
689
+ return { message: text3.slice(0, 300) };
689
690
  }
690
691
  }
691
692
  function apiError2(action2, status, body) {
@@ -861,12 +862,12 @@ function catalogModels(body) {
861
862
  return body.catalog.models.filter((value2) => isRecord3(value2) && typeof value2.id === "string" && typeof value2.provider === "string");
862
863
  }
863
864
  async function responseBody3(res) {
864
- const text2 = await res.text();
865
- if (!text2) return {};
865
+ const text3 = await res.text();
866
+ if (!text3) return {};
866
867
  try {
867
- return JSON.parse(text2);
868
+ return JSON.parse(text3);
868
869
  } catch {
869
- return { message: text2.slice(0, 300) };
870
+ return { message: text3.slice(0, 300) };
870
871
  }
871
872
  }
872
873
  function apiError3(action2, status, body) {
@@ -998,7 +999,7 @@ function calendarServiceConfig(cfg, env) {
998
999
  if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
999
1000
  const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
1000
1001
  if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
1001
- const availability = unique(configured.map((id) => id.trim()));
1002
+ const availability = unique(configured.map((id2) => id2.trim()));
1002
1003
  return {
1003
1004
  provider: "google",
1004
1005
  access: "book",
@@ -1047,7 +1048,7 @@ function validateCalendarConfig(cfg, envs, services, path) {
1047
1048
  if (ids.length > 10) {
1048
1049
  throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
1049
1050
  }
1050
- if (ids.some((id) => !safeText2(id, 1024))) {
1051
+ if (ids.some((id2) => !safeText2(id2, 1024))) {
1051
1052
  throw new Error(`${path}: calendar.google.${availabilityKey}.${env} contains an invalid calendar id`);
1052
1053
  }
1053
1054
  }
@@ -1189,6 +1190,175 @@ function unique2(values) {
1189
1190
  return [...new Set(values.filter(Boolean))];
1190
1191
  }
1191
1192
 
1193
+ // src/monitoring-validation.ts
1194
+ var CADENCES = /* @__PURE__ */ new Set(["1m", "2m", "5m", "10m", "15m", "30m", "1h"]);
1195
+ var WINDOWS = /* @__PURE__ */ new Set(["7d", "28d", "30d"]);
1196
+ var SHORT = /* @__PURE__ */ new Set(["30m", "1h", "6h", "12h", "1d"]);
1197
+ var LONG = /* @__PURE__ */ new Set(["1d", "3d", "7d"]);
1198
+ var DAYS = /* @__PURE__ */ new Set(["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]);
1199
+ var O11Y_METRICS = /* @__PURE__ */ new Set(["error_rate", "latency_p95", "synthetic_success", "synthetic_publish_to_visible"]);
1200
+ var COMPARATORS = /* @__PURE__ */ new Set(["gt", "gte", "lt", "lte"]);
1201
+ function validateMonitoringConfig(cfg, envs, services, path) {
1202
+ if (!cfg.o11y) return;
1203
+ if (!record(cfg.o11y)) fail(path, "o11y must be an object");
1204
+ only(cfg.o11y, ["service", "endpoint", "version", "monitoring"], `${path}: o11y`);
1205
+ const monitoring = cfg.o11y.monitoring;
1206
+ if (!monitoring) return;
1207
+ if (!services.includes("o11y")) fail(path, 'o11y.monitoring requires "o11y" in services');
1208
+ if (!record(monitoring)) fail(path, "o11y.monitoring must be an object");
1209
+ only(monitoring, ["probes", "slos", "notifications"], `${path}: o11y.monitoring`);
1210
+ if (monitoring.probes !== void 0 && (!Array.isArray(monitoring.probes) || monitoring.probes.length > 50)) {
1211
+ fail(path, "o11y.monitoring.probes must contain at most 50 probes");
1212
+ }
1213
+ const probeIds = /* @__PURE__ */ new Set();
1214
+ (monitoring.probes ?? []).forEach((probe, index) => validateProbe(probe, index, envs, path, probeIds));
1215
+ if (!Array.isArray(monitoring.slos) || monitoring.slos.length < 1 || monitoring.slos.length > 50) {
1216
+ fail(path, "o11y.monitoring.slos must contain 1 through 50 SLOs");
1217
+ }
1218
+ const sloIds = /* @__PURE__ */ new Set();
1219
+ monitoring.slos.forEach((slo, index) => validateSlo(slo, index, path, probeIds, sloIds));
1220
+ if (monitoring.notifications !== void 0) validateNotifications(monitoring.notifications, envs, path);
1221
+ }
1222
+ function validateProbe(value2, index, envs, path, ids) {
1223
+ const label = `${path}: o11y.monitoring.probes[${index}]`;
1224
+ if (!record(value2)) fail(label, "must be an object");
1225
+ only(value2, ["id", "route", "envs", "every", "timeout", "ready", "expect", "enabled"], label);
1226
+ if (!id(value2.id)) fail(label, "id must be lowercase letters, numbers, and hyphens");
1227
+ if (ids.has(value2.id)) fail(label, `id duplicates ${value2.id}`);
1228
+ ids.add(value2.id);
1229
+ if (!route(value2.route)) fail(label, "route must be a relative absolute path without credentials or a fragment");
1230
+ if (!CADENCES.has(String(value2.every))) fail(label, `every must be one of ${[...CADENCES].join(", ")}`);
1231
+ 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");
1232
+ 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");
1233
+ if (value2.ready !== void 0) {
1234
+ if (!record(value2.ready) || !text(value2.ready.selector, 300)) fail(label, "ready.selector is required");
1235
+ only(value2.ready, ["selector"], `${label}.ready`);
1236
+ }
1237
+ if (!record(value2.expect)) fail(label, "expect must be an object");
1238
+ only(value2.expect, ["status", "titleIncludes", "textIncludes", "accessibility"], `${label}.expect`);
1239
+ 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");
1240
+ if (value2.expect.titleIncludes !== void 0 && !text(value2.expect.titleIncludes, 300)) fail(label, "expect.titleIncludes is invalid");
1241
+ 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");
1242
+ if (value2.expect.accessibility !== void 0) validateAccessibility(value2.expect.accessibility, label);
1243
+ }
1244
+ function validateAccessibility(value2, label) {
1245
+ if (!Array.isArray(value2) || value2.length > 10) fail(label, "expect.accessibility must contain at most 10 assertions");
1246
+ for (const item of value2) {
1247
+ if (!record(item) || !text(item.role, 80) || !text(item.name, 300)) fail(label, "expect.accessibility entries need role and name");
1248
+ only(item, ["role", "name"], `${label}.expect.accessibility`);
1249
+ }
1250
+ }
1251
+ function validateSlo(value2, index, path, probes, ids) {
1252
+ const label = `${path}: o11y.monitoring.slos[${index}]`;
1253
+ if (!record(value2)) fail(label, "must be an object");
1254
+ only(value2, ["id", "name", "indicator", "target", "window", "alerts", "enabled"], label);
1255
+ if (!id(value2.id) || ids.has(value2.id)) fail(label, "id must be unique lowercase letters, numbers, and hyphens");
1256
+ ids.add(value2.id);
1257
+ if (value2.name !== void 0 && !text(value2.name, 160)) fail(label, "name is invalid");
1258
+ validateIndicator(value2.indicator, label, probes);
1259
+ 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");
1260
+ if (!WINDOWS.has(String(value2.window))) fail(label, `window must be one of ${[...WINDOWS].join(", ")}`);
1261
+ if (value2.alerts !== void 0) validateAlerts(value2.alerts, label);
1262
+ }
1263
+ function validateIndicator(value2, label, probes) {
1264
+ if (!record(value2)) fail(label, "indicator must be an object");
1265
+ if (value2.type === "probe-success") {
1266
+ only(value2, ["type", "probes"], `${label}.indicator`);
1267
+ if (!Array.isArray(value2.probes) || value2.probes.length < 1) fail(label, "probe-success must select at least one probe");
1268
+ if (value2.probes.some((probe) => typeof probe !== "string" || !probes.has(probe))) fail(label, "indicator references an unknown probe");
1269
+ return;
1270
+ }
1271
+ if (value2.type !== "o11y-metric") fail(label, "indicator.type must be probe-success or o11y-metric");
1272
+ only(value2, ["type", "metric", "comparator", "threshold", "every", "observationWindow", "route"], `${label}.indicator`);
1273
+ if (!O11Y_METRICS.has(String(value2.metric))) fail(label, "indicator.metric is unsupported");
1274
+ if (!COMPARATORS.has(String(value2.comparator))) fail(label, "indicator.comparator is unsupported");
1275
+ if (typeof value2.threshold !== "number" || !Number.isFinite(value2.threshold)) fail(label, "indicator.threshold must be finite");
1276
+ if (!CADENCES.has(String(value2.every)) || !CADENCES.has(String(value2.observationWindow))) fail(label, "indicator cadence and observationWindow must be supported durations");
1277
+ if (value2.route !== void 0 && !routePattern(value2.route)) fail(label, "indicator.route must be an exact route template or trailing-* prefix");
1278
+ if ((value2.metric === "synthetic_success" || value2.metric === "synthetic_publish_to_visible") && value2.route !== void 0) fail(label, "synthetic indicators cannot select a route");
1279
+ }
1280
+ function validateAlerts(value2, label) {
1281
+ if (!record(value2)) fail(label, "alerts must be an object");
1282
+ only(value2, ["spike", "trend"], `${label}.alerts`);
1283
+ if (value2.spike !== void 0) {
1284
+ if (!record(value2.spike)) fail(label, "alerts.spike must be an object");
1285
+ only(value2.spike, ["badChecks", "withinChecks", "recoverAfter"], `${label}.alerts.spike`);
1286
+ const bad = positive(value2.spike.badChecks, 2), within = positive(value2.spike.withinChecks, 3), recover = positive(value2.spike.recoverAfter, 2);
1287
+ if (bad > within || within > 20 || recover > 20) fail(label, "alerts.spike requires badChecks <= withinChecks <= 20 and recoverAfter <= 20");
1288
+ }
1289
+ if (value2.trend !== void 0) {
1290
+ if (!record(value2.trend)) fail(label, "alerts.trend must be an object");
1291
+ only(value2.trend, ["burnRate", "shortWindow", "longWindow", "minBadChecks"], `${label}.alerts.trend`);
1292
+ const burn = value2.trend.burnRate ?? 1;
1293
+ if (typeof burn !== "number" || !Number.isFinite(burn) || burn <= 0 || burn > 1e3) fail(label, "alerts.trend.burnRate must be greater than 0");
1294
+ if (value2.trend.shortWindow !== void 0 && !SHORT.has(String(value2.trend.shortWindow))) fail(label, "alerts.trend.shortWindow is unsupported");
1295
+ if (value2.trend.longWindow !== void 0 && !LONG.has(String(value2.trend.longWindow))) fail(label, "alerts.trend.longWindow is unsupported");
1296
+ if (positive(value2.trend.minBadChecks, 2) > 100) fail(label, "alerts.trend.minBadChecks must be at most 100");
1297
+ }
1298
+ }
1299
+ function validateNotifications(value2, envs, path) {
1300
+ if (!record(value2)) fail(path, "o11y.monitoring.notifications must map environments to policies");
1301
+ for (const [env, policy] of Object.entries(value2)) {
1302
+ const label = `${path}: o11y.monitoring.notifications.${env}`;
1303
+ if (!envs.includes(env) && env !== "prod") fail(label, "is not a configured environment");
1304
+ if (!record(policy)) fail(label, "must be an object");
1305
+ only(policy, ["email", "timezone", "daily", "weekly"], label);
1306
+ 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");
1307
+ if (!timezone(policy.timezone)) fail(label, "timezone must be an IANA timezone");
1308
+ if (policy.daily !== void 0 && policy.daily !== false && !clock(policy.daily)) fail(label, "daily must be HH:MM or false");
1309
+ if (policy.weekly !== void 0 && policy.weekly !== false) {
1310
+ if (!record(policy.weekly) || !DAYS.has(String(policy.weekly.day)) || !clock(policy.weekly.at)) fail(label, "weekly needs a weekday and HH:MM time");
1311
+ only(policy.weekly, ["day", "at"], `${label}.weekly`);
1312
+ }
1313
+ }
1314
+ }
1315
+ function fail(label, message2) {
1316
+ throw new Error(`${label}: ${message2}`);
1317
+ }
1318
+ function record(value2) {
1319
+ return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
1320
+ }
1321
+ function only(value2, keys, label) {
1322
+ const extra = Object.keys(value2).find((key) => !keys.includes(key));
1323
+ if (extra) fail(label, `${extra} is not supported`);
1324
+ }
1325
+ function id(value2) {
1326
+ return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
1327
+ }
1328
+ function text(value2, max) {
1329
+ return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
1330
+ }
1331
+ function positive(value2, fallback) {
1332
+ return value2 === void 0 ? fallback : Number.isSafeInteger(value2) && Number(value2) > 0 ? Number(value2) : Infinity;
1333
+ }
1334
+ function route(value2) {
1335
+ if (typeof value2 !== "string" || value2.length > 2048 || !value2.startsWith("/") || value2.startsWith("//")) return false;
1336
+ try {
1337
+ const url = new URL(value2, "https://probe.invalid");
1338
+ return url.origin === "https://probe.invalid" && !url.hash;
1339
+ } catch {
1340
+ return false;
1341
+ }
1342
+ }
1343
+ function routePattern(value2) {
1344
+ return typeof value2 === "string" && value2.length <= 160 && /^\/[A-Za-z0-9_./:-]+\*?$/.test(value2) && !value2.slice(0, -1).includes("*");
1345
+ }
1346
+ function emailAddress(value2) {
1347
+ return typeof value2 === "string" && value2.length <= 254 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value2);
1348
+ }
1349
+ function clock(value2) {
1350
+ return typeof value2 === "string" && /^(?:[01]\d|2[0-3]):[0-5]\d$/.test(value2);
1351
+ }
1352
+ function timezone(value2) {
1353
+ if (typeof value2 !== "string" || value2.length > 100) return false;
1354
+ try {
1355
+ new Intl.DateTimeFormat("en", { timeZone: value2 }).format(0);
1356
+ return true;
1357
+ } catch {
1358
+ return false;
1359
+ }
1360
+ }
1361
+
1192
1362
  // src/config.ts
1193
1363
  var DEFAULT_PLATFORM = "https://odla.ai";
1194
1364
  var DEFAULT_ENVS = ["dev"];
@@ -1209,6 +1379,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
1209
1379
  const services = unique3(raw.services?.length ? raw.services : DEFAULT_SERVICES);
1210
1380
  validateServices(services, resolved);
1211
1381
  validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
1382
+ validateMonitoringConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
1212
1383
  const local = {
1213
1384
  tokenFile: resolve2(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
1214
1385
  credentialsFile: resolve2(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
@@ -1610,7 +1781,7 @@ async function adminCommand(parsed, deps = {}) {
1610
1781
  import process11 from "process";
1611
1782
 
1612
1783
  // src/whoami-command.ts
1613
- var text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
1784
+ var text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
1614
1785
  function principalKind(value2, machine) {
1615
1786
  return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
1616
1787
  }
@@ -1623,12 +1794,12 @@ function credentialKind(value2, machine, scopes) {
1623
1794
  function managerOf(value2) {
1624
1795
  if (!value2 || typeof value2 !== "object") return null;
1625
1796
  const row = value2;
1626
- const principalId = text(row.principalId);
1797
+ const principalId = text2(row.principalId);
1627
1798
  if (!principalId) return null;
1628
1799
  return {
1629
1800
  principalId,
1630
- displayName: text(row.displayName) ?? "Unnamed member",
1631
- handle: text(row.handle) ?? ""
1801
+ displayName: text2(row.displayName) ?? "Unnamed member",
1802
+ handle: text2(row.handle) ?? ""
1632
1803
  };
1633
1804
  }
1634
1805
  function unnamedPrincipal(kind) {
@@ -1642,14 +1813,14 @@ async function fetchIdentity(platformUrl, token, doFetch) {
1642
1813
  });
1643
1814
  if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
1644
1815
  const body = await res.json();
1645
- const developerId = text(body.developerId) ?? "";
1816
+ const developerId = text2(body.developerId) ?? "";
1646
1817
  const machine = body.machine === true;
1647
1818
  const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
1648
- const principalId = text(body.principalId) ?? developerId;
1649
- const email = text(body.email);
1819
+ const principalId = text2(body.principalId) ?? developerId;
1820
+ const email = text2(body.email);
1650
1821
  const kind = principalKind(body.principalKind, machine);
1651
- const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
1652
- const handle = text(body.handle) ?? "";
1822
+ const displayName = text2(body.displayName) ?? email ?? unnamedPrincipal(kind);
1823
+ const handle = text2(body.handle) ?? "";
1653
1824
  const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
1654
1825
  return {
1655
1826
  developerId,
@@ -1659,7 +1830,7 @@ async function fetchIdentity(platformUrl, token, doFetch) {
1659
1830
  handle,
1660
1831
  manager: managerOf(body.manager),
1661
1832
  credential: {
1662
- id: text(credential2.id),
1833
+ id: text2(credential2.id),
1663
1834
  kind: credentialKind(credential2.kind, machine, scopes)
1664
1835
  },
1665
1836
  email,
@@ -1854,13 +2025,13 @@ async function agentCommand(parsed, deps = {}) {
1854
2025
  const base = `${cfg.dbEndpoint}/app/${encodeURIComponent(tenant)}/admin/agent-jobs`;
1855
2026
  const headers = { authorization: `Bearer ${credential2}` };
1856
2027
  if (action2 === "retry") {
1857
- const id = parsed.positionals[2];
1858
- const res2 = await doFetch(`${base}/${encodeURIComponent(id)}/retry`, { method: "POST", headers });
2028
+ const id2 = parsed.positionals[2];
2029
+ const res2 = await doFetch(`${base}/${encodeURIComponent(id2)}/retry`, { method: "POST", headers });
1859
2030
  const body2 = await readJson(res2);
1860
2031
  if (!res2.ok) throw new Error(`agent retry failed (${res2.status}): ${errorMessage(body2)}`);
1861
2032
  const result2 = { v: 1, appId: cfg.app.id, env, tenant, ...body2 };
1862
2033
  if (parsed.options.json === true) out.log(JSON.stringify(result2, null, 2));
1863
- else out.log(`${tenant}: requeued ${id}`);
2034
+ else out.log(`${tenant}: requeued ${id2}`);
1864
2035
  return;
1865
2036
  }
1866
2037
  const state2 = stringOpt(parsed.options.state);
@@ -1944,8 +2115,8 @@ async function appImport(options) {
1944
2115
  const out = options.stdout ?? console;
1945
2116
  const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
1946
2117
  const { tenant } = resolveTenant(cfg, options.env);
1947
- const text2 = options.file === "-" ? (options.readStdin ?? (() => readFileSync4(0, "utf8")))() : readFileSync4(options.file, "utf8");
1948
- const { format, sources } = parseImport(text2, options.ns);
2118
+ const text3 = options.file === "-" ? (options.readStdin ?? (() => readFileSync4(0, "utf8")))() : readFileSync4(options.file, "utf8");
2119
+ const { format, sources } = parseImport(text3, options.ns);
1949
2120
  if (format === "namespace-map" && options.ns) {
1950
2121
  throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
1951
2122
  }
@@ -2161,7 +2332,7 @@ var EXTENSIONS = {
2161
2332
  "text/html": "html",
2162
2333
  "application/json": "json"
2163
2334
  };
2164
- var encode = (text2) => new TextEncoder().encode(text2);
2335
+ var encode = (text3) => new TextEncoder().encode(text3);
2165
2336
  function assetFileName(uuid, mime) {
2166
2337
  const ext = EXTENSIONS[mime.split(";")[0].trim().toLowerCase()] ?? "bin";
2167
2338
  return `${uuid.replace(/[^a-zA-Z0-9._-]/g, "_")}.${ext}`;
@@ -2323,18 +2494,18 @@ async function readCalendarStatus(ctx) {
2323
2494
  }
2324
2495
  async function discoverGoogleCalendars(ctx) {
2325
2496
  const raw = await calendarJson(ctx, "/calendars", {});
2326
- const value2 = record(raw);
2497
+ const value2 = record2(raw);
2327
2498
  if (!value2 || !Array.isArray(value2.calendars)) throw new Error("calendar discovery returned an invalid response");
2328
2499
  return value2.calendars.map((item, index) => {
2329
- const calendar = record(item);
2330
- const id = textField(calendar?.id, 1024);
2331
- if (!calendar || !id) throw new Error(`calendar discovery returned an invalid calendar at index ${index}`);
2500
+ const calendar = record2(item);
2501
+ const id2 = textField(calendar?.id, 1024);
2502
+ if (!calendar || !id2) throw new Error(`calendar discovery returned an invalid calendar at index ${index}`);
2332
2503
  const role = calendar.accessRole;
2333
2504
  if (role !== void 0 && role !== "freeBusyReader" && role !== "reader" && role !== "writer" && role !== "owner") {
2334
2505
  throw new Error(`calendar discovery returned an invalid access role at index ${index}`);
2335
2506
  }
2336
2507
  return {
2337
- id,
2508
+ id: id2,
2338
2509
  ...optionalText("summary", calendar.summary, 500),
2339
2510
  ...typeof calendar.primary === "boolean" ? { primary: calendar.primary } : {},
2340
2511
  ...typeof calendar.selected === "boolean" ? { selected: calendar.selected } : {},
@@ -2362,10 +2533,10 @@ async function pollCalendarConnection(ctx, attemptId) {
2362
2533
  }
2363
2534
  function parseCalendarStatus(raw, env) {
2364
2535
  const outer = wrapped(raw, "calendar");
2365
- const value2 = record(outer.attempt) ?? record(outer.status) ?? outer;
2366
- const connection = record(value2.connection) ?? {};
2367
- const config = record(value2.config) ?? record(outer.config) ?? {};
2368
- const googleConfig = record(config.google) ?? config;
2536
+ const value2 = record2(outer.attempt) ?? record2(outer.status) ?? outer;
2537
+ const connection = record2(value2.connection) ?? {};
2538
+ const config = record2(value2.config) ?? record2(outer.config) ?? {};
2539
+ const googleConfig = record2(config.google) ?? config;
2369
2540
  const stateValue = calendarState(value2.status ?? value2.state ?? connection.status ?? connection.state);
2370
2541
  if (!stateValue) {
2371
2542
  throw new Error("calendar status returned an invalid connection state");
@@ -2378,7 +2549,7 @@ function parseCalendarStatus(raw, env) {
2378
2549
  if (accessValue !== void 0 && accessValue !== "book" && accessValue !== "read") {
2379
2550
  throw new Error("calendar status returned unsupported access");
2380
2551
  }
2381
- const errorValue = record(value2.error) ?? record(connection.error);
2552
+ const errorValue = record2(value2.error) ?? record2(connection.error);
2382
2553
  const errorCode2 = textField(value2.lastErrorCode, 128);
2383
2554
  const bookingPageValue = Object.hasOwn(value2, "bookingPageUrl") ? value2.bookingPageUrl : Object.hasOwn(config, "bookingPageUrl") ? config.bookingPageUrl : googleConfig.bookingPageUrl;
2384
2555
  const connected = typeof (value2.connected ?? connection.connected) === "boolean" ? Boolean(value2.connected ?? connection.connected) : ["healthy", "degraded"].includes(stateValue);
@@ -2448,11 +2619,11 @@ async function calendarJson(ctx, suffix, init) {
2448
2619
  return body;
2449
2620
  }
2450
2621
  function wrapped(raw, key) {
2451
- const outer = record(raw);
2622
+ const outer = record2(raw);
2452
2623
  if (!outer) throw new Error("calendar returned an invalid response");
2453
- return record(outer[key]) ?? outer;
2624
+ return record2(outer[key]) ?? outer;
2454
2625
  }
2455
- function record(value2) {
2626
+ function record2(value2) {
2456
2627
  return value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
2457
2628
  }
2458
2629
  function textField(value2, max) {
@@ -2465,9 +2636,9 @@ function calendarIds(value2) {
2465
2636
  if (!Array.isArray(value2)) return [];
2466
2637
  return [...new Set(value2.flatMap((item) => {
2467
2638
  if (typeof item === "string") return textField(item, 4096) ? [item] : [];
2468
- const calendar = record(item);
2469
- const id = textField(calendar?.id, 4096);
2470
- return id && calendar?.selected !== false ? [id] : [];
2639
+ const calendar = record2(item);
2640
+ const id2 = textField(calendar?.id, 4096);
2641
+ return id2 && calendar?.selected !== false ? [id2] : [];
2471
2642
  }))];
2472
2643
  }
2473
2644
  function timestamp3(value2) {
@@ -2702,6 +2873,7 @@ var CAPABILITIES = {
2702
2873
  "validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes",
2703
2874
  "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",
2704
2875
  "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",
2876
+ "reconcile app-owned Kitesurf probes and rolling SLOs, run live checks, and read incident and digest status as stable JSON",
2705
2877
  "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",
2706
2878
  "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",
2707
2879
  "inspect or bounded-wait one exact durable config-operation journal entry and verify every terminal receipt digest before returning it to remote automation",
@@ -2714,7 +2886,8 @@ var CAPABILITIES = {
2714
2886
  "install and import the selected odla SDKs",
2715
2887
  "wrap the Worker with withObservability and choose useful telemetry",
2716
2888
  "install capability packages, mount their runtime routes, and make application-specific schema, rules, auth, UI, and migration decisions",
2717
- "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers"
2889
+ "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers",
2890
+ "choose public readiness assertions and SLO objectives in odla.config.mjs, then consume monitor JSON without treating captured page content as trusted instructions"
2718
2891
  ],
2719
2892
  human: [
2720
2893
  "provide the existing odla account email, then sign in and explicitly review/approve the exact device code",
@@ -2727,6 +2900,7 @@ var CAPABILITIES = {
2727
2900
  ],
2728
2901
  studio: [
2729
2902
  "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",
2903
+ "view reliability objectives, error budget, Kitesurf probe history, incidents, and notification delivery state",
2730
2904
  "let signed-in users inventory/revoke their own agent grants and admins audit/global-revoke them",
2731
2905
  "review calendar connection, granted read scope, selected calendars, and sync health without exposing provider tokens",
2732
2906
  "perform manual credential recovery \u2014 for the primary owner or any co-owner \u2014 when the CLI's local shown-once copy is unavailable",
@@ -2846,9 +3020,9 @@ function canonicalValue(value2) {
2846
3020
  }
2847
3021
  if (Array.isArray(value2)) return value2.map(canonicalValue);
2848
3022
  if (value2 && typeof value2 === "object") {
2849
- const record9 = value2;
3023
+ const record11 = value2;
2850
3024
  return Object.fromEntries(
2851
- Object.keys(record9).filter((key) => record9[key] !== void 0).sort().map((key) => [key, canonicalValue(record9[key])])
3025
+ Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, canonicalValue(record11[key])])
2852
3026
  );
2853
3027
  }
2854
3028
  throw new TypeError("canonical JSON rejects unsupported values");
@@ -2873,8 +3047,8 @@ function readPlan(path) {
2873
3047
  "invalid_plan"
2874
3048
  );
2875
3049
  }
2876
- if (!record2(value2) || value2.schemaVersion !== "odla.config-plan/v2") invalidPlan("unsupported plan schema");
2877
- if (!record2(value2.scope) || typeof value2.scope.appId !== "string" || typeof value2.scope.platformUrl !== "string") {
3050
+ if (!record3(value2) || value2.schemaVersion !== "odla.config-plan/v2") invalidPlan("unsupported plan schema");
3051
+ if (!record3(value2.scope) || typeof value2.scope.appId !== "string" || typeof value2.scope.platformUrl !== "string") {
2878
3052
  invalidPlan("plan scope is invalid");
2879
3053
  }
2880
3054
  if (!DIGEST.test(String(value2.desiredRevision)) || !DIGEST.test(String(value2.observedRevision))) {
@@ -2924,10 +3098,10 @@ function assertOperationId(value2) {
2924
3098
  function assertActions(actions) {
2925
3099
  const ids = /* @__PURE__ */ new Set();
2926
3100
  for (const action2 of actions) {
2927
- if (!record2(action2)) invalidPlan("every plan action must be an object");
2928
- const id = String(action2.id ?? "");
2929
- if (!ACTION_ID.test(id) || ids.has(id)) invalidPlan("plan action ids must be unique frozen ids");
2930
- ids.add(id);
3101
+ if (!record3(action2)) invalidPlan("every plan action must be an object");
3102
+ const id2 = String(action2.id ?? "");
3103
+ if (!ACTION_ID.test(id2) || ids.has(id2)) invalidPlan("plan action ids must be unique frozen ids");
3104
+ ids.add(id2);
2931
3105
  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))) {
2932
3106
  invalidPlan("plan action metadata is invalid");
2933
3107
  }
@@ -2953,14 +3127,14 @@ function assertConditionalAction(action2) {
2953
3127
  if (action2.path !== (action2.kind === "configure_service" ? `${base}.config` : base)) {
2954
3128
  invalidPlan("service action path is invalid");
2955
3129
  }
2956
- if (action2.applySupport !== "provision" || !record2(action2.after)) {
3130
+ if (action2.applySupport !== "provision" || !record3(action2.after)) {
2957
3131
  invalidPlan("service action payload is invalid");
2958
3132
  }
2959
3133
  if (action2.kind === "enable_service") {
2960
- if (action2.after.enabled !== true || action2.before !== null && !record2(action2.before)) {
3134
+ if (action2.after.enabled !== true || action2.before !== null && !record3(action2.before)) {
2961
3135
  invalidPlan("service enable action is invalid");
2962
3136
  }
2963
- } else if (!record2(action2.before)) {
3137
+ } else if (!record3(action2.before)) {
2964
3138
  invalidPlan("service configure action is invalid");
2965
3139
  }
2966
3140
  }
@@ -2977,7 +3151,7 @@ function linkState(value2) {
2977
3151
  function invalidPlan(message2) {
2978
3152
  throw new ConfigOperationCommandError(message2, "invalid_plan");
2979
3153
  }
2980
- function record2(value2) {
3154
+ function record3(value2) {
2981
3155
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
2982
3156
  }
2983
3157
 
@@ -3016,9 +3190,9 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
3016
3190
  }
3017
3191
  throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
3018
3192
  }
3019
- function errorCode(text2) {
3193
+ function errorCode(text3) {
3020
3194
  try {
3021
- const body = JSON.parse(text2);
3195
+ const body = JSON.parse(text3);
3022
3196
  return typeof body.error?.code === "string" ? body.error.code : null;
3023
3197
  } catch {
3024
3198
  return null;
@@ -3161,7 +3335,7 @@ async function configApply(options) {
3161
3335
  throw new ConfigOperationCommandError("--idempotency-key is not a safe 1-120 character key", "invalid_plan");
3162
3336
  }
3163
3337
  const client = await operationClient(cfg, options, "apply");
3164
- const request2 = {
3338
+ const request3 = {
3165
3339
  schemaVersion: "odla.config-operation-request/v1",
3166
3340
  expectedRevision: plan.registryRevision,
3167
3341
  desiredRevision: plan.desiredRevision,
@@ -3173,7 +3347,7 @@ async function configApply(options) {
3173
3347
  };
3174
3348
  let receipt;
3175
3349
  try {
3176
- receipt = await client.applyConfigOperation(cfg.app.id, request2);
3350
+ receipt = await client.applyConfigOperation(cfg.app.id, request3);
3177
3351
  } catch (error) {
3178
3352
  const retained = retainedReceipt(error);
3179
3353
  if (retained) {
@@ -3272,8 +3446,8 @@ function failureForReceipt(receipt) {
3272
3446
  return new ConfigOperationCommandError(receipt.error?.message ?? `config operation ${receipt.state}`, "config_operation_failed");
3273
3447
  }
3274
3448
  function retainedReceipt(error) {
3275
- if (!(error instanceof AppsError) || !record3(error.details)) return null;
3276
- return record3(error.details.operation) ? error.details.operation : null;
3449
+ if (!(error instanceof AppsError) || !record4(error.details)) return null;
3450
+ return record4(error.details.operation) ? error.details.operation : null;
3277
3451
  }
3278
3452
  function normalizeRequestError(error) {
3279
3453
  if (!(error instanceof AppsError)) return error instanceof Error ? error : new Error(String(error));
@@ -3286,7 +3460,7 @@ function normalizeRequestError(error) {
3286
3460
  }
3287
3461
  return new ConfigOperationCommandError(error.message, error.code || "config_operation_failed");
3288
3462
  }
3289
- function record3(value2) {
3463
+ function record4(value2) {
3290
3464
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
3291
3465
  }
3292
3466
 
@@ -3753,15 +3927,15 @@ function readWranglerConfig(path) {
3753
3927
  return null;
3754
3928
  }
3755
3929
  }
3756
- function stripJsonComments(text2) {
3930
+ function stripJsonComments(text3) {
3757
3931
  let result = "";
3758
3932
  let inString = false;
3759
- for (let i = 0; i < text2.length; i++) {
3760
- const ch = text2[i];
3933
+ for (let i = 0; i < text3.length; i++) {
3934
+ const ch = text3[i];
3761
3935
  if (inString) {
3762
3936
  result += ch;
3763
3937
  if (ch === "\\") {
3764
- result += text2[i + 1] ?? "";
3938
+ result += text3[i + 1] ?? "";
3765
3939
  i++;
3766
3940
  } else if (ch === '"') {
3767
3941
  inString = false;
@@ -3773,14 +3947,14 @@ function stripJsonComments(text2) {
3773
3947
  result += ch;
3774
3948
  continue;
3775
3949
  }
3776
- if (ch === "/" && text2[i + 1] === "/") {
3777
- while (i < text2.length && text2[i] !== "\n") i++;
3950
+ if (ch === "/" && text3[i + 1] === "/") {
3951
+ while (i < text3.length && text3[i] !== "\n") i++;
3778
3952
  result += "\n";
3779
3953
  continue;
3780
3954
  }
3781
- if (ch === "/" && text2[i + 1] === "*") {
3955
+ if (ch === "/" && text3[i + 1] === "*") {
3782
3956
  i += 2;
3783
- while (i < text2.length && !(text2[i] === "*" && text2[i + 1] === "/")) i++;
3957
+ while (i < text3.length && !(text3[i] === "*" && text3[i + 1] === "/")) i++;
3784
3958
  i++;
3785
3959
  continue;
3786
3960
  }
@@ -3819,7 +3993,7 @@ async function wranglerRuntimeTarget(run, opts) {
3819
3993
  throw new Error(`wrangler is not logged in \u2014 run "wrangler login" (a browser step for the human)`);
3820
3994
  }
3821
3995
  const discovered = [...new Set(`${whoami.stdout}
3822
- ${whoami.stderr}`.match(/\b[a-f0-9]{32}\b/gi)?.map((id) => id.toLowerCase()) ?? [])];
3996
+ ${whoami.stderr}`.match(/\b[a-f0-9]{32}\b/gi)?.map((id2) => id2.toLowerCase()) ?? [])];
3823
3997
  const accountId = configuredAccount.toLowerCase() || (discovered.length === 1 ? discovered[0] : "");
3824
3998
  if (!/^[a-f0-9]{32}$/.test(accountId) || configuredAccount && discovered.length > 0 && !discovered.includes(accountId)) {
3825
3999
  throw new Error("Wrangler account is ambiguous or does not match account_id in the config");
@@ -4294,9 +4468,9 @@ function initProject(options) {
4294
4468
  out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
4295
4469
  out.log("updated .gitignore for local odla credentials");
4296
4470
  }
4297
- function writeIfMissing(path, text2) {
4471
+ function writeIfMissing(path, text3) {
4298
4472
  if (existsSync8(path)) return;
4299
- writeFileSync2(path, text2);
4473
+ writeFileSync2(path, text3);
4300
4474
  }
4301
4475
  function configTemplate(input) {
4302
4476
  const calendar = input.services.includes("calendar") ? ` calendar: {
@@ -4506,13 +4680,13 @@ async function secretsSetClerkKey(options) {
4506
4680
  body: JSON.stringify({ value: value2 })
4507
4681
  });
4508
4682
  if (!res.ok) {
4509
- const text2 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
4510
- throw new Error(`store Clerk secret key failed (${res.status}): ${text2 || "request failed"}`);
4683
+ const text3 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
4684
+ throw new Error(`store Clerk secret key failed (${res.status}): ${text3 || "request failed"}`);
4511
4685
  }
4512
4686
  out.log(`Clerk secret key stored for ${tenantId} ($clerk_secret, reserved + write-only; the value was never echoed)`);
4513
4687
  }
4514
- function scrubValue(text2, value2) {
4515
- return redactSecrets(text2).split(value2).join("[value redacted]");
4688
+ function scrubValue(text3, value2) {
4689
+ return redactSecrets(text3).split(value2).join("[value redacted]");
4516
4690
  }
4517
4691
  async function resolveVaultWrite(options) {
4518
4692
  const out = options.stdout ?? console;
@@ -4607,18 +4781,29 @@ For work that creates an odla app or adds odla services, read and follow
4607
4781
  \`.agents/skills/odla-o11y-debug/SKILL.md\`.
4608
4782
 
4609
4783
  Track the work in odla's PM as you go. Before project-mutating work, run
4610
- \`npx @odla-ai/cli pm next --app <appId>\`, confirm alignment to an open goal,
4784
+ \`npx --yes @odla-ai/cli@latest pm next --app <appId>\`, confirm alignment to an open goal,
4611
4785
  and atomically claim a refined Ready task. Record decisions when you make them
4612
4786
  and file bugs when you notice them. The conventions and the full command set are
4613
4787
  in \`.agents/skills/odla/references/pm.md\`.
4614
4788
 
4615
- Use the human's signed-in odla account email for device authorization; never
4616
- infer it from git config, commit metadata, or GitHub. If authorization is not
4617
- already active, run
4618
- \`npx @odla-ai/cli auth login --app <appId> --email <odla-account>\` and open
4619
- the exact Studio URL it prints. Never file an odla project or product defect in
4620
- GitHub Issues: run \`npx @odla-ai/cli bug report --app <appId> ...\` so the bug
4621
- lands in odla PM with the rest of the project's goals, tasks, and decisions.
4789
+ Use the human's known signed-in odla account email for device authorization;
4790
+ never infer it from git config, commit metadata, or GitHub. If authorization is
4791
+ not already active, run this exact command as one foreground process:
4792
+
4793
+ \`npx --yes @odla-ai/cli@latest auth login --app <appId> --email <odla-account> --no-open --wait 600\`
4794
+
4795
+ When it prints the approval URL and code, immediately give the human a clickable
4796
+ link, name the code they must verify, and tell them to click **Approve**. Keep
4797
+ that CLI process running and wait on the same tool process for its result. The
4798
+ CLI owns protocol polling: never call the OS \`open\` command, use browser
4799
+ control, curl a handshake endpoint, build a shell wait/poll loop, detach the
4800
+ process, or start another handshake while it is alive. The device code exists
4801
+ only in that process. If it exits 75 before approval, the old code cannot be
4802
+ collected; start one fresh foreground command and surface its new URL.
4803
+
4804
+ Never file an odla project or product defect in GitHub Issues: run
4805
+ \`npx --yes @odla-ai/cli@latest bug report --app <appId> ...\` so the bug lands
4806
+ in odla PM with the rest of the project's goals, tasks, and decisions.
4622
4807
 
4623
4808
  The setup runbooks and their references are installed in this repository, pinned
4624
4809
  to this CLI version. Use them as your setup context.
@@ -4630,9 +4815,9 @@ When this repository has an \`appId\`, pass it: app-scoped discovery includes
4630
4815
  that project's instructions plus the shared platform procedures.
4631
4816
 
4632
4817
  \`\`\`
4633
- npx odla-ai runbook ask "<what you are about to do>" --app <appId>
4634
- npx odla-ai runbook list
4635
- npx odla-ai runbook get <slug>
4818
+ npx --yes @odla-ai/cli@latest runbook ask "<what you are about to do>" --app <appId>
4819
+ npx --yes @odla-ai/cli@latest runbook list
4820
+ npx --yes @odla-ai/cli@latest runbook get <slug>
4636
4821
  \`\`\`
4637
4822
 
4638
4823
  Never scrape odla.ai HTML for any of this. The CLI reads the same content
@@ -4646,8 +4831,8 @@ alwaysApply: false
4646
4831
 
4647
4832
  ${PROJECT_INSTRUCTIONS}
4648
4833
  `;
4649
- function claudeAdapter(skill, canonical) {
4650
- const match = canonical.match(/^---\r?\n([\s\S]*?)\r?\n---/);
4834
+ function claudeAdapter(skill, canonical2) {
4835
+ const match = canonical2.match(/^---\r?\n([\s\S]*?)\r?\n---/);
4651
4836
  if (!match) throw new Error(`bundled skill ${skill} has no YAML frontmatter`);
4652
4837
  const lines = match[1].split(/\r?\n/);
4653
4838
  const frontmatter = [];
@@ -4717,8 +4902,8 @@ function installSkill(options = {}) {
4717
4902
  for (const harness of harnesses) rememberTarget(harness, sharedRoot);
4718
4903
  if (harnesses.includes("claude")) {
4719
4904
  for (const skill of skillNames(files)) {
4720
- const canonical = readFileSync8(join9(sourceDir, skill, "SKILL.md"), "utf8");
4721
- plan(join9(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
4905
+ const canonical2 = readFileSync8(join9(sourceDir, skill, "SKILL.md"), "utf8");
4906
+ plan(join9(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
4722
4907
  }
4723
4908
  rememberTarget("claude", claudeRoot);
4724
4909
  }
@@ -4994,7 +5179,7 @@ function assertCalendarHealthy(status, expected) {
4994
5179
  if (!status.writable) throw new Error('calendar grant does not cover booking writes; run "odla-ai calendar connect" to re-consent');
4995
5180
  const hasEventsScope = status.grantedScopes.some((scope) => scope === GOOGLE_CALENDAR_EVENTS_SCOPE);
4996
5181
  if (!hasEventsScope) throw new Error("calendar connection is missing calendar.events consent");
4997
- const missing = expected.availabilityCalendars.filter((id) => !status.calendars.includes(id));
5182
+ const missing = expected.availabilityCalendars.filter((id2) => !status.calendars.includes(id2));
4998
5183
  if (missing.length) throw new Error(`calendar connection is missing configured calendars: ${missing.join(", ")}`);
4999
5184
  }
5000
5185
  async function getJson(doFetch, url, bearer) {
@@ -5394,8 +5579,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
5394
5579
  const maxFiles = options.maxFiles ?? 2e4;
5395
5580
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
5396
5581
  const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
5397
- const entries = inventory.flatMap((record9) => {
5398
- const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record9);
5582
+ const entries = inventory.flatMap((record11) => {
5583
+ const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record11);
5399
5584
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
5400
5585
  });
5401
5586
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
@@ -5643,8 +5828,8 @@ function normalize(value2) {
5643
5828
  if (Array.isArray(value2)) return value2.map(normalize);
5644
5829
  if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
5645
5830
  if (typeof value2 === "object") {
5646
- const record9 = value2;
5647
- return Object.fromEntries(Object.keys(record9).filter((key) => record9[key] !== void 0).sort().map((key) => [key, normalize(record9[key])]));
5831
+ const record11 = value2;
5832
+ return Object.fromEntries(Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, normalize(record11[key])]));
5648
5833
  }
5649
5834
  throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
5650
5835
  }
@@ -5718,7 +5903,7 @@ function copyRef(ref) {
5718
5903
  function normalizeReaders(readers) {
5719
5904
  if (readers.kind === "public") return Object.freeze({ kind: "public" });
5720
5905
  const principalIds = [...new Set(readers.principalIds)].sort();
5721
- if (principalIds.some((id) => !id)) throw new CamelError("reader_mismatch", "Reader principal IDs must be non-empty.");
5906
+ if (principalIds.some((id2) => !id2)) throw new CamelError("reader_mismatch", "Reader principal IDs must be non-empty.");
5722
5907
  return Object.freeze({ kind: "principals", principalIds: Object.freeze(principalIds) });
5723
5908
  }
5724
5909
 
@@ -5728,7 +5913,7 @@ var SHA = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
5728
5913
  var ID = /^[A-Za-z0-9._:-]{1,160}$/;
5729
5914
  async function digestCodeVerificationReceipt(fields) {
5730
5915
  validate(fields);
5731
- const canonical = {
5916
+ const canonical2 = {
5732
5917
  schemaVersion: fields.schemaVersion,
5733
5918
  verificationId: fields.verificationId,
5734
5919
  trustedBaseCommitSha: fields.trustedBaseCommitSha,
@@ -5755,7 +5940,7 @@ async function digestCodeVerificationReceipt(fields) {
5755
5940
  changedTestsRequireReview: fields.changedTestsRequireReview,
5756
5941
  outcome: fields.outcome
5757
5942
  };
5758
- return `sha256:${await sha256Hex(canonicalJson2(canonical))}`;
5943
+ return `sha256:${await sha256Hex(canonicalJson2(canonical2))}`;
5759
5944
  }
5760
5945
  function validate(fields) {
5761
5946
  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) {
@@ -5973,7 +6158,7 @@ async function createConversionRegistry(config) {
5973
6158
  if (await conversionPolicyDigest(definition) !== policy.digest) throw new CamelError("state_conflict", "Conversion policy digest mismatch.");
5974
6159
  if (policy.output.kind === "registered_id") {
5975
6160
  const registry = config.registeredIds?.[policy.output.registryId];
5976
- const validValues = registry && Object.entries(registry.values).every(([candidate, id]) => candidate.length > 0 && typeof id === "string" && id.length > 0);
6161
+ const validValues = registry && Object.entries(registry.values).every(([candidate, id2]) => candidate.length > 0 && typeof id2 === "string" && id2.length > 0);
5977
6162
  if (!registry || !validValues || registry.digest !== policy.output.registryDigest || await registeredIdRegistryDigest(registry.values) !== registry.digest) {
5978
6163
  throw new CamelError("state_conflict", "Registered-ID registry digest mismatch.");
5979
6164
  }
@@ -5981,63 +6166,63 @@ async function createConversionRegistry(config) {
5981
6166
  policies.set(policy.conversionId, Object.freeze(policy));
5982
6167
  }
5983
6168
  const outputCounts = /* @__PURE__ */ new Map();
5984
- const get = (id, kind) => {
5985
- const policy = policies.get(id);
6169
+ const get = (id2, kind) => {
6170
+ const policy = policies.get(id2);
5986
6171
  if (!policy || policy.output.kind !== kind) throw new CamelError("conversion_rejected", "Conversion policy is missing or has the wrong output kind.");
5987
6172
  return policy;
5988
6173
  };
5989
- const checked = (source, id, kind) => {
5990
- const policy = get(id, kind);
6174
+ const checked = (source, id2, kind) => {
6175
+ const policy = get(id2, kind);
5991
6176
  if (utf8Length(source.value) > policy.maximumSourceBytes) throw new CamelError("limit_exceeded", "Unsafe conversion input exceeds its byte bound.");
5992
6177
  return policy;
5993
6178
  };
5994
- const emit3 = (source, policy, value2) => convert(source, policy, value2, outputCounts);
6179
+ const emit4 = (source, policy, value2) => convert(source, policy, value2, outputCounts);
5995
6180
  const operations = Object.freeze({
5996
- boolean: async (value2, id) => {
5997
- const policy = checked(value2, id, "boolean");
5998
- return emit3(value2, policy, requireBoolean(value2.value));
6181
+ boolean: async (value2, id2) => {
6182
+ const policy = checked(value2, id2, "boolean");
6183
+ return emit4(value2, policy, requireBoolean(value2.value));
5999
6184
  },
6000
- integer: async (value2, id) => {
6001
- const policy = checked(value2, id, "integer");
6002
- return emit3(value2, policy, boundedInteger(value2.value, policy.output));
6185
+ integer: async (value2, id2) => {
6186
+ const policy = checked(value2, id2, "integer");
6187
+ return emit4(value2, policy, boundedInteger(value2.value, policy.output));
6003
6188
  },
6004
- finiteNumber: async (value2, id) => {
6005
- const policy = checked(value2, id, "finite_number");
6006
- return emit3(value2, policy, boundedNumber(value2.value, policy.output));
6189
+ finiteNumber: async (value2, id2) => {
6190
+ const policy = checked(value2, id2, "finite_number");
6191
+ return emit4(value2, policy, boundedNumber(value2.value, policy.output));
6007
6192
  },
6008
- enum: async (value2, id) => {
6009
- const policy = checked(value2, id, "enum");
6010
- return emit3(value2, policy, enumMember(value2.value, policy.output));
6193
+ enum: async (value2, id2) => {
6194
+ const policy = checked(value2, id2, "enum");
6195
+ return emit4(value2, policy, enumMember(value2.value, policy.output));
6011
6196
  },
6012
- date: async (value2, id) => {
6013
- const policy = checked(value2, id, "date");
6014
- return emit3(value2, policy, canonicalDate(value2.value, policy.output));
6197
+ date: async (value2, id2) => {
6198
+ const policy = checked(value2, id2, "date");
6199
+ return emit4(value2, policy, canonicalDate(value2.value, policy.output));
6015
6200
  },
6016
- registeredId: async (value2, id) => {
6017
- const policy = checked(value2, id, "registered_id");
6201
+ registeredId: async (value2, id2) => {
6202
+ const policy = checked(value2, id2, "registered_id");
6018
6203
  const registry = config.registeredIds?.[policy.output.registryId];
6019
6204
  const output = typeof value2.value === "string" ? registry?.values[value2.value] : void 0;
6020
6205
  if (!output) throw new CamelError("conversion_rejected", "Registered-ID conversion rejected the candidate.");
6021
- return emit3(value2, policy, output);
6206
+ return emit4(value2, policy, output);
6022
6207
  },
6023
- digest: async (value2, id) => {
6024
- const policy = checked(value2, id, "digest");
6208
+ digest: async (value2, id2) => {
6209
+ const policy = checked(value2, id2, "digest");
6025
6210
  if (!(value2.value instanceof Uint8Array)) throw new CamelError("conversion_rejected", "Digest conversion requires bytes.");
6026
- return emit3(value2, policy, await sha256Hex(value2.value));
6211
+ return emit4(value2, policy, await sha256Hex(value2.value));
6027
6212
  },
6028
- measure: async (value2, metric, id) => {
6029
- const policy = checked(value2, id, "integer");
6213
+ measure: async (value2, metric, id2) => {
6214
+ const policy = checked(value2, id2, "integer");
6030
6215
  const measured = measure(value2.value, metric);
6031
- return emit3(value2, policy, boundedInteger(measured, policy.output));
6216
+ return emit4(value2, policy, boundedInteger(measured, policy.output));
6032
6217
  },
6033
- test: async (value2, predicateId, id) => {
6034
- const policy = checked(value2, id, "boolean");
6218
+ test: async (value2, predicateId, id2) => {
6219
+ const policy = checked(value2, id2, "boolean");
6035
6220
  const predicate = config.predicates?.[predicateId];
6036
6221
  if (!predicate) throw new CamelError("conversion_rejected", "Predicate is not registered.");
6037
- return emit3(value2, policy, evaluatePredicate(value2.value, predicate, config.registeredIds));
6222
+ return emit4(value2, policy, evaluatePredicate(value2.value, predicate, config.registeredIds));
6038
6223
  }
6039
6224
  });
6040
- return Object.freeze({ operations, policy: (id) => policies.get(id) ?? missingPolicy() });
6225
+ return Object.freeze({ operations, policy: (id2) => policies.get(id2) ?? missingPolicy() });
6041
6226
  }
6042
6227
  async function convert(source, policy, value2, counts) {
6043
6228
  const sourceKey = sourceIdentity(source);
@@ -6080,8 +6265,8 @@ function boundedInteger(value2, spec) {
6080
6265
  }
6081
6266
  function boundedNumber(value2, spec) {
6082
6267
  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.");
6083
- const text2 = String(value2);
6084
- 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.");
6268
+ const text3 = String(value2);
6269
+ 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.");
6085
6270
  return value2;
6086
6271
  }
6087
6272
  function enumMember(value2, spec) {
@@ -6132,10 +6317,10 @@ function createCamelIngress(constants2 = []) {
6132
6317
  const ingress = {
6133
6318
  userInstruction: (value2, input) => createSafeInternal(value2, "user_instruction", metadata("user_instruction", input.id, input.readers)),
6134
6319
  systemPolicy: (value2, input) => createSafeInternal(value2, "system_policy", metadata("system_policy", input.id, input.readers)),
6135
- control: (id) => {
6136
- const item = byId.get(id);
6320
+ control: (id2) => {
6321
+ const item = byId.get(id2);
6137
6322
  if (!item) throw new CamelError("permission_denied", "Unknown control constant.");
6138
- return createSafeInternal(item.value, "harness_constant", metadata("harness", id, item.readers));
6323
+ return createSafeInternal(item.value, "harness_constant", metadata("harness", id2, item.readers));
6139
6324
  },
6140
6325
  external: (value2, label) => createUnsafeInternal(value2, label),
6141
6326
  quarantinedOutput: (value2, input) => {
@@ -6159,9 +6344,9 @@ function assertNoUnsafeConstant(value2, seen = /* @__PURE__ */ new WeakSet()) {
6159
6344
  }
6160
6345
  for (const child of Object.values(value2)) assertNoUnsafeConstant(child, seen);
6161
6346
  }
6162
- function metadata(kind, id, readers) {
6163
- if (!id) throw new CamelError("state_conflict", "Provenance IDs must be non-empty.");
6164
- return { readers, provenance: [{ kind, id }] };
6347
+ function metadata(kind, id2, readers) {
6348
+ if (!id2) throw new CamelError("state_conflict", "Provenance IDs must be non-empty.");
6349
+ return { readers, provenance: [{ kind, id: id2 }] };
6165
6350
  }
6166
6351
 
6167
6352
  // ../camel/dist/policy.js
@@ -6225,7 +6410,7 @@ function isControlOwned(value2) {
6225
6410
  return value2.label.safeBasis === "system_policy" || value2.label.safeBasis === "harness_constant";
6226
6411
  }
6227
6412
  function copyRegistries(registries) {
6228
- return Object.freeze(Object.fromEntries(Object.entries(registries).map(([id, registry]) => [id, Object.freeze({ digest: registry.digest, values: Object.freeze([...registry.values]) })])));
6413
+ return Object.freeze(Object.fromEntries(Object.entries(registries).map(([id2, registry]) => [id2, Object.freeze({ digest: registry.digest, values: Object.freeze([...registry.values]) })])));
6229
6414
  }
6230
6415
  function validateUnsafeSelector(path, value2, tool) {
6231
6416
  const policy = tool.unsafeSelectorPolicy;
@@ -6237,8 +6422,8 @@ function validateUnsafeSelector(path, value2, tool) {
6237
6422
  return void 0;
6238
6423
  }
6239
6424
  function looksLikeDestination(value2) {
6240
- const text2 = value2.trim();
6241
- return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text2) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text2);
6425
+ const text3 = value2.trim();
6426
+ return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text3) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text3);
6242
6427
  }
6243
6428
 
6244
6429
  // ../harness/dist/chunk-ANNX7VGK.js
@@ -6248,9 +6433,9 @@ import { join as join33 } from "path";
6248
6433
 
6249
6434
  // ../graph/dist/chunk-PS2SO4UP.js
6250
6435
  var nodeId = (kind, name) => `${kind}:${name}`;
6251
- function parseNodeId(id) {
6252
- const at = id.indexOf(":");
6253
- return at < 0 ? { kind: "", name: id } : { kind: id.slice(0, at), name: id.slice(at + 1) };
6436
+ function parseNodeId(id2) {
6437
+ const at = id2.indexOf(":");
6438
+ return at < 0 ? { kind: "", name: id2 } : { kind: id2.slice(0, at), name: id2.slice(at + 1) };
6254
6439
  }
6255
6440
  var GraphBuilder = class {
6256
6441
  byId = /* @__PURE__ */ new Map();
@@ -6258,14 +6443,14 @@ var GraphBuilder = class {
6258
6443
  seen = /* @__PURE__ */ new Set();
6259
6444
  /** Add or enrich a node. Later attributes win; the kind never changes. */
6260
6445
  node(kind, name, attrs) {
6261
- const id = nodeId(kind, name);
6262
- const existing = this.byId.get(id);
6446
+ const id2 = nodeId(kind, name);
6447
+ const existing = this.byId.get(id2);
6263
6448
  if (existing) {
6264
- if (attrs) this.byId.set(id, { ...existing, attrs: { ...existing.attrs, ...attrs } });
6265
- return id;
6449
+ if (attrs) this.byId.set(id2, { ...existing, attrs: { ...existing.attrs, ...attrs } });
6450
+ return id2;
6266
6451
  }
6267
- this.byId.set(id, { id, kind, name, ...attrs ? { attrs } : {} });
6268
- return id;
6452
+ this.byId.set(id2, { id: id2, kind, name, ...attrs ? { attrs } : {} });
6453
+ return id2;
6269
6454
  }
6270
6455
  /**
6271
6456
  * Add a directed edge, minting either endpoint if it is not known yet.
@@ -6275,10 +6460,10 @@ var GraphBuilder = class {
6275
6460
  * by how often someone repeated an import.
6276
6461
  */
6277
6462
  edge(from, kind, to, attrs) {
6278
- for (const id of [from, to]) {
6279
- if (!this.byId.has(id)) {
6280
- const parsed = parseNodeId(id);
6281
- this.byId.set(id, { id, kind: parsed.kind, name: parsed.name });
6463
+ for (const id2 of [from, to]) {
6464
+ if (!this.byId.has(id2)) {
6465
+ const parsed = parseNodeId(id2);
6466
+ this.byId.set(id2, { id: id2, kind: parsed.kind, name: parsed.name });
6282
6467
  }
6283
6468
  }
6284
6469
  const key = `${from} ${kind} ${to}`;
@@ -6311,18 +6496,18 @@ function nodesOfKind(graph, kind) {
6311
6496
 
6312
6497
  // ../graph/dist/index.js
6313
6498
  var follows = (kinds, edge) => !kinds || kinds.includes(edge.kind);
6314
- function incident(graph, id, traversal = {}) {
6499
+ function incident(graph, id2, traversal = {}) {
6315
6500
  const direction = traversal.direction ?? "out";
6316
- const forward = direction === "out" || direction === "both" ? graph.out.get(id) ?? [] : [];
6317
- const backward = direction === "in" || direction === "both" ? graph.in.get(id) ?? [] : [];
6501
+ const forward = direction === "out" || direction === "both" ? graph.out.get(id2) ?? [] : [];
6502
+ const backward = direction === "in" || direction === "both" ? graph.in.get(id2) ?? [] : [];
6318
6503
  return [...forward, ...backward].filter((edge) => follows(traversal.kinds, edge));
6319
6504
  }
6320
6505
  var otherEnd = (edge, from) => edge.from === from ? edge.to : edge.from;
6321
- function neighbors(graph, id, traversal = {}) {
6506
+ function neighbors(graph, id2, traversal = {}) {
6322
6507
  const seen = /* @__PURE__ */ new Set();
6323
- for (const edge of incident(graph, id, traversal)) {
6324
- const other = otherEnd(edge, id);
6325
- if (other !== id) seen.add(other);
6508
+ for (const edge of incident(graph, id2, traversal)) {
6509
+ const other = otherEnd(edge, id2);
6510
+ if (other !== id2) seen.add(other);
6326
6511
  }
6327
6512
  return [...seen];
6328
6513
  }
@@ -6406,9 +6591,9 @@ async function extractImports(builder, input) {
6406
6591
  const sources = input.paths.filter(isSourcePath);
6407
6592
  const known = new Set(sources);
6408
6593
  for (const path of sources) {
6409
- let text2;
6594
+ let text3;
6410
6595
  try {
6411
- text2 = await input.read(path);
6596
+ text3 = await input.read(path);
6412
6597
  } catch {
6413
6598
  continue;
6414
6599
  }
@@ -6416,13 +6601,13 @@ async function extractImports(builder, input) {
6416
6601
  const file = builder.node(FILE, path, pkg ? { pkg } : void 0);
6417
6602
  if (pkg) builder.edge(builder.node(PACKAGE, pkg), CONTAINS, file);
6418
6603
  const specifiers = /* @__PURE__ */ new Set();
6419
- for (const match of text2.matchAll(IMPORT_FROM)) specifiers.add(match[1]);
6420
- for (const match of text2.matchAll(BARE_IMPORT)) specifiers.add(match[1]);
6604
+ for (const match of text3.matchAll(IMPORT_FROM)) specifiers.add(match[1]);
6605
+ for (const match of text3.matchAll(BARE_IMPORT)) specifiers.add(match[1]);
6421
6606
  for (const specifier of specifiers) {
6422
6607
  const resolved = resolveImport(path, specifier, known);
6423
6608
  if (resolved) builder.edge(file, IMPORTS, nodeId(FILE, resolved));
6424
6609
  }
6425
- for (const name of exportedNames(text2)) {
6610
+ for (const name of exportedNames(text3)) {
6426
6611
  builder.edge(file, EXPORTS, builder.node(SYMBOL, name));
6427
6612
  }
6428
6613
  }
@@ -6463,16 +6648,16 @@ async function extractData(builder, input) {
6463
6648
  };
6464
6649
  for (const path of input.paths) {
6465
6650
  if (!SOURCE_FILE.test(path) || input.ignore?.(path)) continue;
6466
- let text2;
6651
+ let text3;
6467
6652
  try {
6468
- text2 = await input.read(path);
6653
+ text3 = await input.read(path);
6469
6654
  } catch {
6470
6655
  continue;
6471
6656
  }
6472
- for (const statement of text2.matchAll(STATEMENT)) {
6657
+ for (const statement of text3.matchAll(STATEMENT)) {
6473
6658
  const verb = statement[1].toUpperCase().replace(/\s+/g, " ");
6474
6659
  const start = statement.index ?? 0;
6475
- const rest = text2.slice(start + statement[0].length, start + STATEMENT_WINDOW);
6660
+ const rest = text3.slice(start + statement[0].length, start + STATEMENT_WINDOW);
6476
6661
  if (verb === "SELECT") {
6477
6662
  for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
6478
6663
  continue;
@@ -6488,16 +6673,16 @@ async function extractData(builder, input) {
6488
6673
  for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
6489
6674
  }
6490
6675
  }
6491
- for (const match of text2.matchAll(NS_CONST)) {
6492
- touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(text2, match.index ?? 0));
6676
+ for (const match of text3.matchAll(NS_CONST)) {
6677
+ touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(text3, match.index ?? 0));
6493
6678
  }
6494
- for (const match of text2.matchAll(NS_LITERAL)) {
6495
- touch(path, match[1], NAMESPACE, accessFor(text2, match.index ?? 0));
6679
+ for (const match of text3.matchAll(NS_LITERAL)) {
6680
+ touch(path, match[1], NAMESPACE, accessFor(text3, match.index ?? 0));
6496
6681
  }
6497
6682
  }
6498
6683
  }
6499
- function accessFor(text2, index) {
6500
- const window = text2.slice(Math.max(0, index - 160), index + 40);
6684
+ function accessFor(text3, index) {
6685
+ const window = text3.slice(Math.max(0, index - 160), index + 40);
6501
6686
  return /\b(?:transact|update|delete|create|insert|Ops)\b/.test(window) ? WRITES : READS;
6502
6687
  }
6503
6688
  async function buildCodeGraph(input) {
@@ -6628,13 +6813,13 @@ function createCodeRuntimeControlClient(options) {
6628
6813
  if (!Number.isSafeInteger(modelRequestTimeoutMs) || modelRequestTimeoutMs < 3e4 || modelRequestTimeoutMs > 30 * 6e4) {
6629
6814
  throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
6630
6815
  }
6631
- const request2 = options.fetch ?? fetch;
6816
+ const request3 = options.fetch ?? fetch;
6632
6817
  const call2 = async (path, body, timeoutMs = requestTimeoutMs) => {
6633
6818
  const timeout = AbortSignal.timeout(timeoutMs);
6634
6819
  const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
6635
6820
  let response2;
6636
6821
  try {
6637
- response2 = await request2(`${endpoint}${path}`, {
6822
+ response2 = await request3(`${endpoint}${path}`, {
6638
6823
  method: "POST",
6639
6824
  headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
6640
6825
  body: JSON.stringify(body),
@@ -6647,7 +6832,7 @@ function createCodeRuntimeControlClient(options) {
6647
6832
  }
6648
6833
  const value2 = await response2.json().catch(() => null);
6649
6834
  if (!response2.ok) {
6650
- const problem = record4(record4(value2)?.error);
6835
+ const problem = record5(record5(value2)?.error);
6651
6836
  throw new CodeRuntimeControlError(
6652
6837
  typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
6653
6838
  response2.status,
@@ -6669,12 +6854,12 @@ function createCodeRuntimeControlClient(options) {
6669
6854
  await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
6670
6855
  ),
6671
6856
  infer: async (sessionId, inference) => {
6672
- const value2 = record4(await call2(
6857
+ const value2 = record5(await call2(
6673
6858
  `/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
6674
6859
  inference,
6675
6860
  modelRequestTimeoutMs
6676
6861
  ));
6677
- if (!value2 || value2.requestId !== inference.requestId || !record4(value2.response) || !record4(value2.receipt)) {
6862
+ if (!value2 || value2.requestId !== inference.requestId || !record5(value2.response) || !record5(value2.receipt)) {
6678
6863
  throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
6679
6864
  }
6680
6865
  return value2;
@@ -6742,12 +6927,12 @@ function validateHeartbeat(version, capabilities) {
6742
6927
  }
6743
6928
  }
6744
6929
  function parseSnapshot(value2) {
6745
- const root = record4(value2);
6746
- const host = record4(root?.host);
6930
+ const root = record5(value2);
6931
+ const host = record5(root?.host);
6747
6932
  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");
6748
6933
  const bindingIds = /* @__PURE__ */ new Set();
6749
6934
  const bindings = root.bindings.map((item) => {
6750
- const binding = record4(item);
6935
+ const binding = record5(item);
6751
6936
  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)) {
6752
6937
  throw invalid("binding");
6753
6938
  }
@@ -6757,10 +6942,10 @@ function parseSnapshot(value2) {
6757
6942
  const commandIds = /* @__PURE__ */ new Set();
6758
6943
  const commandSequences = /* @__PURE__ */ new Set();
6759
6944
  const commands = root.commands.map((item) => {
6760
- const command = record4(item);
6945
+ const command = record5(item);
6761
6946
  const binding = bindings.find((candidate) => candidate.bindingId === command?.bindingId);
6762
6947
  const sequenceKey = `${String(command?.instanceId)}:${String(command?.sequence)}`;
6763
- 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");
6948
+ 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");
6764
6949
  commandIds.add(command.commandId);
6765
6950
  commandSequences.add(sequenceKey);
6766
6951
  return command;
@@ -6768,10 +6953,10 @@ function parseSnapshot(value2) {
6768
6953
  return { host, bindings, commands };
6769
6954
  }
6770
6955
  async function parseSource(value2) {
6771
- const snapshot = record4(record4(value2)?.snapshot);
6956
+ const snapshot = record5(record5(value2)?.snapshot);
6772
6957
  if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
6773
6958
  const files = snapshot.files.map((value22) => {
6774
- const file = record4(value22);
6959
+ const file = record5(value22);
6775
6960
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
6776
6961
  return { path: file.path, content: file.content };
6777
6962
  });
@@ -6780,11 +6965,11 @@ async function parseSource(value2) {
6780
6965
  const aliases = /* @__PURE__ */ new Set();
6781
6966
  const references = [];
6782
6967
  for (const item of referencesValue) {
6783
- const reference = record4(item);
6968
+ const reference = record5(item);
6784
6969
  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");
6785
6970
  aliases.add(reference.alias);
6786
6971
  const referenceFiles = reference.files.map((entry) => {
6787
- const file = record4(entry);
6972
+ const file = record5(entry);
6788
6973
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
6789
6974
  return { path: file.path, content: file.content };
6790
6975
  });
@@ -6799,18 +6984,18 @@ async function parseSource(value2) {
6799
6984
  return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
6800
6985
  }
6801
6986
  function parseReview(value2) {
6802
- const review = record4(record4(value2)?.review);
6987
+ const review = record5(record5(value2)?.review);
6803
6988
  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");
6804
6989
  return review;
6805
6990
  }
6806
6991
  function parseCandidate(value2) {
6807
- const candidate = record4(record4(value2)?.candidate);
6992
+ const candidate = record5(record5(value2)?.candidate);
6808
6993
  if (!candidate || typeof candidate.candidateId !== "string" || !/^ccand_[0-9a-f]{32}$/.test(candidate.candidateId) || !["submitted", "approved", "published", "failed"].includes(String(candidate.status))) {
6809
6994
  throw invalid("candidate");
6810
6995
  }
6811
6996
  return { candidateId: candidate.candidateId, status: candidate.status };
6812
6997
  }
6813
- var record4 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
6998
+ var record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
6814
6999
  var invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
6815
7000
  var RESERVED = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
6816
7001
  var SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
@@ -6906,8 +7091,8 @@ function gitApply(cwd, patch2, check) {
6906
7091
  });
6907
7092
  let stderr = "";
6908
7093
  child.stderr.setEncoding("utf8");
6909
- child.stderr.on("data", (text2) => {
6910
- if (stderr.length < 4e3) stderr += text2.slice(0, 4e3);
7094
+ child.stderr.on("data", (text3) => {
7095
+ if (stderr.length < 4e3) stderr += text3.slice(0, 4e3);
6911
7096
  });
6912
7097
  child.once("error", reject);
6913
7098
  child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr.trim().slice(0, 500)))));
@@ -7775,7 +7960,7 @@ async function runCodeAgentAttempt(options) {
7775
7960
  }
7776
7961
  }
7777
7962
  async function handleCodeRuntimeInference(input) {
7778
- const { command, metadata: metadata2, request: request2, state: state2 } = input;
7963
+ const { command, metadata: metadata2, request: request3, state: state2 } = input;
7779
7964
  if (state2.tokens >= metadata2.maxTokensPerInteraction) {
7780
7965
  if (!state2.noticeEmitted) {
7781
7966
  state2.noticeEmitted = true;
@@ -7788,7 +7973,7 @@ async function handleCodeRuntimeInference(input) {
7788
7973
  return {
7789
7974
  protocolVersion: HARNESS_PROTOCOL_VERSION,
7790
7975
  type: "inference.response",
7791
- requestId: request2.requestId,
7976
+ requestId: request3.requestId,
7792
7977
  response: {
7793
7978
  id: `budget:${command.commandId}`,
7794
7979
  provider: "openai",
@@ -7802,9 +7987,9 @@ async function handleCodeRuntimeInference(input) {
7802
7987
  }
7803
7988
  const startedAt = Date.now();
7804
7989
  const response2 = await input.control.infer(command.sessionId, {
7805
- requestId: request2.requestId,
7990
+ requestId: request3.requestId,
7806
7991
  interactionId: command.commandId,
7807
- call: request2.call
7992
+ call: request3.call
7808
7993
  });
7809
7994
  state2.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
7810
7995
  await input.event({
@@ -7821,14 +8006,14 @@ async function handleCodeRuntimeInference(input) {
7821
8006
  return {
7822
8007
  protocolVersion: HARNESS_PROTOCOL_VERSION,
7823
8008
  type: "inference.response",
7824
- requestId: request2.requestId,
8009
+ requestId: request3.requestId,
7825
8010
  response: response2.response
7826
8011
  };
7827
8012
  }
7828
8013
  function createCodeRuntimeInference(options) {
7829
8014
  let seq = 0;
7830
8015
  return {
7831
- chat: async (request2) => {
8016
+ chat: async (request3) => {
7832
8017
  const requestId = `${options.command.commandId}:${++seq}`;
7833
8018
  const answer = await handleCodeRuntimeInference({
7834
8019
  command: options.command,
@@ -7840,7 +8025,7 @@ function createCodeRuntimeInference(options) {
7840
8025
  protocolVersion: HARNESS_PROTOCOL_VERSION,
7841
8026
  type: "inference.request",
7842
8027
  requestId,
7843
- call: request2
8028
+ call: request3
7844
8029
  }
7845
8030
  });
7846
8031
  if (answer.type !== "inference.response") throw new TypeError("brokered inference returned the wrong frame");
@@ -8046,9 +8231,9 @@ async function safePrefix(base, paths, prefix) {
8046
8231
  function descriptor(name, effect, argumentRoles) {
8047
8232
  return { name, version: 1, effect, inputSchema: { type: "object" }, argumentRoles, policyId: `odla.code.${name}.v1` };
8048
8233
  }
8049
- async function conversionPolicy(id, output) {
8234
+ async function conversionPolicy(id2, output) {
8050
8235
  const definition = {
8051
- conversionId: id,
8236
+ conversionId: id2,
8052
8237
  version: 1,
8053
8238
  output,
8054
8239
  maximumSourceBytes: 1e6,
@@ -8057,18 +8242,18 @@ async function conversionPolicy(id, output) {
8057
8242
  };
8058
8243
  return { ...definition, digest: await conversionPolicyDigest(definition) };
8059
8244
  }
8060
- async function registeredPolicy(id, registryId, values) {
8245
+ async function registeredPolicy(id2, registryId, values) {
8061
8246
  const mapping = Object.fromEntries(values.map((value2) => [value2, value2]));
8062
- return conversionPolicy(id, {
8247
+ return conversionPolicy(id2, {
8063
8248
  kind: "registered_id",
8064
8249
  registryId,
8065
8250
  registryDigest: await registeredIdRegistryDigest(mapping)
8066
8251
  });
8067
8252
  }
8068
8253
  async function conversionRegistry(policies, values) {
8069
- const registeredIds = Object.fromEntries(await Promise.all(Object.entries(values).map(async ([id, entries]) => {
8254
+ const registeredIds = Object.fromEntries(await Promise.all(Object.entries(values).map(async ([id2, entries]) => {
8070
8255
  const mapping = Object.fromEntries(entries.map((value2) => [value2, value2]));
8071
- return [id, { values: mapping, digest: await registeredIdRegistryDigest(mapping) }];
8256
+ return [id2, { values: mapping, digest: await registeredIdRegistryDigest(mapping) }];
8072
8257
  })));
8073
8258
  return createConversionRegistry({ policies, registeredIds });
8074
8259
  }
@@ -8122,10 +8307,10 @@ function decision(input, policy, approvalConsumed, tool, actionDigest) {
8122
8307
  actionDigest: actionDigest ?? (policy.outcome === "require_approval" ? policy.actionDigest : "")
8123
8308
  };
8124
8309
  }
8125
- function policyContext(context, request2, options, extra) {
8310
+ function policyContext(context, request3, options, extra) {
8126
8311
  return {
8127
8312
  lease: context.lease,
8128
- request: request2,
8313
+ request: request3,
8129
8314
  workspaceId: `workspace:${context.lease.task.attemptId}`,
8130
8315
  readers: { kind: "principals", principalIds: [options.readerId] },
8131
8316
  ...extra
@@ -8144,8 +8329,8 @@ function optionalInteger(value2) {
8144
8329
  if (!Number.isSafeInteger(value2) || value2 < 1) throw new TypeError("line bounds must be positive integers");
8145
8330
  return value2;
8146
8331
  }
8147
- function response(request2, ok, content2, details) {
8148
- return { requestId: request2.requestId, ok, content: content2, ...details ? { details } : {} };
8332
+ function response(request3, ok, content2, details) {
8333
+ return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
8149
8334
  }
8150
8335
  var cache = /* @__PURE__ */ new Map();
8151
8336
  function workspaceGraphs(workspaceDir, paths) {
@@ -8161,7 +8346,7 @@ function workspaceGraphs(workspaceDir, paths) {
8161
8346
  cache.set(workspaceDir, built);
8162
8347
  return built;
8163
8348
  }
8164
- var shortId = (id) => id.slice(id.indexOf(":") + 1);
8349
+ var shortId = (id2) => id2.slice(id2.indexOf(":") + 1);
8165
8350
  function renderOverview(graphs, prefix) {
8166
8351
  const rows = rollup(graphs.graph, FILE, prefix === void 0 ? {} : { prefix });
8167
8352
  if (rows.length === 0) return prefix ? `No source under "${prefix}".` : "No source files.";
@@ -8170,19 +8355,19 @@ function renderOverview(graphs, prefix) {
8170
8355
  return [`${total} source files. Directories, largest first \u2014 read one with sandbox.list --prefix.`, ...lines].join("\n");
8171
8356
  }
8172
8357
  function renderWhereIs(graphs, symbol) {
8173
- const sites = neighbors(graphs.graph, nodeId(SYMBOL, symbol), { direction: "in", kinds: ["exports"] }).map((id) => ({
8174
- path: shortId(id),
8175
- pkg: neighbors(graphs.graph, id, { direction: "in", kinds: ["contains"] })[0],
8176
- dependents: incident(graphs.graph, id, { direction: "in", kinds: [IMPORTS] }).length
8358
+ const sites = neighbors(graphs.graph, nodeId(SYMBOL, symbol), { direction: "in", kinds: ["exports"] }).map((id2) => ({
8359
+ path: shortId(id2),
8360
+ pkg: neighbors(graphs.graph, id2, { direction: "in", kinds: ["contains"] })[0],
8361
+ dependents: incident(graphs.graph, id2, { direction: "in", kinds: [IMPORTS] }).length
8177
8362
  })).sort((left, right) => right.dependents - left.dependents || left.path.localeCompare(right.path));
8178
8363
  if (sites.length === 0) return `No exported symbol named "${symbol}". Try sandbox.search for a textual match.`;
8179
8364
  return sites.slice(0, 20).map((site) => `${site.path}${site.pkg ? ` [${shortId(site.pkg)}]` : ""} ${site.dependents} dependents`).join("\n");
8180
8365
  }
8181
8366
  function renderWhoImports(graphs, path) {
8182
- const id = nodeId(FILE, path);
8183
- const importers = neighbors(graphs.graph, id, { direction: "in", kinds: [IMPORTS] });
8367
+ const id2 = nodeId(FILE, path);
8368
+ const importers = neighbors(graphs.graph, id2, { direction: "in", kinds: [IMPORTS] });
8184
8369
  if (importers.length === 0) {
8185
- return graphs.graph.nodes.has(id) ? `Nothing imports ${path}. It is a leaf.` : `${path} is not a source file in this workspace.`;
8370
+ return graphs.graph.nodes.has(id2) ? `Nothing imports ${path}. It is a leaf.` : `${path} is not a source file in this workspace.`;
8186
8371
  }
8187
8372
  return importers.slice(0, 40).map(shortId).sort().join("\n");
8188
8373
  }
@@ -8205,11 +8390,11 @@ var GRAPH_TOOLS = /* @__PURE__ */ new Set([
8205
8390
  "sandbox.who_imports",
8206
8391
  "sandbox.who_touches"
8207
8392
  ]);
8208
- async function read(context, request2, options, policy) {
8209
- exactKeys(request2.input, ["path", "startLine", "endLine"]);
8210
- const path = stringField(request2.input, "path");
8211
- const startLine = optionalInteger(request2.input.startLine) ?? 1;
8212
- const endLine = optionalInteger(request2.input.endLine) ?? startLine + (options.maxReadLines ?? 2e3) - 1;
8393
+ async function read(context, request3, options, policy) {
8394
+ exactKeys(request3.input, ["path", "startLine", "endLine"]);
8395
+ const path = stringField(request3.input, "path");
8396
+ const startLine = optionalInteger(request3.input.startLine) ?? 1;
8397
+ const endLine = optionalInteger(request3.input.endLine) ?? startLine + (options.maxReadLines ?? 2e3) - 1;
8213
8398
  if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
8214
8399
  throw new TypeError("requested line range exceeds its bound");
8215
8400
  }
@@ -8217,8 +8402,8 @@ async function read(context, request2, options, policy) {
8217
8402
  if (!paths.includes(path)) {
8218
8403
  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.`);
8219
8404
  }
8220
- const allowed = await policy.read(policyContext(context, request2, options, { paths, path, startLine, endLine }));
8221
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
8405
+ const allowed = await policy.read(policyContext(context, request3, options, { paths, path, startLine, endLine }));
8406
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8222
8407
  const target = resolveCodePath(context.workspaceDir, path);
8223
8408
  const info = await stat2(target);
8224
8409
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
@@ -8231,74 +8416,74 @@ async function read(context, request2, options, policy) {
8231
8416
  if (Buffer.byteLength(content2) > (options.maxReadBytes ?? 128 * 1024)) {
8232
8417
  throw new TypeError("read result exceeds its byte bound");
8233
8418
  }
8234
- return response(request2, true, content2, { path, startLine, endLine: Math.min(endLine, lines.length) });
8419
+ return response(request3, true, content2, { path, startLine, endLine: Math.min(endLine, lines.length) });
8235
8420
  }
8236
- async function list(context, request2, options, policy) {
8237
- exactKeys(request2.input, ["prefix", "maxEntries"]);
8238
- const raw = request2.input.prefix;
8421
+ async function list(context, request3, options, policy) {
8422
+ exactKeys(request3.input, ["prefix", "maxEntries"]);
8423
+ const raw = request3.input.prefix;
8239
8424
  const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
8240
- const maxEntries = optionalInteger(request2.input.maxEntries) ?? 1e3;
8425
+ const maxEntries = optionalInteger(request3.input.maxEntries) ?? 1e3;
8241
8426
  if (maxEntries > 5e3) throw new TypeError("maxEntries exceeds its bound");
8242
8427
  const paths = await registeredFiles(context.workspaceDir, 2e4);
8243
- const allowed = await policy.list(policyContext(context, request2, options, { paths, ...prefix ? { prefix } : {} }));
8244
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
8428
+ const allowed = await policy.list(policyContext(context, request3, options, { paths, ...prefix ? { prefix } : {} }));
8429
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8245
8430
  const entries = listWorkspace(paths, { ...prefix ? { prefix } : {}, maxEntries });
8246
8431
  if (!entries.length) {
8247
- return response(request2, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
8432
+ return response(request3, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
8248
8433
  }
8249
8434
  const truncated = entries.length < paths.length && entries.length === maxEntries;
8250
8435
  const hint = !prefix && paths.length > 500 ? `
8251
8436
  \u2026 ${paths.length} files total. sandbox.overview is far cheaper for orientation; use a prefix here once you know the area.` : "";
8252
8437
  return response(
8253
- request2,
8438
+ request3,
8254
8439
  true,
8255
8440
  `${entries.join("\n")}${truncated ? `
8256
8441
  \u2026 truncated at ${maxEntries} entries` : ""}${hint}`,
8257
8442
  { count: entries.length, truncated }
8258
8443
  );
8259
8444
  }
8260
- async function search(context, request2, options, policy) {
8261
- exactKeys(request2.input, ["query", "prefix", "maxResults", "caseSensitive"]);
8262
- const query = stringField(request2.input, "query");
8445
+ async function search(context, request3, options, policy) {
8446
+ exactKeys(request3.input, ["query", "prefix", "maxResults", "caseSensitive"]);
8447
+ const query = stringField(request3.input, "query");
8263
8448
  if (query.length > 512) throw new TypeError("search query exceeds its bound");
8264
- const raw = request2.input.prefix;
8449
+ const raw = request3.input.prefix;
8265
8450
  const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
8266
- const maxResults = optionalInteger(request2.input.maxResults) ?? 100;
8451
+ const maxResults = optionalInteger(request3.input.maxResults) ?? 100;
8267
8452
  if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
8268
- const caseSensitive = request2.input.caseSensitive === void 0 ? true : request2.input.caseSensitive === true;
8453
+ const caseSensitive = request3.input.caseSensitive === void 0 ? true : request3.input.caseSensitive === true;
8269
8454
  const paths = await registeredFiles(context.workspaceDir, 2e4);
8270
- const allowed = await policy.search(policyContext(context, request2, options, { paths, query, ...prefix ? { prefix } : {} }));
8271
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
8455
+ const allowed = await policy.search(policyContext(context, request3, options, { paths, query, ...prefix ? { prefix } : {} }));
8456
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8272
8457
  const matches = await searchWorkspace(context.workspaceDir, paths, {
8273
8458
  query,
8274
8459
  maxResults,
8275
8460
  caseSensitive,
8276
8461
  ...prefix ? { prefix } : {}
8277
8462
  });
8278
- if (!matches.length) return response(request2, true, `No match for "${query}".`, { count: 0 });
8279
- return response(request2, true, matches.map((match) => `${match.path}:${match.line}: ${match.text}`).join("\n"), {
8463
+ if (!matches.length) return response(request3, true, `No match for "${query}".`, { count: 0 });
8464
+ return response(request3, true, matches.map((match) => `${match.path}:${match.line}: ${match.text}`).join("\n"), {
8280
8465
  count: matches.length
8281
8466
  });
8282
8467
  }
8283
- async function graphQuery(context, request2, options, policy) {
8284
- exactKeys(request2.input, ["query"]);
8285
- const raw = request2.input.query;
8468
+ async function graphQuery(context, request3, options, policy) {
8469
+ exactKeys(request3.input, ["query"]);
8470
+ const raw = request3.input.query;
8286
8471
  const query = typeof raw === "string" ? raw : "";
8287
8472
  if (query.length > 512) throw new TypeError("query exceeds its bound");
8288
- const allowed = await policy.graph(policyContext(context, request2, options, {
8289
- tool: request2.tool,
8473
+ const allowed = await policy.graph(policyContext(context, request3, options, {
8474
+ tool: request3.tool,
8290
8475
  selector: query
8291
8476
  }));
8292
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
8477
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8293
8478
  const paths = await registeredFiles(context.workspaceDir, 2e4);
8294
8479
  const graphs = await workspaceGraphs(context.workspaceDir, paths);
8295
- if (request2.tool === "sandbox.overview") {
8296
- return response(request2, true, renderOverview(graphs, query || void 0));
8480
+ if (request3.tool === "sandbox.overview") {
8481
+ return response(request3, true, renderOverview(graphs, query || void 0));
8297
8482
  }
8298
- if (!query) throw new TypeError(`${request2.tool} requires a query`);
8299
- if (request2.tool === "sandbox.where_is") return response(request2, true, renderWhereIs(graphs, query));
8300
- if (request2.tool === "sandbox.who_imports") return response(request2, true, renderWhoImports(graphs, query));
8301
- return response(request2, true, renderWhoTouches(graphs, query));
8483
+ if (!query) throw new TypeError(`${request3.tool} requires a query`);
8484
+ if (request3.tool === "sandbox.where_is") return response(request3, true, renderWhereIs(graphs, query));
8485
+ if (request3.tool === "sandbox.who_imports") return response(request3, true, renderWhoImports(graphs, query));
8486
+ return response(request3, true, renderWhoTouches(graphs, query));
8302
8487
  }
8303
8488
  function createCodeToolBroker(options) {
8304
8489
  validateOptions(options);
@@ -8306,24 +8491,24 @@ function createCodeToolBroker(options) {
8306
8491
  const policy = createCodePolicyGate(options);
8307
8492
  let tail = Promise.resolve();
8308
8493
  return {
8309
- execute(context, request2) {
8310
- const result = tail.then(() => route(context, request2, options, recipes, policy));
8494
+ execute(context, request3) {
8495
+ const result = tail.then(() => route2(context, request3, options, recipes, policy));
8311
8496
  tail = result.then(() => void 0, () => void 0);
8312
8497
  return result;
8313
8498
  }
8314
8499
  };
8315
8500
  }
8316
- async function route(context, request2, options, recipes, policy) {
8501
+ async function route2(context, request3, options, recipes, policy) {
8317
8502
  try {
8318
8503
  if (context.signal?.aborted) throw new TypeError("tool request was cancelled");
8319
- if (request2.tool === "sandbox.read") return await read(context, request2, options, policy);
8320
- if (request2.tool === "sandbox.list") return await list(context, request2, options, policy);
8321
- if (request2.tool === "sandbox.search") return await search(context, request2, options, policy);
8322
- if (GRAPH_TOOLS.has(request2.tool)) return await graphQuery(context, request2, options, policy);
8323
- if (request2.tool === "sandbox.apply_patch") return await patch(context, request2, options, policy);
8324
- return await recipe(context, request2, options, recipes, policy);
8504
+ if (request3.tool === "sandbox.read") return await read(context, request3, options, policy);
8505
+ if (request3.tool === "sandbox.list") return await list(context, request3, options, policy);
8506
+ if (request3.tool === "sandbox.search") return await search(context, request3, options, policy);
8507
+ if (GRAPH_TOOLS.has(request3.tool)) return await graphQuery(context, request3, options, policy);
8508
+ if (request3.tool === "sandbox.apply_patch") return await patch(context, request3, options, policy);
8509
+ return await recipe(context, request3, options, recipes, policy);
8325
8510
  } catch (reason) {
8326
- return response(request2, false, toolFailureMessage(reason));
8511
+ return response(request3, false, toolFailureMessage(reason));
8327
8512
  }
8328
8513
  }
8329
8514
  function toolFailureMessage(reason) {
@@ -8335,34 +8520,34 @@ function toolFailureMessage(reason) {
8335
8520
  if (code === "EACCES" || code === "EPERM") return "that path is not readable through this tool";
8336
8521
  return "tool failed closed";
8337
8522
  }
8338
- async function patch(context, request2, options, policy) {
8339
- exactKeys(request2.input, ["patch"]);
8340
- const value2 = stringField(request2.input, "patch");
8523
+ async function patch(context, request3, options, policy) {
8524
+ exactKeys(request3.input, ["patch"]);
8525
+ const value2 = stringField(request3.input, "patch");
8341
8526
  const paths = validateCodePatch(value2, options.maxPatchBytes ?? 256 * 1024);
8342
8527
  if (paths.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
8343
8528
  throw new TypeError("patch targets a read-only reference source");
8344
8529
  }
8345
- const allowed = await policy.patch(policyContext(context, request2, options, { patch: value2 }));
8346
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
8530
+ const allowed = await policy.patch(policyContext(context, request3, options, { patch: value2 }));
8531
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8347
8532
  await applyCodePatch(context.workspaceDir, value2, paths);
8348
- return response(request2, true, `Applied patch to ${paths.length} file(s).`, { paths });
8533
+ return response(request3, true, `Applied patch to ${paths.length} file(s).`, { paths });
8349
8534
  }
8350
- async function recipe(context, request2, options, recipes, policy) {
8351
- exactKeys(request2.input, ["recipeId"]);
8352
- const recipeId = stringField(request2.input, "recipeId");
8535
+ async function recipe(context, request3, options, recipes, policy) {
8536
+ exactKeys(request3.input, ["recipeId"]);
8537
+ const recipeId = stringField(request3.input, "recipeId");
8353
8538
  const digestLimits = {
8354
8539
  maxFiles: options.maxRecipeWorkspaceFiles ?? 2e4,
8355
8540
  maxBytes: options.maxRecipeWorkspaceBytes ?? 512 * 1024 * 1024
8356
8541
  };
8357
8542
  const sourceDigest = await digestStagedWorkspace(context.workspaceDir, digestLimits);
8358
- const allowed = await policy.recipe(policyContext(context, request2, options, {
8543
+ const allowed = await policy.recipe(policyContext(context, request3, options, {
8359
8544
  recipeIds: [...recipes.keys()].sort(),
8360
8545
  recipeId,
8361
8546
  sourceDigest
8362
8547
  }));
8363
- if (!allowed) return response(request2, false, "tool denied by CaMeL policy");
8548
+ if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
8364
8549
  const selected = recipes.get(recipeId);
8365
- if (!selected) return response(request2, false, "build recipe is not registered");
8550
+ if (!selected) return response(request3, false, "build recipe is not registered");
8366
8551
  const staged = await stageWorkspace(context.workspaceDir, {
8367
8552
  maxFiles: digestLimits.maxFiles,
8368
8553
  maxBytes: digestLimits.maxBytes
@@ -8379,7 +8564,7 @@ async function recipe(context, request2, options, recipes, policy) {
8379
8564
  const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
8380
8565
  const ok = result.exitCode === 0 && !result.outputLimitExceeded && !result.timedOut;
8381
8566
  const status = result.timedOut ? "timed out" : result.outputLimitExceeded ? "exceeded output limit" : ok ? "passed" : `failed with exit ${result.exitCode}`;
8382
- return response(request2, ok, `Recipe ${recipeId} ${status}.${output ? `
8567
+ return response(request3, ok, `Recipe ${recipeId} ${status}.${output ? `
8383
8568
  ${output}` : ""}`, {
8384
8569
  recipeId,
8385
8570
  exitCode: result.exitCode,
@@ -8429,7 +8614,7 @@ async function runGoal(spec, attempt) {
8429
8614
  const startedAt = now();
8430
8615
  const attempts = [];
8431
8616
  const boardErrors = [];
8432
- const emit3 = async (event) => {
8617
+ const emit4 = async (event) => {
8433
8618
  if (!spec.onEvent) return;
8434
8619
  try {
8435
8620
  await spec.onEvent(event);
@@ -8442,7 +8627,7 @@ async function runGoal(spec, attempt) {
8442
8627
  let costKnown = false;
8443
8628
  const finish2 = async (stoppedReason) => {
8444
8629
  const met = stoppedReason === "proof_passed";
8445
- await emit3(met ? { type: "goal_met", attempts: attempts.length, tokens, ...costKnown ? { costUsd } : {} } : {
8630
+ await emit4(met ? { type: "goal_met", attempts: attempts.length, tokens, ...costKnown ? { costUsd } : {} } : {
8446
8631
  type: "goal_abandoned",
8447
8632
  reason: stoppedReason,
8448
8633
  attempts: attempts.length,
@@ -8463,7 +8648,7 @@ async function runGoal(spec, attempt) {
8463
8648
  if (spec.signal?.aborted) return finish2("cancelled");
8464
8649
  if (spec.budget.deadline !== void 0 && now() >= spec.budget.deadline) return finish2("deadline");
8465
8650
  const prompt = index === 1 ? openingPrompt(spec) : retryPrompt(spec, attempts.at(-1));
8466
- await emit3({ type: "attempt_started", attempt: index, prompt });
8651
+ await emit4({ type: "attempt_started", attempt: index, prompt });
8467
8652
  const outcome = await attempt({
8468
8653
  attempt: index,
8469
8654
  prompt,
@@ -8483,7 +8668,7 @@ async function runGoal(spec, attempt) {
8483
8668
  ...outcome.error === void 0 ? {} : { error: outcome.error }
8484
8669
  });
8485
8670
  if (outcome.gatePassed) return finish2("proof_passed");
8486
- await emit3({
8671
+ await emit4({
8487
8672
  type: "attempt_failed",
8488
8673
  attempt: index,
8489
8674
  feedback: outcome.feedback,
@@ -8532,7 +8717,7 @@ function createCodeRuntimeToolBroker(input, lease, role) {
8532
8717
  readerId: `code-session:${lease.task.taskId}`,
8533
8718
  readOnlyPrefixes: [".odla-references"]
8534
8719
  });
8535
- 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" }) };
8720
+ 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" }) };
8536
8721
  }
8537
8722
  var POSITIVE = (value2) => Number.isFinite(value2) && Number(value2) > 0 ? Number(value2) : void 0;
8538
8723
  function codeGoalSpec(payload) {
@@ -8920,18 +9105,18 @@ var CodePiRuntimeEngine = class {
8920
9105
  /** Report every brokered effect as it starts and finishes. */
8921
9106
  #observed(command, active, broker) {
8922
9107
  return {
8923
- execute: async (context, request2) => {
9108
+ execute: async (context, request3) => {
8924
9109
  const startedAt = Date.now();
8925
9110
  await this.#event(
8926
9111
  command,
8927
- { type: "tool", phase: "started", tool: request2.tool },
9112
+ { type: "tool", phase: "started", tool: request3.tool },
8928
9113
  active.conversationRefs
8929
9114
  ).catch(() => void 0);
8930
- const response2 = await broker.execute(context, request2);
9115
+ const response2 = await broker.execute(context, request3);
8931
9116
  await this.#event(command, {
8932
9117
  type: "tool",
8933
9118
  phase: "completed",
8934
- tool: request2.tool,
9119
+ tool: request3.tool,
8935
9120
  ok: response2.ok,
8936
9121
  durationMs: Date.now() - startedAt
8937
9122
  }, active.conversationRefs).catch(() => void 0);
@@ -9071,7 +9256,7 @@ async function waitForHostedPoll(milliseconds, signal) {
9071
9256
  }
9072
9257
  function isValidHostedSecurityPlan(value2, env) {
9073
9258
  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;
9074
- 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;
9259
+ 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;
9075
9260
  return validRoute(value2.routes?.discovery, "security.discovery") && validRoute(value2.routes?.validation, "security.validation");
9076
9261
  }
9077
9262
  function hostedSecurityCredential(value2) {
@@ -9420,20 +9605,20 @@ async function runCodeRuntime(input) {
9420
9605
  }
9421
9606
  }
9422
9607
  function parseConnection(value2, appId, appEnv) {
9423
- const root = record5(value2);
9424
- const host = record5(root?.host);
9425
- const offer = record5(root?.offer);
9426
- const binding = record5(root?.binding);
9608
+ const root = record6(value2);
9609
+ const host = record6(root?.host);
9610
+ const offer = record6(root?.offer);
9611
+ const binding = record6(root?.binding);
9427
9612
  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)) {
9428
9613
  throw new Error("connect Code host returned an invalid response");
9429
9614
  }
9430
9615
  return root;
9431
9616
  }
9432
9617
  function apiFailure(action2, status, value2) {
9433
- const message2 = record5(record5(value2)?.error)?.message;
9618
+ const message2 = record6(record6(value2)?.error)?.message;
9434
9619
  return `${action2} failed (${status})${typeof message2 === "string" ? `: ${message2}` : ""}`;
9435
9620
  }
9436
- function record5(value2) {
9621
+ function record6(value2) {
9437
9622
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
9438
9623
  }
9439
9624
 
@@ -9652,9 +9837,9 @@ async function credentialCommand(parsed, deps = {}) {
9652
9837
  }, doFetch, out, { optionalProjectCapabilities: ["app.manage"] });
9653
9838
  const base = `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials`;
9654
9839
  if (action2 === "revoke") {
9655
- const id = parsed.positionals[2];
9656
- if (!id) throw new Error("credentials revoke requires the exact receipt id from credentials list");
9657
- const response3 = await doFetch(`${base}/${encodeURIComponent(id)}`, {
9840
+ const id2 = parsed.positionals[2];
9841
+ if (!id2) throw new Error("credentials revoke requires the exact receipt id from credentials list");
9842
+ const response3 = await doFetch(`${base}/${encodeURIComponent(id2)}`, {
9658
9843
  method: "DELETE",
9659
9844
  headers: { authorization: `Bearer ${token}` }
9660
9845
  });
@@ -9762,6 +9947,12 @@ Usage:
9762
9947
  odla-ai context save <name> [--platform <url>] [--app <id>] [--env <name>] [--json]
9763
9948
  odla-ai context remove <name> --yes [--json]
9764
9949
  odla-ai o11y status [--app <id>] [--context <name>] [--platform https://odla.ai] [--env prod] [--minutes 60] [--json]
9950
+ odla-ai monitor plan [--config odla.config.mjs] [--env prod] [--json]
9951
+ odla-ai monitor apply [--config odla.config.mjs] [--env prod] [--json] [--yes]
9952
+ odla-ai monitor run <probe-id> [--app <id>] [--env prod] [--json]
9953
+ odla-ai monitor status [--app <id>] [--context <name>] [--env prod] [--json]
9954
+ odla-ai monitor incidents [--app <id>] [--env prod] [--limit 100] [--runs] [--json]
9955
+ odla-ai monitor report [--app <id>] [--env prod] [--period daily|weekly] [--json]
9765
9956
  odla-ai platform status [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
9766
9957
  odla-ai whoami [--context <name>] [--platform https://odla.ai] [--json]
9767
9958
  odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
@@ -9781,8 +9972,8 @@ Usage:
9781
9972
  odla-ai runbook revert <slug> --version <n> [--app <id>]
9782
9973
  odla-ai runbook rm <slug> [--app <id>]
9783
9974
  odla-ai capabilities [--json]
9784
- odla-ai code connect [--env dev|prod] [--email <odla-account>] [--engine auto|container|podman|docker] [--slots <1-64>] [--once]
9785
- odla-ai admin ai show [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
9975
+ odla-ai code connect [--env dev|prod] [--email <odla-account>] [--no-open] [--engine auto|container|podman|docker] [--slots <1-64>] [--once]
9976
+ odla-ai admin ai show [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--no-open] [--json]
9786
9977
  odla-ai admin ai models [--context <name>] [--provider <id>] [--json]
9787
9978
  odla-ai admin ai set <purpose> [--context <name>] [--provider <id>] [--model <id>] [--enabled|--no-enabled]
9788
9979
  [--max-input-bytes <n>] [--max-output-tokens <n>] [--max-calls-per-run <n>] [--json]
@@ -9801,7 +9992,7 @@ Usage:
9801
9992
  odla-ai security report <job-id> [--json]
9802
9993
  odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
9803
9994
  odla-ai security run [target] --self --ack-redacted-source
9804
- odla-ai provision [--live] [--config odla.config.mjs] [--email <odla-account>] [--request-grant] [--wait <seconds>] [--dry-run] [--push-secrets] [--rotate-o11y-token] [--write-dev-vars[=path]] [--yes]
9995
+ odla-ai provision [--live] [--config odla.config.mjs] [--email <odla-account>] [--request-grant] [--no-open] [--wait <seconds>] [--dry-run] [--push-secrets] [--rotate-o11y-token] [--write-dev-vars[=path]] [--yes]
9805
9996
  odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
9806
9997
  odla-ai credentials revoke <receipt-id> [--config odla.config.mjs] [--json]
9807
9998
  odla-ai smoke [--config odla.config.mjs] [--env dev] [--runtime] [--email <odla-account>] [--no-open]
@@ -9816,7 +10007,7 @@ function printHelp(output = console) {
9816
10007
  output.log(`odla-ai
9817
10008
  ${USAGE_SECTION}
9818
10009
  Commands:
9819
- auth Start a fresh, exact-project agent authorization in the browser.
10010
+ auth Start a fresh, exact-project agent authorization for human review.
9820
10011
  The email is the signed-in odla account, never git or GitHub
9821
10012
  identity. The approval screen confirms the agent name first.
9822
10013
  agent Inspect durable agent wakeups and explicitly requeue a
@@ -9899,6 +10090,9 @@ Commands:
9899
10090
  canary, collector ingest/scheduler trust, Cloudflare-owned
9900
10091
  runtime metrics, and a machine verdict.
9901
10092
  --json keeps auth progress on stderr for unattended agents.
10093
+ monitor Reconcile checked-in Kitesurf routes, rolling SLOs, spike/trend
10094
+ policies, and email digests; run probes manually and expose
10095
+ stable status, incident, and report JSON to agents and CI.
9902
10096
  platform Read canonical fleet health, releases, provider load/freshness,
9903
10097
  explicit unknowns, and next actions through a read-only grant.
9904
10098
  provision Register services, compose integrations, persist credentials, optionally push secrets.
@@ -9943,23 +10137,25 @@ Safety:
9943
10137
  the metadata file. Flags and specific ODLA_* scope variables beat a selected
9944
10138
  context, which beats project config. There is no ambient current context.
9945
10139
  "context show" reports only provenance and cache state and never authenticates.
9946
- Every real CLI handshake prints one canonical /studio?code= approval URL and
9947
- attempts to open it \u2014 including in CI, SSH, display-less, and agent-driven
9948
- shells. Only --no-open suppresses the attempt. Browser launch is best-effort:
9949
- agents with browser control must open that exact URL immediately; otherwise
9950
- they must give it to the human verbatim. A device code remains only in the
9951
- running process. Outside an interactive terminal the wait is capped (90s by
9952
- default, --wait <seconds> to change); a still-pending handshake then exits
9953
- with code 75. Rerunning always requests and opens a new code; older clients'
9954
- persisted pending state is discarded.
10140
+ Every real CLI handshake prints one canonical /studio?code= approval URL.
10141
+ Interactive humans may let the CLI attempt its best-effort browser launch.
10142
+ Agent-driven commands must pass --no-open --wait 600, immediately surface the
10143
+ exact URL and code as a clickable human approval action, and keep that CLI
10144
+ process alive. Wait only on that same process: the CLI owns protocol polling.
10145
+ Do not call OS open, use browser control, curl handshake endpoints, build a
10146
+ shell wait loop, detach the command, or start a substitute handshake. The
10147
+ device code remains only in the running process. If the process exits 75, its
10148
+ old code cannot be collected; a later invocation requests a new code. Older
10149
+ clients' persisted pending state is discarded.
9955
10150
  A fresh device handshake requires --email <odla-account> or ODLA_USER_EMAIL.
9956
10151
  The email is a non-secret identity hint: never provide a password or session
9957
10152
  token. It is the email shown by the signed-in odla account \u2014 never infer it
9958
10153
  from git config, a commit author, or GitHub. The matching account must already exist, be signed in, explicitly
9959
10154
  review the exact code, and finish any current request before claiming another.
9960
- Use "auth login --app <id> --email <odla-account>" when an outside agent needs
9961
- a deliberate fresh request; it ignores cached credentials and opens the same
9962
- focused authorization sequence used by every first-time command.
10155
+ Use "auth login --app <id> --email <odla-account> --no-open --wait 600" when
10156
+ an outside agent needs a deliberate fresh request; it ignores cached
10157
+ credentials and uses the same focused authorization sequence as every
10158
+ first-time command.
9963
10159
  If provision reports that the current agent principal has no live app.manage
9964
10160
  grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
9965
10161
  the local cache, prints and opens a fresh exact-project owner-review URL, then
@@ -10102,7 +10298,7 @@ async function discussList(ctx, parsed) {
10102
10298
  }
10103
10299
  });
10104
10300
  }
10105
- async function discussRead(ctx, id, parsed) {
10301
+ async function discussRead(ctx, id2, parsed) {
10106
10302
  const requestedLimit = stringOpt(parsed.options.limit);
10107
10303
  const requestedOffset = stringOpt(parsed.options.offset);
10108
10304
  if (requestedLimit !== void 0 || requestedOffset !== void 0) {
@@ -10110,7 +10306,7 @@ async function discussRead(ctx, id, parsed) {
10110
10306
  limit: requestedLimit ?? "200",
10111
10307
  offset: requestedOffset ?? "0"
10112
10308
  });
10113
- const page2 = await request(ctx, "GET", `/topics/${encodeURIComponent(id)}?${query}`);
10309
+ const page2 = await request(ctx, "GET", `/topics/${encodeURIComponent(id2)}?${query}`);
10114
10310
  emit(
10115
10311
  ctx,
10116
10312
  page2,
@@ -10130,7 +10326,7 @@ async function discussRead(ctx, id, parsed) {
10130
10326
  const page2 = await request(
10131
10327
  ctx,
10132
10328
  "GET",
10133
- `/topics/${encodeURIComponent(id)}?limit=200&offset=${offset}`
10329
+ `/topics/${encodeURIComponent(id2)}?limit=200&offset=${offset}`
10134
10330
  );
10135
10331
  topic = page2.topic;
10136
10332
  for (const post of page2.posts) posts.set(post.id, post);
@@ -10172,20 +10368,20 @@ async function discussPost(ctx, parsed) {
10172
10368
  });
10173
10369
  emit(ctx, created, () => ctx.out.log(`opened topic ${created.id}`));
10174
10370
  }
10175
- async function discussReply(ctx, id, parsed) {
10371
+ async function discussReply(ctx, id2, parsed) {
10176
10372
  const created = await request(
10177
10373
  ctx,
10178
10374
  "POST",
10179
- `/topics/${encodeURIComponent(id)}/replies`,
10375
+ `/topics/${encodeURIComponent(id2)}/replies`,
10180
10376
  { ...content(parsed), mutationId: writeMutationId(parsed) }
10181
10377
  );
10182
10378
  emit(ctx, created, () => ctx.out.log(`replied ${created.id}`));
10183
10379
  }
10184
- async function discussResolve(ctx, id, resolved, parsed) {
10380
+ async function discussResolve(ctx, id2, resolved, parsed) {
10185
10381
  const result = await request(
10186
10382
  ctx,
10187
10383
  "PATCH",
10188
- `/topics/${encodeURIComponent(id)}`,
10384
+ `/topics/${encodeURIComponent(id2)}`,
10189
10385
  { resolved, mutationId: writeMutationId(parsed) }
10190
10386
  );
10191
10387
  emit(ctx, result, () => ctx.out.log(`${resolved ? "resolved" : "reopened"} ${result.id}`));
@@ -10459,9 +10655,9 @@ var ALLOWED = [
10459
10655
  "context",
10460
10656
  "open"
10461
10657
  ];
10462
- function requireId(id, action2) {
10463
- if (!id) throw new Error(`"discuss ${action2}" needs a topic id`);
10464
- return id;
10658
+ function requireId(id2, action2) {
10659
+ if (!id2) throw new Error(`"discuss ${action2}" needs a topic id`);
10660
+ return id2;
10465
10661
  }
10466
10662
  async function buildContext(parsed, deps) {
10467
10663
  const context = await resolveOperatorContext(parsed, {
@@ -10496,7 +10692,7 @@ async function buildContext(parsed, deps) {
10496
10692
  async function discussCommand(parsed, deps = {}) {
10497
10693
  assertArgs(parsed, ALLOWED, 3);
10498
10694
  const action2 = parsed.positionals[1];
10499
- const id = parsed.positionals[2];
10695
+ const id2 = parsed.positionals[2];
10500
10696
  if (!action2) throw new Error('"discuss" needs an action. Run "odla-ai help".');
10501
10697
  const ctx = await buildContext(parsed, deps);
10502
10698
  switch (action2) {
@@ -10506,17 +10702,17 @@ async function discussCommand(parsed, deps = {}) {
10506
10702
  case "topics":
10507
10703
  return discussList(ctx, parsed);
10508
10704
  case "read":
10509
- return discussRead(ctx, requireId(id, "read"), parsed);
10705
+ return discussRead(ctx, requireId(id2, "read"), parsed);
10510
10706
  case "post":
10511
10707
  return discussPost(ctx, parsed);
10512
10708
  case "reply":
10513
- return discussReply(ctx, requireId(id, "reply"), parsed);
10709
+ return discussReply(ctx, requireId(id2, "reply"), parsed);
10514
10710
  case "resolve":
10515
- return discussResolve(ctx, requireId(id, "resolve"), parsed.options.reopen !== true, parsed);
10711
+ return discussResolve(ctx, requireId(id2, "resolve"), parsed.options.reopen !== true, parsed);
10516
10712
  case "who":
10517
10713
  return discussWho(ctx, parsed);
10518
10714
  case "watch": {
10519
- const result = await discussWatch(ctx, id, parsed);
10715
+ const result = await discussWatch(ctx, id2, parsed);
10520
10716
  if (!result.found) throw new WatchTimeoutError(result.cursor);
10521
10717
  return;
10522
10718
  }
@@ -10587,8 +10783,8 @@ function collectFields(parsed, allowClear) {
10587
10783
  if (allowClear) out[spec.key] = null;
10588
10784
  continue;
10589
10785
  }
10590
- const text2 = stringOpt(value2);
10591
- out[spec.key] = spec.num ? Number(text2) : text2;
10786
+ const text3 = stringOpt(value2);
10787
+ out[spec.key] = spec.num ? Number(text3) : text3;
10592
10788
  }
10593
10789
  return out;
10594
10790
  }
@@ -10601,17 +10797,17 @@ function collectEntityFields(entity, parsed, allowClear) {
10601
10797
  if (entity === "task" && fields.column === "ready") fields.column = "todo";
10602
10798
  return fields;
10603
10799
  }
10604
- function statusCol(entity, record9) {
10605
- if (entity === "bug") return `${record9.status ?? ""}/${record9.severity ?? ""}`;
10800
+ function statusCol(entity, record11) {
10801
+ if (entity === "bug") return `${record11.status ?? ""}/${record11.severity ?? ""}`;
10606
10802
  if (entity === "task") {
10607
- const state2 = record9.column === "todo" ? "ready" : String(record9.column ?? "");
10608
- return record9.revision ? `${state2}; r${record9.revision}` : state2;
10803
+ const state2 = record11.column === "todo" ? "ready" : String(record11.column ?? "");
10804
+ return record11.revision ? `${state2}; r${record11.revision}` : state2;
10609
10805
  }
10610
- return String(record9.status ?? "");
10806
+ return String(record11.status ?? "");
10611
10807
  }
10612
- function referenceMarkup(entity, record9) {
10613
- const label = (record9.title?.trim() || `${entity} ${record9.id}`).replaceAll("]", ")");
10614
- return `@[${label}](pm:${entity}/${record9.id})`;
10808
+ function referenceMarkup(entity, record11) {
10809
+ const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
10810
+ return `@[${label}](pm:${entity}/${record11.id})`;
10615
10811
  }
10616
10812
  var STUDIO_SECTION = {
10617
10813
  goal: "goals",
@@ -10619,19 +10815,19 @@ var STUDIO_SECTION = {
10619
10815
  decision: "decisions",
10620
10816
  bug: "bugs"
10621
10817
  };
10622
- function studioRecordUrl(ctx, entity, id) {
10818
+ function studioRecordUrl(ctx, entity, id2) {
10623
10819
  return new URL(
10624
- `/studio/pm/${STUDIO_SECTION[entity]}/${encodeURIComponent(id)}`,
10820
+ `/studio/pm/${STUDIO_SECTION[entity]}/${encodeURIComponent(id2)}`,
10625
10821
  ctx.platformUrl
10626
10822
  ).href;
10627
10823
  }
10628
- function studioRecordLink(ctx, entity, record9) {
10629
- const label = (record9.title?.trim() || `${entity} ${record9.id}`).replaceAll("]", ")");
10630
- return `[${label}](${studioRecordUrl(ctx, entity, record9.id)})`;
10824
+ function studioRecordLink(ctx, entity, record11) {
10825
+ const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
10826
+ return `[${label}](${studioRecordUrl(ctx, entity, record11.id)})`;
10631
10827
  }
10632
- function printRecord(ctx, entity, record9) {
10828
+ function printRecord(ctx, entity, record11) {
10633
10829
  ctx.out.log(
10634
- `${record9.id} [${statusCol(entity, record9)}] ${record9.appId} ${studioRecordLink(ctx, entity, record9)}`
10830
+ `${record11.id} [${statusCol(entity, record11)}] ${record11.appId} ${studioRecordLink(ctx, entity, record11)}`
10635
10831
  );
10636
10832
  }
10637
10833
  function emit2(ctx, value2, human) {
@@ -10685,52 +10881,52 @@ async function pmAdd(ctx, entity, parsed) {
10685
10881
  input,
10686
10882
  mutationId: writeMutationId2(parsed)
10687
10883
  });
10688
- const record9 = { id: res.id, appId, title: String(input.title) };
10689
- emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record9)}`));
10884
+ const record11 = { id: res.id, appId, title: String(input.title) };
10885
+ emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record11)}`));
10690
10886
  }
10691
- async function pmGet(ctx, entity, id) {
10692
- const { record: record9 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
10693
- emit2(ctx, record9, () => printRecord(ctx, entity, record9));
10887
+ async function pmGet(ctx, entity, id2) {
10888
+ const { record: record11 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id2)}`);
10889
+ emit2(ctx, record11, () => printRecord(ctx, entity, record11));
10694
10890
  }
10695
- async function pmReference(ctx, entity, id) {
10696
- const { record: record9 } = await pmRequest(
10891
+ async function pmReference(ctx, entity, id2) {
10892
+ const { record: record11 } = await pmRequest(
10697
10893
  ctx,
10698
10894
  "GET",
10699
- `/${entity}/${encodeURIComponent(id)}`
10895
+ `/${entity}/${encodeURIComponent(id2)}`
10700
10896
  );
10701
- const markup = referenceMarkup(entity, record9);
10702
- emit2(ctx, { kind: `pm:${entity}`, id: record9.id, label: record9.title ?? "", markup }, () => {
10897
+ const markup = referenceMarkup(entity, record11);
10898
+ emit2(ctx, { kind: `pm:${entity}`, id: record11.id, label: record11.title ?? "", markup }, () => {
10703
10899
  ctx.out.log(markup);
10704
10900
  });
10705
10901
  }
10706
- async function pmSet(ctx, entity, id, parsed) {
10902
+ async function pmSet(ctx, entity, id2, parsed) {
10707
10903
  const patch2 = collectEntityFields(entity, parsed, true);
10708
10904
  if (Object.keys(patch2).length === 0)
10709
10905
  throw new Error("pm set needs at least one field flag (e.g. --status doing, --assignee me, --no-assignee)");
10710
- const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
10906
+ const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id2)}`, {
10711
10907
  patch: patch2,
10712
10908
  mutationId: writeMutationId2(parsed)
10713
10909
  });
10714
10910
  emit2(ctx, res, () => {
10715
- if (!res.record) return ctx.out.log(`updated ${entity} ${id}`);
10911
+ if (!res.record) return ctx.out.log(`updated ${entity} ${id2}`);
10716
10912
  ctx.out.log(`${entity}: ${studioRecordLink(ctx, entity, res.record)} \u2192 ${statusCol(entity, res.record)}`);
10717
10913
  });
10718
10914
  }
10719
- async function pmDone(ctx, entity, id, parsed) {
10915
+ async function pmDone(ctx, entity, id2, parsed) {
10720
10916
  const decisionId = stringOpt(parsed.options.decision);
10721
10917
  if (decisionId && entity !== "bug") throw new Error("--decision is only valid when completing a bug");
10722
10918
  const patch2 = { ...DONE[entity], ...decisionId ? { decisionId } : {} };
10723
- const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
10919
+ const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id2)}`, {
10724
10920
  patch: patch2,
10725
10921
  mutationId: writeMutationId2(parsed)
10726
10922
  });
10727
10923
  emit2(ctx, res, () => {
10728
- const label = res.record ? studioRecordLink(ctx, entity, res.record) : id;
10924
+ const label = res.record ? studioRecordLink(ctx, entity, res.record) : id2;
10729
10925
  const state2 = res.record ? statusCol(entity, res.record) : "done";
10730
10926
  ctx.out.log(`${entity}: ${label} \u2192 ${state2}`);
10731
10927
  });
10732
10928
  }
10733
- async function pmTaskLifecycle(ctx, id, action2, parsed) {
10929
+ async function pmTaskLifecycle(ctx, id2, action2, parsed) {
10734
10930
  const rawRevision = stringOpt(parsed.options["expected-revision"]);
10735
10931
  const expectedRevision = Number(rawRevision);
10736
10932
  if (!rawRevision || !Number.isSafeInteger(expectedRevision) || expectedRevision < 1) {
@@ -10740,7 +10936,7 @@ async function pmTaskLifecycle(ctx, id, action2, parsed) {
10740
10936
  const res = action2 === "ready" ? await pmRequest(
10741
10937
  ctx,
10742
10938
  "PATCH",
10743
- `/task/${encodeURIComponent(id)}`,
10939
+ `/task/${encodeURIComponent(id2)}`,
10744
10940
  {
10745
10941
  patch: {
10746
10942
  ...collectEntityFields("task", parsed, true),
@@ -10752,12 +10948,12 @@ async function pmTaskLifecycle(ctx, id, action2, parsed) {
10752
10948
  ) : await pmRequest(
10753
10949
  ctx,
10754
10950
  "POST",
10755
- `/task/${encodeURIComponent(id)}/${action2}`,
10951
+ `/task/${encodeURIComponent(id2)}/${action2}`,
10756
10952
  { expectedRevision, mutationId }
10757
10953
  );
10758
10954
  emit2(ctx, res, () => {
10759
10955
  const state2 = res.record ? statusCol("task", res.record) : action2;
10760
- const label = res.record ? studioRecordLink(ctx, "task", res.record) : id;
10956
+ const label = res.record ? studioRecordLink(ctx, "task", res.record) : id2;
10761
10957
  ctx.out.log(`task: ${label} \u2192 ${state2}`);
10762
10958
  });
10763
10959
  }
@@ -10786,9 +10982,9 @@ async function pmNext(ctx, parsed) {
10786
10982
  const result = {
10787
10983
  appId,
10788
10984
  projectId,
10789
- openGoals: goals.filter((record9) => record9.status === "open"),
10790
- doing: tasks.filter((record9) => record9.column === "doing"),
10791
- ready: tasks.filter((record9) => record9.column === "todo")
10985
+ openGoals: goals.filter((record11) => record11.status === "open"),
10986
+ doing: tasks.filter((record11) => record11.column === "doing"),
10987
+ ready: tasks.filter((record11) => record11.column === "todo")
10792
10988
  };
10793
10989
  emit2(ctx, result, () => {
10794
10990
  ctx.out.log(`${appId}: goal-aligned work intake (read only)`);
@@ -10799,10 +10995,10 @@ async function pmNext(ctx, parsed) {
10799
10995
  ]) {
10800
10996
  ctx.out.log(`${label}:`);
10801
10997
  if (!records.length) ctx.out.log("- (none)");
10802
- else for (const record9 of records) printRecord(
10998
+ else for (const record11 of records) printRecord(
10803
10999
  ctx,
10804
11000
  label === "open goals" ? "goal" : "task",
10805
- record9
11001
+ record11
10806
11002
  );
10807
11003
  }
10808
11004
  if (!result.openGoals.length) {
@@ -10826,9 +11022,9 @@ async function pmHandoff(ctx, parsed) {
10826
11022
  const handoff = {
10827
11023
  appId,
10828
11024
  projectId,
10829
- unmetGoals: goals.filter((record9) => record9.status !== "met"),
10830
- activeTasks: tasks.filter((record9) => record9.column !== "done"),
10831
- openBugs: bugs.filter((record9) => record9.status !== "fixed" && record9.status !== "wontfix")
11025
+ unmetGoals: goals.filter((record11) => record11.status !== "met"),
11026
+ activeTasks: tasks.filter((record11) => record11.column !== "done"),
11027
+ openBugs: bugs.filter((record11) => record11.status !== "fixed" && record11.status !== "wontfix")
10832
11028
  };
10833
11029
  const result = {
10834
11030
  ...handoff,
@@ -10847,45 +11043,45 @@ async function pmHandoff(ctx, parsed) {
10847
11043
  ]) {
10848
11044
  ctx.out.log(`${label}:`);
10849
11045
  if (!records.length) ctx.out.log("- (none)");
10850
- else for (const record9 of records) printRecord(
11046
+ else for (const record11 of records) printRecord(
10851
11047
  ctx,
10852
11048
  label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
10853
- record9
11049
+ record11
10854
11050
  );
10855
11051
  }
10856
11052
  });
10857
11053
  }
10858
- async function pmRemove(ctx, entity, id) {
10859
- await pmRequest(ctx, "DELETE", `/${entity}/${encodeURIComponent(id)}`);
10860
- ctx.out.log(`deleted ${entity} ${id}`);
11054
+ async function pmRemove(ctx, entity, id2) {
11055
+ await pmRequest(ctx, "DELETE", `/${entity}/${encodeURIComponent(id2)}`);
11056
+ ctx.out.log(`deleted ${entity} ${id2}`);
10861
11057
  }
10862
11058
 
10863
11059
  // src/pm-links.ts
10864
- async function pmLink(ctx, entity, id) {
10865
- const { record: record9 } = await pmRequest(
11060
+ async function pmLink(ctx, entity, id2) {
11061
+ const { record: record11 } = await pmRequest(
10866
11062
  ctx,
10867
11063
  "GET",
10868
- `/${entity}/${encodeURIComponent(id)}`
11064
+ `/${entity}/${encodeURIComponent(id2)}`
10869
11065
  );
10870
- const url = studioRecordUrl(ctx, entity, record9.id);
10871
- const markdown = studioRecordLink(ctx, entity, record9);
10872
- emit2(ctx, { kind: entity, id: record9.id, label: record9.title ?? "", url, markdown }, () => {
11066
+ const url = studioRecordUrl(ctx, entity, record11.id);
11067
+ const markdown = studioRecordLink(ctx, entity, record11);
11068
+ emit2(ctx, { kind: entity, id: record11.id, label: record11.title ?? "", url, markdown }, () => {
10873
11069
  ctx.out.log(markdown);
10874
11070
  });
10875
11071
  }
10876
11072
 
10877
11073
  // src/pm-comments.ts
10878
- async function pmComment(ctx, entity, id, parsed) {
11074
+ async function pmComment(ctx, entity, id2, parsed) {
10879
11075
  const body = stringOpt(parsed.options.body);
10880
11076
  if (!body) throw new Error('pm comment needs --body "..."');
10881
- await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id)}/comments`, {
11077
+ await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id2)}/comments`, {
10882
11078
  body,
10883
11079
  mutationId: writeMutationId2(parsed)
10884
11080
  });
10885
- ctx.out.log(`commented on ${entity} ${id}`);
11081
+ ctx.out.log(`commented on ${entity} ${id2}`);
10886
11082
  }
10887
- async function pmComments(ctx, entity, id) {
10888
- const { messages } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}/comments`);
11083
+ async function pmComments(ctx, entity, id2) {
11084
+ const { messages } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id2)}/comments`);
10889
11085
  emit2(ctx, messages, () => {
10890
11086
  if (messages.length === 0) ctx.out.log("(no comments)");
10891
11087
  else for (const message2 of messages) {
@@ -10903,12 +11099,12 @@ function fieldLine(change) {
10903
11099
  const before = change.before.length > 60 ? `${change.before.slice(0, 60)}\u2026` : change.before;
10904
11100
  return `${change.field} (was: ${before.replace(/\s+/g, " ")})`;
10905
11101
  }
10906
- async function pmHistory(ctx, entity, id, parsed) {
11102
+ async function pmHistory(ctx, entity, id2, parsed) {
10907
11103
  const limit = numberOpt(parsed.options.limit, "--limit");
10908
11104
  const page2 = await pmRequest(
10909
11105
  ctx,
10910
11106
  "GET",
10911
- `/${entity}/${encodeURIComponent(id)}/history${limit === void 0 ? "" : `?limit=${limit}`}`
11107
+ `/${entity}/${encodeURIComponent(id2)}/history${limit === void 0 ? "" : `?limit=${limit}`}`
10912
11108
  );
10913
11109
  emit2(ctx, page2, () => {
10914
11110
  if (!page2.entries.length) {
@@ -10996,16 +11192,16 @@ async function page(ctx, appId, cursor) {
10996
11192
  }
10997
11193
  return data;
10998
11194
  }
10999
- function recordState(record9) {
11000
- if (record9.column) return record9.column === "todo" ? "ready" : record9.column;
11001
- return String(record9.status ?? "");
11195
+ function recordState(record11) {
11196
+ if (record11.column) return record11.column === "todo" ? "ready" : record11.column;
11197
+ return String(record11.status ?? "");
11002
11198
  }
11003
11199
  function eventRecord(event) {
11004
11200
  return event.payload.payload;
11005
11201
  }
11006
11202
  function eventLabel(event) {
11007
- const record9 = eventRecord(event);
11008
- if (record9) return String(record9.title ?? event.payload.entityId);
11203
+ const record11 = eventRecord(event);
11204
+ if (record11) return String(record11.title ?? event.payload.entityId);
11009
11205
  const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
11010
11206
  return body || event.payload.entityId;
11011
11207
  }
@@ -11013,10 +11209,10 @@ function report2(ctx, parsed, result) {
11013
11209
  if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
11014
11210
  else if (parsed.options.jsonl !== true && result.found) {
11015
11211
  for (const event of result.events ?? []) {
11016
- const record9 = eventRecord(event);
11017
- const state2 = record9 ? recordState(record9) : "comment";
11212
+ const record11 = eventRecord(event);
11213
+ const state2 = record11 ? recordState(record11) : "comment";
11018
11214
  ctx.out.log(
11019
- `${event.id} ${event.type} ${state2}${record9?.revision ? `; r${record9.revision}` : ""} ${eventLabel(event)}`
11215
+ `${event.id} ${event.type} ${state2}${record11?.revision ? `; r${record11.revision}` : ""} ${eventLabel(event)}`
11020
11216
  );
11021
11217
  }
11022
11218
  }
@@ -11090,8 +11286,8 @@ async function pmWatch(ctx, parsed) {
11090
11286
  }
11091
11287
  firstSuccess = false;
11092
11288
  const matching = current.events.filter((event) => {
11093
- const record9 = eventRecord(event);
11094
- const state2 = record9 ? recordState(record9).toLowerCase() : "";
11289
+ const record11 = eventRecord(event);
11290
+ const state2 = record11 ? recordState(record11).toLowerCase() : "";
11095
11291
  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);
11096
11292
  });
11097
11293
  for (const event of matching) {
@@ -11179,9 +11375,9 @@ async function pmProjectAdd(ctx, parsed) {
11179
11375
  });
11180
11376
  emit2(ctx, result, () => ctx.out.log(`created project: ${result.project.name} (${result.project.id})`));
11181
11377
  }
11182
- async function pmProjectUse(ctx, id) {
11378
+ async function pmProjectUse(ctx, id2) {
11183
11379
  if (!ctx.rootDir) throw new Error("pm project use needs a local project directory");
11184
- const { project } = await pmRequest(ctx, "GET", `/project/${encodeURIComponent(id)}`);
11380
+ const { project } = await pmRequest(ctx, "GET", `/project/${encodeURIComponent(id2)}`);
11185
11381
  if (project.status !== "active") throw new Error(`project ${project.name} is ${project.status}, not active`);
11186
11382
  writePmProjectContext(ctx.rootDir, { appId: project.appId, projectId: project.id });
11187
11383
  emit2(ctx, project, () => ctx.out.log(`using ${project.appId} / ${project.name} (${project.id}) in this worktree`));
@@ -11249,9 +11445,9 @@ function allowedOptions(entity, action2) {
11249
11445
  const entityOptions = action2 === "list" || action2 === "add" || action2 === "set" || action2 === "done" ? ENTITY_OPTIONS[entity][action2] : [];
11250
11446
  return [...COMMON_OPTIONS, ...ACTION_OPTIONS[action2], ...entityOptions];
11251
11447
  }
11252
- function requireId2(id, action2) {
11253
- if (!id) throw new Error(`"pm ... ${action2}" needs an item id`);
11254
- return id;
11448
+ function requireId2(id2, action2) {
11449
+ if (!id2) throw new Error(`"pm ... ${action2}" needs an item id`);
11450
+ return id2;
11255
11451
  }
11256
11452
  async function buildContext2(parsed, deps) {
11257
11453
  const context = await resolveOperatorContext(parsed, {
@@ -11342,34 +11538,34 @@ async function pmCommand(parsed, deps = {}) {
11342
11538
  throw new Error(`pm ${action2} is only valid for tasks`);
11343
11539
  }
11344
11540
  const ctx = await buildContext2(parsed, deps);
11345
- const id = parsed.positionals[3];
11541
+ const id2 = parsed.positionals[3];
11346
11542
  switch (action2) {
11347
11543
  case "list":
11348
11544
  return pmList(ctx, entity, parsed);
11349
11545
  case "add":
11350
11546
  return pmAdd(ctx, entity, parsed);
11351
11547
  case "get":
11352
- return pmGet(ctx, entity, requireId2(id, action2));
11548
+ return pmGet(ctx, entity, requireId2(id2, action2));
11353
11549
  case "set":
11354
- return pmSet(ctx, entity, requireId2(id, action2), parsed);
11550
+ return pmSet(ctx, entity, requireId2(id2, action2), parsed);
11355
11551
  case "done":
11356
- return pmDone(ctx, entity, requireId2(id, action2), parsed);
11552
+ return pmDone(ctx, entity, requireId2(id2, action2), parsed);
11357
11553
  case "comment":
11358
- return pmComment(ctx, entity, requireId2(id, action2), parsed);
11554
+ return pmComment(ctx, entity, requireId2(id2, action2), parsed);
11359
11555
  case "comments":
11360
- return pmComments(ctx, entity, requireId2(id, action2));
11556
+ return pmComments(ctx, entity, requireId2(id2, action2));
11361
11557
  case "history":
11362
- return pmHistory(ctx, entity, requireId2(id, action2), parsed);
11558
+ return pmHistory(ctx, entity, requireId2(id2, action2), parsed);
11363
11559
  case "rm":
11364
- return pmRemove(ctx, entity, requireId2(id, action2));
11560
+ return pmRemove(ctx, entity, requireId2(id2, action2));
11365
11561
  case "link":
11366
- return pmLink(ctx, entity, requireId2(id, action2));
11562
+ return pmLink(ctx, entity, requireId2(id2, action2));
11367
11563
  case "ref":
11368
- return pmReference(ctx, entity, requireId2(id, action2));
11564
+ return pmReference(ctx, entity, requireId2(id2, action2));
11369
11565
  case "ready":
11370
11566
  case "claim":
11371
11567
  case "release":
11372
- return pmTaskLifecycle(ctx, requireId2(id, action2), action2, parsed);
11568
+ return pmTaskLifecycle(ctx, requireId2(id2, action2), action2, parsed);
11373
11569
  }
11374
11570
  }
11375
11571
 
@@ -11472,17 +11668,17 @@ async function platformStatus(parsed, deps) {
11472
11668
  }
11473
11669
  }
11474
11670
  function isPlatformStatus(value2) {
11475
- if (!record6(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
11476
- if (!record6(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
11477
- if (!record6(value2.catalog) || !record6(value2.summary)) return false;
11671
+ if (!record7(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
11672
+ if (!record7(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
11673
+ if (!record7(value2.catalog) || !record7(value2.summary)) return false;
11478
11674
  return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
11479
11675
  }
11480
11676
  function apiMessage(value2) {
11481
- if (!record6(value2)) return "request failed";
11482
- const error = record6(value2.error) ? value2.error : value2;
11677
+ if (!record7(value2)) return "request failed";
11678
+ const error = record7(value2.error) ? value2.error : value2;
11483
11679
  return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
11484
11680
  }
11485
- function record6(value2) {
11681
+ function record7(value2) {
11486
11682
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
11487
11683
  }
11488
11684
 
@@ -11523,7 +11719,7 @@ function statusVerdict(reads) {
11523
11719
  severity: "degraded"
11524
11720
  });
11525
11721
  }
11526
- const performance = record7(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
11722
+ const performance = record8(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
11527
11723
  if (performance?.status === "unavailable") {
11528
11724
  reasons.push({
11529
11725
  source: "liveSync",
@@ -11604,7 +11800,7 @@ function statusVerdict(reads) {
11604
11800
  reasons
11605
11801
  };
11606
11802
  }
11607
- function record7(value2) {
11803
+ function record8(value2) {
11608
11804
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
11609
11805
  }
11610
11806
  function numeric2(value2) {
@@ -11632,7 +11828,7 @@ function printO11yStatus(status, out) {
11632
11828
  out.log(
11633
11829
  `o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
11634
11830
  );
11635
- const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record8) : [];
11831
+ const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record9) : [];
11636
11832
  const requests = routes.reduce(
11637
11833
  (total, row) => total + numeric3(row.requests),
11638
11834
  0
@@ -11644,39 +11840,39 @@ function printO11yStatus(status, out) {
11644
11840
  out.log(
11645
11841
  `application ${status.application.httpStatus} ${requests} requests ${errors} errors`
11646
11842
  );
11647
- const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record8) : [];
11843
+ const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record9) : [];
11648
11844
  out.log(
11649
11845
  `application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
11650
11846
  (row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
11651
11847
  ).join(", ") : "none observed"}`
11652
11848
  );
11653
11849
  out.log(liveSyncLine(status.liveSync));
11654
- const canaryDurations = record8(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
11850
+ const canaryDurations = record9(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
11655
11851
  out.log(
11656
11852
  `canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
11657
11853
  );
11658
- const collectorIngest = record8(status.collector.body.ingest) ? status.collector.body.ingest : {};
11659
- const collectorStorage = record8(collectorIngest.storage) ? collectorIngest.storage : {};
11854
+ const collectorIngest = record9(status.collector.body.ingest) ? status.collector.body.ingest : {};
11855
+ const collectorStorage = record9(collectorIngest.storage) ? collectorIngest.storage : {};
11660
11856
  out.log(
11661
11857
  `collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
11662
11858
  );
11663
- const providerMetrics = record8(status.provider.body.metrics) ? status.provider.body.metrics : {};
11664
- const providerCapacity = record8(status.provider.body.capacity) ? status.provider.body.capacity : {};
11665
- const workerMemory = record8(providerCapacity.memory) ? providerCapacity.memory : {};
11859
+ const providerMetrics = record9(status.provider.body.metrics) ? status.provider.body.metrics : {};
11860
+ const providerCapacity = record9(status.provider.body.capacity) ? status.provider.body.capacity : {};
11861
+ const workerMemory = record9(providerCapacity.memory) ? providerCapacity.memory : {};
11666
11862
  out.log(
11667
11863
  `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`
11668
11864
  );
11669
11865
  for (const line of providerCapacityLines(status.providerCapacity)) {
11670
11866
  out.log(line);
11671
11867
  }
11672
- const coverage = record8(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
11673
- const coverageCounts = record8(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
11674
- const coverageBudget = record8(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
11868
+ const coverage = record9(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
11869
+ const coverageCounts = record9(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
11870
+ const coverageBudget = record9(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
11675
11871
  out.log(
11676
11872
  `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`
11677
11873
  );
11678
11874
  const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
11679
- const providerFreshness = record8(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
11875
+ const providerFreshness = record9(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
11680
11876
  out.log(
11681
11877
  `cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
11682
11878
  );
@@ -11685,17 +11881,17 @@ function printO11yStatus(status, out) {
11685
11881
  );
11686
11882
  }
11687
11883
  function providerCapacityLines(read3) {
11688
- const resources = record8(read3.body.resources) ? read3.body.resources : {};
11689
- const durableObjects = record8(resources.durableObjects) ? resources.durableObjects : {};
11690
- const periodic = record8(durableObjects.periodic) ? durableObjects.periodic : {};
11691
- const storage = record8(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
11692
- const d1 = record8(resources.d1) ? resources.d1 : {};
11693
- const d1Activity = record8(d1.activity) ? d1.activity : {};
11694
- const d1Storage = record8(d1.storage) ? d1.storage : {};
11695
- const d1Latency = record8(d1Activity.latency) ? d1Activity.latency : {};
11696
- const r2 = record8(resources.r2) ? resources.r2 : {};
11697
- const r2Operations = record8(r2.operations) ? r2.operations : {};
11698
- const r2Storage = record8(r2.storage) ? r2.storage : {};
11884
+ const resources = record9(read3.body.resources) ? read3.body.resources : {};
11885
+ const durableObjects = record9(resources.durableObjects) ? resources.durableObjects : {};
11886
+ const periodic = record9(durableObjects.periodic) ? durableObjects.periodic : {};
11887
+ const storage = record9(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
11888
+ const d1 = record9(resources.d1) ? resources.d1 : {};
11889
+ const d1Activity = record9(d1.activity) ? d1.activity : {};
11890
+ const d1Storage = record9(d1.storage) ? d1.storage : {};
11891
+ const d1Latency = record9(d1Activity.latency) ? d1Activity.latency : {};
11892
+ const r2 = record9(resources.r2) ? resources.r2 : {};
11893
+ const r2Operations = record9(r2.operations) ? r2.operations : {};
11894
+ const r2Storage = record9(r2.storage) ? r2.storage : {};
11699
11895
  const status = String(
11700
11896
  read3.body.status ?? read3.body.error ?? "unavailable"
11701
11897
  );
@@ -11706,11 +11902,11 @@ function providerCapacityLines(read3) {
11706
11902
  ];
11707
11903
  }
11708
11904
  function liveSyncLine(read3) {
11709
- const performance = record8(read3.body.performance) ? read3.body.performance : {};
11710
- const commitToSend = record8(performance.commitToSend) ? performance.commitToSend : {};
11905
+ const performance = record9(read3.body.performance) ? read3.body.performance : {};
11906
+ const commitToSend = record9(performance.commitToSend) ? performance.commitToSend : {};
11711
11907
  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`;
11712
11908
  }
11713
- function record8(value2) {
11909
+ function record9(value2) {
11714
11910
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
11715
11911
  }
11716
11912
  function numeric3(value2) {
@@ -11881,19 +12077,292 @@ function statusMinutes(value2) {
11881
12077
  }
11882
12078
  async function read2(url, headers, doFetch) {
11883
12079
  const response2 = await doFetch(url, { headers });
11884
- const text2 = await response2.text();
12080
+ const text3 = await response2.text();
11885
12081
  let body = {};
11886
- if (text2) {
12082
+ if (text3) {
11887
12083
  try {
11888
- const value2 = JSON.parse(text2);
12084
+ const value2 = JSON.parse(text3);
11889
12085
  body = value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : { value: value2 };
11890
12086
  } catch {
11891
- body = { message: text2.slice(0, 300) };
12087
+ body = { message: text3.slice(0, 300) };
11892
12088
  }
11893
12089
  }
11894
12090
  return { httpStatus: response2.status, body };
11895
12091
  }
11896
12092
 
12093
+ // src/monitoring-config.ts
12094
+ import { createHash as createHash5 } from "crypto";
12095
+ function monitoringWireConfig(cfg, env) {
12096
+ const monitoring = cfg.o11y?.monitoring;
12097
+ if (!monitoring) throw new Error("o11y.monitoring is not configured");
12098
+ if (!cfg.services.includes("o11y")) throw new Error('o11y.monitoring requires "o11y" in services');
12099
+ const authoredLink = cfg.links?.[env];
12100
+ if (!authoredLink) throw new Error(`links.${env} is required for live monitoring`);
12101
+ const baseUrl = new URL(authoredLink).toString();
12102
+ const selectedProbes = (monitoring.probes ?? []).filter((probe) => !probe.envs || probe.envs.includes(env));
12103
+ const probeIds = new Set(selectedProbes.map((probe) => probe.id));
12104
+ const selectedSlos = monitoring.slos.filter(
12105
+ (slo) => slo.indicator.type === "o11y-metric" || slo.indicator.probes.some((id2) => probeIds.has(id2))
12106
+ );
12107
+ if (selectedSlos.length === 0) throw new Error(`o11y.monitoring has no SLOs for env "${env}"`);
12108
+ for (const slo of selectedSlos) {
12109
+ if (slo.indicator.type !== "probe-success") continue;
12110
+ const unavailable = slo.indicator.probes.filter((id2) => !probeIds.has(id2));
12111
+ if (unavailable.length) throw new Error(`SLO "${slo.id}" mixes probes unavailable in env "${env}": ${unavailable.join(", ")}`);
12112
+ }
12113
+ const payload = {
12114
+ environment: env,
12115
+ baseUrl,
12116
+ probes: selectedProbes.map(normalizeProbe),
12117
+ slos: selectedSlos.map(normalizeSlo),
12118
+ ...notification(cfg.o11y.monitoring.notifications?.[env])
12119
+ };
12120
+ const revision = `sha256:${createHash5("sha256").update(canonical(payload)).digest("hex")}`;
12121
+ return { revision, ...payload };
12122
+ }
12123
+ function normalizeProbe(probe) {
12124
+ return {
12125
+ id: probe.id,
12126
+ route: probe.route,
12127
+ cadenceMinutes: durationMinutes(probe.every),
12128
+ timeoutMs: probe.timeout ?? 2e4,
12129
+ ...probe.ready?.selector ? { readySelector: probe.ready.selector } : {},
12130
+ expect: {
12131
+ status: probe.expect.status,
12132
+ ...probe.expect.titleIncludes ? { titleIncludes: probe.expect.titleIncludes } : {},
12133
+ textIncludes: probe.expect.textIncludes ?? [],
12134
+ accessibility: probe.expect.accessibility ?? []
12135
+ },
12136
+ enabled: probe.enabled !== false
12137
+ };
12138
+ }
12139
+ function normalizeSlo(slo) {
12140
+ return {
12141
+ id: slo.id,
12142
+ name: slo.name ?? slo.id,
12143
+ indicator: normalizeIndicator(slo.indicator),
12144
+ target: slo.target,
12145
+ windowMinutes: durationMinutes(slo.window),
12146
+ spike: {
12147
+ badChecks: slo.alerts?.spike?.badChecks ?? 2,
12148
+ withinChecks: slo.alerts?.spike?.withinChecks ?? 3,
12149
+ recoverAfter: slo.alerts?.spike?.recoverAfter ?? 2
12150
+ },
12151
+ trend: {
12152
+ burnRate: slo.alerts?.trend?.burnRate ?? 1,
12153
+ shortMinutes: durationMinutes(slo.alerts?.trend?.shortWindow ?? "6h"),
12154
+ longMinutes: durationMinutes(slo.alerts?.trend?.longWindow ?? "3d"),
12155
+ minBadChecks: slo.alerts?.trend?.minBadChecks ?? 2
12156
+ },
12157
+ enabled: slo.enabled !== false
12158
+ };
12159
+ }
12160
+ function normalizeIndicator(indicator) {
12161
+ if (indicator.type === "probe-success") {
12162
+ return { type: "probe-success", probes: [...new Set(indicator.probes)] };
12163
+ }
12164
+ return {
12165
+ type: "o11y-metric",
12166
+ metric: indicator.metric,
12167
+ comparator: indicator.comparator,
12168
+ threshold: indicator.threshold,
12169
+ cadenceMinutes: durationMinutes(indicator.every),
12170
+ observationWindowMinutes: durationMinutes(indicator.observationWindow),
12171
+ ...indicator.route ? { route: indicator.route } : {}
12172
+ };
12173
+ }
12174
+ function notification(policy) {
12175
+ if (!policy) return {};
12176
+ return {
12177
+ notifications: {
12178
+ email: [...new Set(policy.email.map((email) => email.trim().toLowerCase()))],
12179
+ timezone: policy.timezone,
12180
+ daily: policy.daily === void 0 ? "08:00" : policy.daily,
12181
+ weekly: policy.weekly === void 0 ? { day: "monday", at: "08:00" } : policy.weekly
12182
+ }
12183
+ };
12184
+ }
12185
+ function durationMinutes(value2) {
12186
+ const match = /^(\d+)(m|h|d)$/.exec(value2);
12187
+ if (!match) throw new Error(`unsupported duration ${value2}`);
12188
+ const amount = Number(match[1]);
12189
+ return amount * (match[2] === "d" ? 1440 : match[2] === "h" ? 60 : 1);
12190
+ }
12191
+ function canonical(value2) {
12192
+ if (Array.isArray(value2)) return `[${value2.map(canonical).join(",")}]`;
12193
+ if (value2 && typeof value2 === "object") {
12194
+ return `{${Object.entries(value2).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(",")}}`;
12195
+ }
12196
+ return JSON.stringify(value2);
12197
+ }
12198
+
12199
+ // src/monitor-command.ts
12200
+ var OPTIONS = [
12201
+ "config",
12202
+ "context",
12203
+ "platform",
12204
+ "token",
12205
+ "email",
12206
+ "json",
12207
+ "app",
12208
+ "env",
12209
+ "open",
12210
+ "yes",
12211
+ "period",
12212
+ "limit",
12213
+ "runs"
12214
+ ];
12215
+ async function monitorCommand(parsed, deps = {}) {
12216
+ assertArgs(parsed, OPTIONS, 3);
12217
+ const action2 = parsed.positionals[1] ?? "status";
12218
+ if (!["plan", "apply", "run", "status", "incidents", "report"].includes(action2)) {
12219
+ throw new Error(`unknown monitor action "${action2}". Try "odla-ai monitor status --json".`);
12220
+ }
12221
+ const context = await resolveOperatorContext(parsed, {
12222
+ allowMissingConfig: action2 !== "plan" && action2 !== "apply",
12223
+ requireApp: true
12224
+ });
12225
+ if ((action2 === "plan" || action2 === "apply") && context.config.status !== "loaded") {
12226
+ throw new Error(`monitor ${action2} requires odla.config.mjs`);
12227
+ }
12228
+ const env = context.environment.value ?? context.cfg.envs[0] ?? "prod";
12229
+ const appId = context.app.value;
12230
+ const doFetch = deps.fetch ?? fetch;
12231
+ const out = deps.stdout ?? console;
12232
+ const token = await getDeveloperToken(
12233
+ context.cfg,
12234
+ {
12235
+ configPath: context.cfg.configPath,
12236
+ token: stringOpt(parsed.options.token),
12237
+ email: stringOpt(parsed.options.email),
12238
+ open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
12239
+ openApprovalUrl: deps.openUrl
12240
+ },
12241
+ doFetch,
12242
+ out,
12243
+ action2 === "apply" || action2 === "run" ? { optionalProjectCapabilities: ["app.manage"] } : {}
12244
+ );
12245
+ const base = `${context.cfg.platformUrl}/o11y/${encodeURIComponent(appId)}/monitoring`;
12246
+ const headers = { authorization: `Bearer ${token}`, "content-type": "application/json" };
12247
+ const jsonOutput = parsed.options.json === true;
12248
+ if (action2 === "plan" || action2 === "apply") {
12249
+ const desired = monitoringWireConfig(context.cfg, env);
12250
+ const live = await request2(`${base}?env=${encodeURIComponent(env)}`, { headers }, doFetch);
12251
+ const currentRevision = record10(live.config) ? string(live.config.revision) : null;
12252
+ const changed = currentRevision !== desired.revision;
12253
+ const plan = {
12254
+ schemaVersion: 1,
12255
+ appId,
12256
+ env,
12257
+ currentRevision,
12258
+ desiredRevision: desired.revision,
12259
+ changed,
12260
+ probes: desired.probes.map((probe) => ({ id: probe.id, route: probe.route, cadenceMinutes: probe.cadenceMinutes })),
12261
+ slos: desired.slos.map((slo) => ({ id: slo.id, indicator: slo.indicator, target: slo.target, windowMinutes: slo.windowMinutes })),
12262
+ notifications: desired.notifications ? { recipients: desired.notifications.email.length, timezone: desired.notifications.timezone, daily: desired.notifications.daily, weekly: desired.notifications.weekly } : null
12263
+ };
12264
+ if (action2 === "plan") {
12265
+ emit3(plan, jsonOutput, out, () => {
12266
+ out.log(`monitor plan ${appId}/${env}: ${changed ? "changes pending" : "in sync"}`);
12267
+ out.log(`revision ${currentRevision ?? "not configured"} -> ${desired.revision}`);
12268
+ for (const probe of desired.probes) out.log(`probe ${probe.id} ${probe.route} every ${probe.cadenceMinutes}m`);
12269
+ for (const slo of desired.slos) out.log(`slo ${slo.id} ${slo.indicator.type} ${(slo.target * 100).toFixed(3)}% ${slo.windowMinutes}m`);
12270
+ });
12271
+ return;
12272
+ }
12273
+ if ((env === "prod" || env === "production") && parsed.options.yes !== true) {
12274
+ throw new Error(`refusing to apply live monitoring for "${env}" without --yes; run monitor plan first`);
12275
+ }
12276
+ if (!changed) {
12277
+ emit3({ ...plan, applied: false }, jsonOutput, out, () => out.log(`monitor apply ${appId}/${env}: already in sync`));
12278
+ return;
12279
+ }
12280
+ const applied = await request2(`${base}?env=${encodeURIComponent(env)}`, {
12281
+ method: "PUT",
12282
+ headers,
12283
+ body: JSON.stringify(desired)
12284
+ }, doFetch);
12285
+ emit3({ schemaVersion: 1, appId, env, ...applied }, jsonOutput, out, () => out.log(`monitor apply ${appId}/${env}: ${applied.changed === true ? "applied" : "unchanged"} ${desired.revision}`));
12286
+ return;
12287
+ }
12288
+ if (action2 === "run") {
12289
+ const probeId = parsed.positionals[2];
12290
+ if (!probeId) throw new Error("monitor run requires a probe id");
12291
+ const result2 = await request2(`${base}/probes/${encodeURIComponent(probeId)}/run?env=${encodeURIComponent(env)}`, {
12292
+ method: "POST",
12293
+ headers
12294
+ }, doFetch);
12295
+ emit3(result2, jsonOutput, out, () => {
12296
+ const run = record10(result2.run) ? result2.run : {};
12297
+ out.log(`monitor run ${appId}/${env}/${probeId}: ${string(run.outcome) ?? "unknown"}${run.failure_code ? ` (${String(run.failure_code)})` : ""}`);
12298
+ });
12299
+ return;
12300
+ }
12301
+ let path = action2;
12302
+ if (action2 === "report") {
12303
+ const period = stringOpt(parsed.options.period) ?? "daily";
12304
+ if (period !== "daily" && period !== "weekly") throw new Error("--period must be daily or weekly");
12305
+ path = `report?period=${period}`;
12306
+ } else if (action2 === "incidents") {
12307
+ const params = new URLSearchParams({ limit: String(numberOpt(parsed.options.limit, "--limit") ?? 100) });
12308
+ if (boolOpt(parsed.options.runs) === true) params.set("runs", "true");
12309
+ path = `incidents?${params}`;
12310
+ }
12311
+ const separator = path.includes("?") ? "&" : "?";
12312
+ const result = await request2(`${base}/${path}${separator}env=${encodeURIComponent(env)}`, { headers }, doFetch);
12313
+ emit3(result, jsonOutput, out, () => printRead(action2, appId, env, result, out));
12314
+ }
12315
+ async function request2(url, init, doFetch) {
12316
+ const response2 = await doFetch(url, init);
12317
+ const text3 = await response2.text();
12318
+ let body = {};
12319
+ try {
12320
+ const parsed = text3 ? JSON.parse(text3) : {};
12321
+ body = record10(parsed) ? parsed : { value: parsed };
12322
+ } catch {
12323
+ body = { message: text3.slice(0, 500) };
12324
+ }
12325
+ if (!response2.ok) {
12326
+ const error = record10(body.error) ? body.error : body;
12327
+ throw new Error(string(error.message) ?? string(error.code) ?? `monitor request failed (${response2.status})`);
12328
+ }
12329
+ return body;
12330
+ }
12331
+ function printRead(action2, appId, env, result, out) {
12332
+ if (action2 === "status") {
12333
+ out.log(`monitor status ${appId}/${env}: ${String(result.overall ?? (result.configured === false ? "not configured" : "unknown"))}`);
12334
+ const slos = Array.isArray(result.slos) ? result.slos.filter(record10) : [];
12335
+ for (const slo of slos) out.log(`slo ${String(slo.id)} ${String(slo.state)} ${percent(slo.observed)} observed ${percent(slo.budgetRemaining)} budget remaining`);
12336
+ const incidents = Array.isArray(result.openIncidents) ? result.openIncidents.length : 0;
12337
+ const gaps = Array.isArray(result.monitoringGaps) ? result.monitoringGaps.length : 0;
12338
+ out.log(`open incidents ${incidents}`);
12339
+ out.log(`monitoring gaps ${gaps}`);
12340
+ return;
12341
+ }
12342
+ if (action2 === "incidents") {
12343
+ const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record10) : [];
12344
+ out.log(`monitor incidents ${appId}/${env}: ${incidents.length}`);
12345
+ for (const incident2 of incidents) out.log(`${String(incident2.state)} ${String(incident2.kind)} ${String(incident2.slo_id)} ${new Date(Number(incident2.opened_at)).toISOString()}`);
12346
+ return;
12347
+ }
12348
+ out.log(`monitor report ${appId}/${env}: ${String(result.period)} ${String(result.overall)}`);
12349
+ const probes = Array.isArray(result.probes) ? result.probes.filter(record10) : [];
12350
+ for (const probe of probes) out.log(`probe ${String(probe.id)} ${Number(probe.good)} good ${Number(probe.bad)} bad ${Number(probe.unknown)} unknown`);
12351
+ }
12352
+ function emit3(value2, json, out, human) {
12353
+ if (json) out.log(JSON.stringify(value2, null, 2));
12354
+ else human();
12355
+ }
12356
+ function record10(value2) {
12357
+ return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
12358
+ }
12359
+ function string(value2) {
12360
+ return typeof value2 === "string" ? value2 : null;
12361
+ }
12362
+ function percent(value2) {
12363
+ return typeof value2 === "number" && Number.isFinite(value2) ? `${(value2 * 100).toFixed(2)}%` : "unknown";
12364
+ }
12365
+
11897
12366
  // src/provision.ts
11898
12367
  import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor6 } from "@odla-ai/apps";
11899
12368
  import { putSecret as putSecret2 } from "@odla-ai/ai";
@@ -12052,8 +12521,8 @@ function runtimeUrl(cfg, suffix = "") {
12052
12521
  return `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials${suffix}`;
12053
12522
  }
12054
12523
  async function safeError(response2) {
12055
- const text2 = await response2.text();
12056
- return redactSecrets(text2.slice(0, 1e3));
12524
+ const text3 = await response2.text();
12525
+ return redactSecrets(text3.slice(0, 1e3));
12057
12526
  }
12058
12527
  async function finish(doFetch, cfg, token, sessionId, method) {
12059
12528
  return doFetch(runtimeUrl(cfg, `/${encodeURIComponent(sessionId)}`), {
@@ -12456,6 +12925,7 @@ var COMMAND_SURFACE = {
12456
12925
  doctor: {},
12457
12926
  help: {},
12458
12927
  init: {},
12928
+ monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
12459
12929
  o11y: { status: {} },
12460
12930
  operations: { get: {}, wait: {} },
12461
12931
  platform: {
@@ -12696,12 +13166,12 @@ async function runbookRemove(ctx, slug) {
12696
13166
  // src/runbook-import.ts
12697
13167
  import { readFileSync as readFileSync10, readdirSync as readdirSync2, statSync } from "fs";
12698
13168
  import { basename as basename2, join as join13 } from "path";
12699
- function parseRunbook(text2, slug) {
12700
- let rest = text2;
13169
+ function parseRunbook(text3, slug) {
13170
+ let rest = text3;
12701
13171
  const meta = {};
12702
- const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text2);
13172
+ const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text3);
12703
13173
  if (fm) {
12704
- rest = text2.slice(fm[0].length);
13174
+ rest = text3.slice(fm[0].length);
12705
13175
  for (const line of fm[1].split(/\r?\n/)) {
12706
13176
  const pair = /^(\w+)\s*:\s*(.+)$/.exec(line.trim());
12707
13177
  if (!pair) continue;
@@ -13052,7 +13522,7 @@ async function runbookImpact(ctx, options, deps = {}) {
13052
13522
 
13053
13523
  // src/runbook-lint.ts
13054
13524
  function invocationsIn(body) {
13055
- const re = /(`|npx[^\S\n]+)?(?:@odla-ai\/cli(?:@[\w.-]+)?|odla-ai)((?:[^\S\n]+[a-z][\w-]*)+)/g;
13525
+ const re = /(`|npx[^\S\n]+(?:--yes[^\S\n]+)?)?(?:@odla-ai\/cli(?:@[\w.-]+)?|odla-ai)((?:[^\S\n]+[a-z][\w-]*)+)/g;
13056
13526
  const found = [];
13057
13527
  for (const match of body.matchAll(re)) {
13058
13528
  if (!match[1]) continue;
@@ -13248,7 +13718,8 @@ var ALLOWED2 = [
13248
13718
  "base",
13249
13719
  "requires",
13250
13720
  "platform",
13251
- "context"
13721
+ "context",
13722
+ "open"
13252
13723
  ];
13253
13724
  function requireSlug(slug, action2) {
13254
13725
  if (!slug) throw new Error(`"runbook ${action2}" needs a slug, e.g. "odla-ai runbook ${action2} release"`);
@@ -13276,6 +13747,7 @@ async function buildContext3(parsed, deps, action2) {
13276
13747
  };
13277
13748
  }
13278
13749
  const needsCapability = WRITES2.has(action2) && !dryRun && appId === PLATFORM_SCOPE && !stringOpt(parsed.options.token);
13750
+ const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
13279
13751
  const token = needsCapability ? await getScopedPlatformToken({
13280
13752
  platform: cfg.platformUrl,
13281
13753
  scope: "platform:runbook:write",
@@ -13283,6 +13755,7 @@ async function buildContext3(parsed, deps, action2) {
13283
13755
  label: `odla CLI (runbook ${action2})`,
13284
13756
  fetch: doFetch,
13285
13757
  stdout: out,
13758
+ open,
13286
13759
  openApprovalUrl: deps.openUrl,
13287
13760
  // A project, named context, or the global operator context owns the
13288
13761
  // exact-scope cache; it never follows an arbitrary shell directory.
@@ -13294,11 +13767,7 @@ async function buildContext3(parsed, deps, action2) {
13294
13767
  configPath: cfg.configPath,
13295
13768
  token: stringOpt(parsed.options.token),
13296
13769
  email: stringOpt(parsed.options.email),
13297
- // Let the normal browser policy decide (it already suppresses tests,
13298
- // CI and SSH). Hardcoding `false` meant a first-time handshake printed
13299
- // a link and opened nothing — the one moment a browser is the whole
13300
- // point.
13301
- open: void 0
13770
+ open
13302
13771
  },
13303
13772
  doFetch,
13304
13773
  out
@@ -13470,9 +13939,9 @@ function printHostedSecurityIntent(out, intent) {
13470
13939
  }
13471
13940
  function assertHostedSecurityPlanReady(plan) {
13472
13941
  const reasons = [];
13473
- for (const [label, route2] of Object.entries(plan.routes)) {
13474
- if (!route2.enabled) reasons.push(`${label} is disabled`);
13475
- if (!route2.credentialReady) reasons.push(`${label} provider credential is unavailable`);
13942
+ for (const [label, route3] of Object.entries(plan.routes)) {
13943
+ if (!route3.enabled) reasons.push(`${label} is disabled`);
13944
+ if (!route3.credentialReady) reasons.push(`${label} provider credential is unavailable`);
13476
13945
  }
13477
13946
  if (!plan.independent) reasons.push("discovery and validation are not independently routed");
13478
13947
  if (plan.ready && reasons.length === 0) return;
@@ -13526,20 +13995,20 @@ function enforceHostedReportGate(report4, parsed, out, emitSuccess) {
13526
13995
  out.log(`security gate passed: 0 confirmed >= ${failOn}; 0 leads >= ${failOnCandidates ?? "disabled"}; coverage ${report4.coverageStatus}. This is not proof that the application is secure.`);
13527
13996
  }
13528
13997
  }
13529
- function printHostedSecurityPlanRoute(out, label, route2) {
13530
- const readiness = route2.enabled && route2.credentialReady ? "ready" : [
13531
- route2.enabled ? void 0 : "disabled",
13532
- route2.credentialReady ? void 0 : "credential unavailable"
13998
+ function printHostedSecurityPlanRoute(out, label, route3) {
13999
+ const readiness = route3.enabled && route3.credentialReady ? "ready" : [
14000
+ route3.enabled ? void 0 : "disabled",
14001
+ route3.credentialReady ? void 0 : "credential unavailable"
13533
14002
  ].filter(Boolean).join(", ");
13534
- out.log(` ${label}: ${route2.provider}/${route2.model} \xB7 policy v${route2.policyVersion} \xB7 ${readiness}`);
13535
- out.log(` bounds: ${route2.maxCallsPerRun} calls/run \xB7 ${route2.maxInputBytes} input bytes/call \xB7 ${route2.maxOutputTokens} output tokens/call`);
14003
+ out.log(` ${label}: ${route3.provider}/${route3.model} \xB7 policy v${route3.policyVersion} \xB7 ${readiness}`);
14004
+ out.log(` bounds: ${route3.maxCallsPerRun} calls/run \xB7 ${route3.maxInputBytes} input bytes/call \xB7 ${route3.maxOutputTokens} output tokens/call`);
13536
14005
  }
13537
14006
  function printHostedCoverage(out, job) {
13538
14007
  const coverage = job.coverage;
13539
14008
  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}` : ""}`);
13540
14009
  }
13541
- function routeLabel(route2) {
13542
- return `${route2.provider}/${route2.model}${route2.policyVersion ? ` policy v${route2.policyVersion}` : ""}`;
14010
+ function routeLabel(route3) {
14011
+ return `${route3.provider}/${route3.model}${route3.policyVersion ? ` policy v${route3.policyVersion}` : ""}`;
13543
14012
  }
13544
14013
  var HOSTED_SEVERITIES = ["informational", "low", "medium", "high", "critical"];
13545
14014
  function hostedSeverity(value2, flag) {
@@ -13633,11 +14102,11 @@ function selectEnv(requested, declared, configPath, rootDir) {
13633
14102
  }
13634
14103
  return env;
13635
14104
  }
13636
- async function injectedToken(options, request2) {
13637
- const value2 = options.token ?? await options.getToken?.(Object.freeze({ ...request2 }));
14105
+ async function injectedToken(options, request3) {
14106
+ const value2 = options.token ?? await options.getToken?.(Object.freeze({ ...request3 }));
13638
14107
  if (typeof value2 !== "string" || value2.length < 8 || value2.length > 8192 || /\s|[\u0000-\u001f\u007f]/.test(value2)) {
13639
14108
  throw new Error(
13640
- request2.selfAudit ? "Self-audit requires an injected, scoped platform security token" : "Hosted security requires an injected app developer token or getToken callback"
14109
+ request3.selfAudit ? "Self-audit requires an injected, scoped platform security token" : "Hosted security requires an injected app developer token or getToken callback"
13641
14110
  );
13642
14111
  }
13643
14112
  return value2;
@@ -13950,11 +14419,11 @@ async function runLocalSecurityCommand(parsed, dependencies) {
13950
14419
  sourceDisclosureAck: parsed.options["ack-redacted-source"] === true ? "redacted" : void 0,
13951
14420
  fetch: doFetch,
13952
14421
  stdout: out,
13953
- getToken: async (request2) => {
13954
- if (request2.scope === "platform:security:self") {
14422
+ getToken: async (request3) => {
14423
+ if (request3.scope === "platform:security:self") {
13955
14424
  return getScopedPlatformToken({
13956
- platform: request2.platform,
13957
- scope: request2.scope,
14425
+ platform: request3.platform,
14426
+ scope: request3.scope,
13958
14427
  email: stringOpt(parsed.options.email),
13959
14428
  open,
13960
14429
  fetch: doFetch,
@@ -13963,7 +14432,7 @@ async function runLocalSecurityCommand(parsed, dependencies) {
13963
14432
  });
13964
14433
  }
13965
14434
  const cfg = await loadProjectConfig(configPath);
13966
- if (platformAudience(cfg.platformUrl) !== platformAudience(request2.platform)) {
14435
+ if (platformAudience(cfg.platformUrl) !== platformAudience(request3.platform)) {
13967
14436
  throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
13968
14437
  }
13969
14438
  return getDeveloperToken(
@@ -14168,10 +14637,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
14168
14637
  }
14169
14638
  if (command === "bug") {
14170
14639
  const action2 = parsed.positionals[1] ?? "list";
14171
- const canonical = action2 === "report" || action2 === "create" ? "add" : action2;
14640
+ const canonical2 = action2 === "report" || action2 === "create" ? "add" : action2;
14172
14641
  await pmCommand({
14173
14642
  ...parsed,
14174
- positionals: ["pm", "bug", canonical, ...parsed.positionals.slice(2)]
14643
+ positionals: ["pm", "bug", canonical2, ...parsed.positionals.slice(2)]
14175
14644
  }, runtime);
14176
14645
  return;
14177
14646
  }
@@ -14183,6 +14652,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
14183
14652
  await o11yCommand(parsed, runtime);
14184
14653
  return;
14185
14654
  }
14655
+ if (command === "monitor") {
14656
+ await monitorCommand(parsed, runtime);
14657
+ return;
14658
+ }
14186
14659
  if (command === "platform") {
14187
14660
  await platformCommand(parsed, runtime);
14188
14661
  return;
@@ -14297,6 +14770,8 @@ export {
14297
14770
  CODE_BUILD_RECIPES,
14298
14771
  codeConnect,
14299
14772
  runCodeRuntime,
14773
+ monitoringWireConfig,
14774
+ monitorCommand,
14300
14775
  provision,
14301
14776
  COMMAND_SURFACE,
14302
14777
  acceptedAfter,
@@ -14315,4 +14790,4 @@ export {
14315
14790
  isTerminalHostedSecurityStatus,
14316
14791
  runCli
14317
14792
  };
14318
- //# sourceMappingURL=chunk-YSQORU5J.js.map
14793
+ //# sourceMappingURL=chunk-RUEM7ZTA.js.map