@odla-ai/cli 0.46.12 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1252,6 +1252,7 @@ var COMMAND_SURFACE = {
1252
1252
  watch: {}
1253
1253
  },
1254
1254
  provision: {},
1255
+ promotion: { plan: {}, inspect: {}, status: {}, apply: {}, rollback: {}, next: {}, record: {}, cancel: {} },
1255
1256
  runbook: {
1256
1257
  ask: {},
1257
1258
  search: {},
@@ -3557,9 +3558,9 @@ function canonicalValue(value2) {
3557
3558
  }
3558
3559
  if (Array.isArray(value2)) return value2.map(canonicalValue);
3559
3560
  if (value2 && typeof value2 === "object") {
3560
- const record11 = value2;
3561
+ const record12 = value2;
3561
3562
  return Object.fromEntries(
3562
- Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, canonicalValue(record11[key])])
3563
+ Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, canonicalValue(record12[key])])
3563
3564
  );
3564
3565
  }
3565
3566
  throw new TypeError("canonical JSON rejects unsupported values");
@@ -5943,10 +5944,10 @@ import { existsSync as existsSync10 } from "fs";
5943
5944
  import { cpus, hostname, totalmem } from "os";
5944
5945
  import { resolve as resolve11 } from "path";
5945
5946
 
5946
- // ../harness/dist/chunk-I43KTCJ2.js
5947
+ // ../harness/dist/chunk-U324RQ4N.js
5947
5948
  var HARNESS_PROTOCOL_VERSION = 1;
5948
5949
 
5949
- // ../harness/dist/chunk-HDIR4MM5.js
5950
+ // ../harness/dist/chunk-5LRYJKUI.js
5950
5951
  import { execFile, spawn as spawn3 } from "child_process";
5951
5952
  import { constants } from "fs";
5952
5953
  import { access } from "fs/promises";
@@ -6114,8 +6115,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
6114
6115
  const maxFiles = options.maxFiles ?? 2e4;
6115
6116
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
6116
6117
  const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
6117
- const entries = inventory.flatMap((record11) => {
6118
- const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record11);
6118
+ const entries = inventory.flatMap((record12) => {
6119
+ const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record12);
6119
6120
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
6120
6121
  });
6121
6122
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
@@ -6319,7 +6320,165 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
6319
6320
  }
6320
6321
  }
6321
6322
 
6322
- // ../harness/dist/chunk-4EPJJMFG.js
6323
+ // ../harness/dist/chunk-FAN2R3GW.js
6324
+ var text3 = (value2, maximum) => {
6325
+ if (typeof value2 !== "string") return void 0;
6326
+ const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
6327
+ return bounded ? bounded.slice(0, maximum) : void 0;
6328
+ };
6329
+ var integer = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
6330
+ var record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
6331
+ var excerpt = (value2, tail = false) => {
6332
+ if (typeof value2 !== "string") return void 0;
6333
+ const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
6334
+ const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
6335
+ if (!source) return void 0;
6336
+ const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
6337
+ const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
6338
+ return text3(selected.join("\n"), 2400);
6339
+ };
6340
+ var paths = (value2) => {
6341
+ if (!Array.isArray(value2)) return void 0;
6342
+ const items = value2.flatMap((item) => {
6343
+ const path = text3(item, 1024);
6344
+ return path ? [path] : [];
6345
+ }).slice(0, 12);
6346
+ return items.length ? items : void 0;
6347
+ };
6348
+ function patchStats(value2) {
6349
+ if (typeof value2 !== "string") return {};
6350
+ let additions = 0;
6351
+ let deletions = 0;
6352
+ for (const line2 of value2.slice(0, 262144).split("\n")) {
6353
+ if (line2.startsWith("+++") || line2.startsWith("---")) continue;
6354
+ if (line2.startsWith("+")) additions += 1;
6355
+ else if (line2.startsWith("-")) deletions += 1;
6356
+ }
6357
+ return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
6358
+ }
6359
+ function searchResults(value2) {
6360
+ if (typeof value2 !== "string") return void 0;
6361
+ const results = value2.split("\n").flatMap((line2) => {
6362
+ const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
6363
+ if (!match) return [];
6364
+ const lineNumber = Number(match[2]);
6365
+ const itemText = text3(match[3], 240);
6366
+ if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
6367
+ return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
6368
+ }).slice(0, 5);
6369
+ return results.length ? results : void 0;
6370
+ }
6371
+ function codeToolRequestPresentation(request3) {
6372
+ const input = request3.input;
6373
+ if (request3.tool === "sandbox.read") {
6374
+ const path = text3(input.path, 1024);
6375
+ if (!path) return void 0;
6376
+ const startLine = integer(input.startLine);
6377
+ const endLine = integer(input.endLine);
6378
+ return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
6379
+ }
6380
+ if (request3.tool === "sandbox.list") {
6381
+ const scope = text3(input.prefix, 1024);
6382
+ return { kind: "list", ...scope ? { scope } : {} };
6383
+ }
6384
+ if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
6385
+ const query = text3(input.query, 512);
6386
+ const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
6387
+ if (request3.tool === "sandbox.search" && !query) return void 0;
6388
+ return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
6389
+ }
6390
+ if (request3.tool === "sandbox.apply_patch") {
6391
+ return { kind: "patch", ...patchStats(input.patch) };
6392
+ }
6393
+ const recipeId = text3(input.recipeId, 120);
6394
+ return recipeId ? { kind: "recipe", recipeId } : void 0;
6395
+ }
6396
+ function codeToolResultPresentation(request3, response2) {
6397
+ const started = codeToolRequestPresentation(request3);
6398
+ if (!started || !response2.ok) return started;
6399
+ const details = record5(response2.details);
6400
+ if (started.kind === "read") {
6401
+ return {
6402
+ ...started,
6403
+ ...integer(details?.startLine) ? { startLine: integer(details?.startLine) } : {},
6404
+ ...integer(details?.endLine) ? { endLine: integer(details?.endLine) } : {},
6405
+ ...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
6406
+ };
6407
+ }
6408
+ if (started.kind === "list") {
6409
+ const listed = response2.content.split("\n").filter((line2) => line2 && !line2.startsWith("\u2026") && !line2.startsWith("Workspace ")).map((line2) => text3(line2, 1024)).filter((line2) => Boolean(line2)).slice(0, 8);
6410
+ return {
6411
+ ...started,
6412
+ ...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
6413
+ ...listed.length ? { paths: listed } : {}
6414
+ };
6415
+ }
6416
+ if (started.kind === "query") {
6417
+ const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
6418
+ const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
6419
+ return {
6420
+ ...started,
6421
+ ...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
6422
+ ...results ? { results } : {},
6423
+ ...resultExcerpt ? { excerpt: resultExcerpt } : {}
6424
+ };
6425
+ }
6426
+ if (started.kind === "patch") {
6427
+ return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
6428
+ }
6429
+ const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
6430
+ return {
6431
+ ...started,
6432
+ ...integer(details?.exitCode) !== void 0 ? { exitCode: integer(details?.exitCode) } : {},
6433
+ ...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
6434
+ ...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
6435
+ ...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
6436
+ };
6437
+ }
6438
+ var MAX_FAILURE_REASON = 240;
6439
+ function toolFailureReason(response2) {
6440
+ if (response2.ok) return void 0;
6441
+ const supplied = response2.details?.failureReason;
6442
+ const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
6443
+ return reason.slice(0, MAX_FAILURE_REASON);
6444
+ }
6445
+ var DEFAULT_FAILURE_REASON = {
6446
+ "tool denied by CaMeL policy": "tool denied by CaMeL policy",
6447
+ "review sessions are read-only": "review session is read-only; workspace changes are not permitted"
6448
+ };
6449
+ function observedBroker(input) {
6450
+ const now = input.now ?? Date.now;
6451
+ return {
6452
+ execute: async (context, request3) => {
6453
+ const startedAt = now();
6454
+ const operationId = input.operationIdFor(request3.requestId);
6455
+ const startedPresentation = codeToolRequestPresentation(request3);
6456
+ await input.emit({
6457
+ type: "tool",
6458
+ phase: "started",
6459
+ tool: request3.tool,
6460
+ operationId,
6461
+ ...startedPresentation ? { presentation: startedPresentation } : {}
6462
+ });
6463
+ const response2 = await input.broker.execute(context, request3);
6464
+ const completedPresentation = codeToolResultPresentation(request3, response2);
6465
+ const failureReason = toolFailureReason(response2);
6466
+ await input.emit({
6467
+ type: "tool",
6468
+ phase: "completed",
6469
+ tool: request3.tool,
6470
+ ok: response2.ok,
6471
+ durationMs: now() - startedAt,
6472
+ operationId,
6473
+ ...failureReason ? { failureReason } : {},
6474
+ ...completedPresentation ? { presentation: completedPresentation } : {}
6475
+ });
6476
+ return response2;
6477
+ }
6478
+ };
6479
+ }
6480
+
6481
+ // ../harness/dist/chunk-Q4NL5XO3.js
6323
6482
  import { createHash as createHash3 } from "crypto";
6324
6483
  import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
6325
6484
  import { relative as relative4, resolve as resolve10 } from "path";
@@ -6363,8 +6522,8 @@ function normalize(value2) {
6363
6522
  if (Array.isArray(value2)) return value2.map(normalize);
6364
6523
  if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
6365
6524
  if (typeof value2 === "object") {
6366
- const record11 = value2;
6367
- return Object.fromEntries(Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, normalize(record11[key])]));
6525
+ const record12 = value2;
6526
+ return Object.fromEntries(Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, normalize(record12[key])]));
6368
6527
  }
6369
6528
  throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
6370
6529
  }
@@ -6656,7 +6815,7 @@ function validateSnapshot(snapshot, limits) {
6656
6815
  }
6657
6816
  }
6658
6817
 
6659
- // ../harness/dist/chunk-4EPJJMFG.js
6818
+ // ../harness/dist/chunk-Q4NL5XO3.js
6660
6819
  import { spawn as spawn4 } from "child_process";
6661
6820
  import { lstat as lstat2 } from "fs/promises";
6662
6821
  import { resolve as resolve23, sep as sep3 } from "path";
@@ -6963,7 +7122,7 @@ function looksLikeDestination(value2) {
6963
7122
  return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text4) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text4);
6964
7123
  }
6965
7124
 
6966
- // ../harness/dist/chunk-4EPJJMFG.js
7125
+ // ../harness/dist/chunk-Q4NL5XO3.js
6967
7126
  import { readFile as readFile4, stat as stat2 } from "fs/promises";
6968
7127
  import { readFile as readFile3 } from "fs/promises";
6969
7128
  import { join as join33 } from "path";
@@ -7231,7 +7390,7 @@ async function buildCodeGraph(input) {
7231
7390
  return builder.build();
7232
7391
  }
7233
7392
 
7234
- // ../harness/dist/chunk-4EPJJMFG.js
7393
+ // ../harness/dist/chunk-Q4NL5XO3.js
7235
7394
  import { createHash as createHash32 } from "crypto";
7236
7395
  async function digestStagedWorkspace(root, limits) {
7237
7396
  const files = [];
@@ -7290,7 +7449,7 @@ function createCodeRuntimeControlClient(options) {
7290
7449
  }
7291
7450
  const value2 = await response2.json().catch(() => null);
7292
7451
  if (!response2.ok) {
7293
- const problem = record5(record5(value2)?.error);
7452
+ const problem = record6(record6(value2)?.error);
7294
7453
  throw new CodeRuntimeControlError(
7295
7454
  typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
7296
7455
  response2.status,
@@ -7311,12 +7470,12 @@ function createCodeRuntimeControlClient(options) {
7311
7470
  await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
7312
7471
  ),
7313
7472
  infer: async (sessionId, inference) => {
7314
- const value2 = record5(await call4(
7473
+ const value2 = record6(await call4(
7315
7474
  `/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
7316
7475
  inference,
7317
7476
  modelRequestTimeoutMs
7318
7477
  ));
7319
- if (!value2 || value2.requestId !== inference.requestId || !record5(value2.response) || !record5(value2.receipt)) {
7478
+ if (!value2 || value2.requestId !== inference.requestId || !record6(value2.response) || !record6(value2.receipt)) {
7320
7479
  throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
7321
7480
  }
7322
7481
  return value2;
@@ -7496,12 +7655,12 @@ function validateHeartbeat(version, capabilities) {
7496
7655
  }
7497
7656
  }
7498
7657
  function parseSnapshot(value2) {
7499
- const root = record5(value2);
7500
- const host = record5(root?.host);
7658
+ const root = record6(value2);
7659
+ const host = record6(root?.host);
7501
7660
  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");
7502
7661
  const bindingIds = /* @__PURE__ */ new Set();
7503
7662
  const bindings = root.bindings.map((item) => {
7504
- const binding = record5(item);
7663
+ const binding = record6(item);
7505
7664
  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)) {
7506
7665
  throw invalid("binding");
7507
7666
  }
@@ -7511,10 +7670,10 @@ function parseSnapshot(value2) {
7511
7670
  const commandIds = /* @__PURE__ */ new Set();
7512
7671
  const commandSequences = /* @__PURE__ */ new Set();
7513
7672
  const commands = root.commands.map((item) => {
7514
- const command = record5(item);
7673
+ const command = record6(item);
7515
7674
  const binding = bindings.find((candidate) => candidate.bindingId === command?.bindingId);
7516
7675
  const sequenceKey = `${String(command?.instanceId)}:${String(command?.sequence)}`;
7517
- 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");
7676
+ 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)) || !record6(command.payload) || !Number.isSafeInteger(command.createdAt)) throw invalid("command");
7518
7677
  commandIds.add(command.commandId);
7519
7678
  commandSequences.add(sequenceKey);
7520
7679
  return command;
@@ -7523,10 +7682,10 @@ function parseSnapshot(value2) {
7523
7682
  }
7524
7683
  async function parseSource(value2) {
7525
7684
  const repositoryLimits = { maximumFiles: 1e5, maximumBytes: 80 * 1024 * 1024 };
7526
- const snapshot = record5(record5(value2)?.snapshot);
7685
+ const snapshot = record6(record6(value2)?.snapshot);
7527
7686
  if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
7528
7687
  const files = snapshot.files.map((value22) => {
7529
- const file = record5(value22);
7688
+ const file = record6(value22);
7530
7689
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
7531
7690
  return { path: file.path, content: file.content };
7532
7691
  });
@@ -7535,11 +7694,11 @@ async function parseSource(value2) {
7535
7694
  const aliases = /* @__PURE__ */ new Set();
7536
7695
  const references = [];
7537
7696
  for (const item of referencesValue) {
7538
- const reference = record5(item);
7697
+ const reference = record6(item);
7539
7698
  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");
7540
7699
  aliases.add(reference.alias);
7541
7700
  const referenceFiles = reference.files.map((entry) => {
7542
- const file = record5(entry);
7701
+ const file = record6(entry);
7543
7702
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
7544
7703
  return { path: file.path, content: file.content };
7545
7704
  });
@@ -7554,31 +7713,31 @@ async function parseSource(value2) {
7554
7713
  return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
7555
7714
  }
7556
7715
  function parseReview(value2) {
7557
- const review = record5(record5(value2)?.review);
7558
- 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");
7716
+ const review = record6(record6(value2)?.review);
7717
+ 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 || !Number.isSafeInteger(review.score) || Number(review.score) < 0 || Number(review.score) > 100 || typeof review.summary !== "string" || !Array.isArray(review.findings) || review.findings.length > 12) throw invalid("review");
7559
7718
  return review;
7560
7719
  }
7561
7720
  function parseCandidate(value2) {
7562
- const candidate = record5(record5(value2)?.candidate);
7721
+ const candidate = record6(record6(value2)?.candidate);
7563
7722
  if (!candidate || typeof candidate.candidateId !== "string" || !/^ccand_[0-9a-f]{32}$/.test(candidate.candidateId) || !["submitted", "approved", "published", "failed"].includes(String(candidate.status))) {
7564
7723
  throw invalid("candidate");
7565
7724
  }
7566
7725
  return { candidateId: candidate.candidateId, status: candidate.status };
7567
7726
  }
7568
7727
  function parseCollaborationSkills(value2) {
7569
- const items = record5(value2)?.skills;
7728
+ const items = record6(value2)?.skills;
7570
7729
  if (!Array.isArray(items) || items.length > 16) throw invalid("collaboration skills");
7571
7730
  const skillNames2 = /* @__PURE__ */ new Set();
7572
7731
  const toolNames = /* @__PURE__ */ new Set();
7573
7732
  return items.map((item) => {
7574
- const skill = record5(item);
7733
+ const skill = record6(item);
7575
7734
  if (!skill || !validManifestName(skill.name) || skillNames2.has(skill.name) || skill.instructions !== void 0 && (typeof skill.instructions !== "string" || utf8Bytes(skill.instructions) > 32e3) || !Array.isArray(skill.tools) || !skill.tools.length || skill.tools.length > 128) {
7576
7735
  throw invalid("collaboration skill");
7577
7736
  }
7578
7737
  skillNames2.add(skill.name);
7579
7738
  const tools = skill.tools.map((candidate) => {
7580
- const tool = record5(candidate);
7581
- const inputSchema = record5(tool?.inputSchema);
7739
+ const tool = record6(candidate);
7740
+ const inputSchema = record6(tool?.inputSchema);
7582
7741
  if (!tool || !validManifestName(tool.name) || toolNames.has(tool.name) || typeof tool.description !== "string" || utf8Bytes(tool.description) > 8e3 || !inputSchema || jsonBytes(inputSchema) > 64e3 || tool.concurrency !== void 0 && tool.concurrency !== "parallel") {
7583
7742
  throw invalid("collaboration tool");
7584
7743
  }
@@ -7603,12 +7762,12 @@ function parseCollaborationSkills(value2) {
7603
7762
  }
7604
7763
  function validateCollaborationToolRequest(value2) {
7605
7764
  validCommandId(value2.commandId);
7606
- if (typeof value2.toolCallId !== "string" || value2.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value2.toolCallId) || !validManifestName(value2.skill) || !validManifestName(value2.tool) || !record5(value2.input) || jsonBytes(value2.input) > 128e3) {
7765
+ if (typeof value2.toolCallId !== "string" || value2.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value2.toolCallId) || !validManifestName(value2.skill) || !validManifestName(value2.tool) || !record6(value2.input) || jsonBytes(value2.input) > 128e3) {
7607
7766
  throw new TypeError("invalid Code collaboration tool request");
7608
7767
  }
7609
7768
  }
7610
7769
  function parseCollaborationToolOutput(value2) {
7611
- const output = record5(record5(value2)?.output);
7770
+ const output = record6(record6(value2)?.output);
7612
7771
  if (!output || output.isError !== void 0 && typeof output.isError !== "boolean") {
7613
7772
  throw invalid("collaboration tool");
7614
7773
  }
@@ -7617,7 +7776,7 @@ function parseCollaborationToolOutput(value2) {
7617
7776
  return { content: output.content, ...output.isError === true ? { isError: true } : {} };
7618
7777
  }
7619
7778
  if (!Array.isArray(output.content) || output.content.length > 64 || jsonBytes(output.content) > 1e6 || !output.content.every((block2) => {
7620
- const item = record5(block2);
7779
+ const item = record6(block2);
7621
7780
  return item && ["text", "image", "audio", "document", "tool_use", "tool_result", "thinking"].includes(String(item.type));
7622
7781
  })) throw invalid("collaboration tool");
7623
7782
  return {
@@ -7650,17 +7809,48 @@ function jsonBytes(value2) {
7650
7809
  return Number.POSITIVE_INFINITY;
7651
7810
  }
7652
7811
  }
7653
- var record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
7812
+ var record6 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
7654
7813
  var invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
7655
7814
  var RESERVED = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
7656
7815
  var SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
7657
7816
  var PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
7658
7817
  var FORBIDDEN = /^(?:GIT binary patch|Binary files |rename (?:from|to) |copy (?:from|to) |similarity index |old mode |new mode |deleted file mode 160000|new file mode 160000)/m;
7659
7818
  function stripPatchEnvelope(patch2) {
7660
- if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return patch2;
7819
+ if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return applyPatchDialectToDiff(patch2);
7661
7820
  const kept = patch2.split("\n").filter((line2) => !/^\*\*\* (?:Begin|End) Patch\s*$/.test(line2));
7662
7821
  const stripped = kept.join("\n");
7663
- return /^diff --git /m.test(stripped) ? stripped : patch2;
7822
+ if (/^diff --git /m.test(stripped)) return stripped;
7823
+ const translated = applyPatchDialectToDiff(stripped);
7824
+ return translated === stripped ? patch2 : translated;
7825
+ }
7826
+ function applyPatchDialectToDiff(patch2) {
7827
+ if (!/^\*\*\* (?:Update|Add|Delete) File: /m.test(patch2)) return patch2;
7828
+ const out = [];
7829
+ let open = false;
7830
+ for (const line2 of patch2.split("\n")) {
7831
+ const file = /^\*\*\* (Update|Add|Delete) File: (.+?)\s*$/.exec(line2);
7832
+ if (file) {
7833
+ const [, verb, raw] = file;
7834
+ const path = raw.trim();
7835
+ if (!PATH.test(path)) return patch2;
7836
+ out.push(`diff --git a/${path} b/${path}`);
7837
+ if (verb === "Add") out.push("new file mode 100644", "--- /dev/null", `+++ b/${path}`);
7838
+ else if (verb === "Delete") out.push(`--- a/${path}`, "+++ /dev/null");
7839
+ else out.push(`--- a/${path}`, `+++ b/${path}`);
7840
+ open = true;
7841
+ continue;
7842
+ }
7843
+ if (/^\*\*\* /.test(line2)) continue;
7844
+ if (!open) continue;
7845
+ if (/^@@/.test(line2)) {
7846
+ out.push("@@ -1 +1 @@");
7847
+ continue;
7848
+ }
7849
+ out.push(line2);
7850
+ }
7851
+ if (!open) return patch2;
7852
+ return `${out.join("\n").replace(/\n+$/, "")}
7853
+ `;
7664
7854
  }
7665
7855
  function validateCodePatch(rawPatch, maxBytes) {
7666
7856
  const patch2 = stripPatchEnvelope(rawPatch);
@@ -7713,17 +7903,20 @@ function resolveCodePath(workspaceDir, path) {
7713
7903
  if (target !== root && !target.startsWith(`${root}${sep3}`)) throw new TypeError("path escapes the staged workspace");
7714
7904
  return target;
7715
7905
  }
7906
+ function hasContextFreeHunk(patch2) {
7907
+ const bodies = patch2.split(/^@@.*$/m).slice(1);
7908
+ return bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
7909
+ }
7716
7910
  function describePatchFailure(patch2, detail) {
7717
7911
  const hunks = patch2.split("\n").filter((line2) => line2.startsWith("@@"));
7718
- const bodies = patch2.split(/^@@.*$/m).slice(1);
7719
- const contextless = bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
7720
- const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
7912
+ const hint = hunks.length > 0 && hasContextFreeHunk(patch2) ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
7721
7913
  return `patch did not apply: ${detail}${hint}`;
7722
7914
  }
7723
7915
  async function applyCodePatch(workspaceDir, rawPatch, paths2) {
7724
7916
  const patch2 = stripPatchEnvelope(rawPatch);
7725
- await gitApply(workspaceDir, patch2, true);
7726
- await gitApply(workspaceDir, patch2, false);
7917
+ const zero = hasContextFreeHunk(patch2);
7918
+ await gitApply(workspaceDir, patch2, true, zero);
7919
+ await gitApply(workspaceDir, patch2, false, zero);
7727
7920
  for (const path of paths2) {
7728
7921
  try {
7729
7922
  const info = await lstat2(resolveCodePath(workspaceDir, path));
@@ -7735,9 +7928,16 @@ async function applyCodePatch(workspaceDir, rawPatch, paths2) {
7735
7928
  }
7736
7929
  }
7737
7930
  }
7738
- function gitApply(cwd, patch2, check) {
7931
+ function gitApply(cwd, patch2, check, unidiffZero = false) {
7739
7932
  return new Promise((accept, reject) => {
7740
- const args = ["apply", "--recount", "--whitespace=nowarn", ...check ? ["--check"] : [], "-"];
7933
+ const args = [
7934
+ "apply",
7935
+ "--recount",
7936
+ ...unidiffZero ? ["--unidiff-zero"] : [],
7937
+ "--whitespace=nowarn",
7938
+ ...check ? ["--check"] : [],
7939
+ "-"
7940
+ ];
7741
7941
  const child = spawn4("git", args, {
7742
7942
  cwd,
7743
7943
  shell: false,
@@ -7746,8 +7946,8 @@ function gitApply(cwd, patch2, check) {
7746
7946
  });
7747
7947
  let stderr2 = "";
7748
7948
  child.stderr.setEncoding("utf8");
7749
- child.stderr.on("data", (text22) => {
7750
- if (stderr2.length < 4e3) stderr2 += text22.slice(0, 4e3);
7949
+ child.stderr.on("data", (text4) => {
7950
+ if (stderr2.length < 4e3) stderr2 += text4.slice(0, 4e3);
7751
7951
  });
7752
7952
  child.once("error", reject);
7753
7953
  child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr2.trim().slice(0, 500)))));
@@ -7993,7 +8193,7 @@ function validate2(input) {
7993
8193
  maximumFiles: policy.maximumFiles ?? 2e4,
7994
8194
  maximumBytes: policy.maximumBytes ?? 512 * 1024 * 1024
7995
8195
  };
7996
- if ([...result.testPathPrefixes, ...result.testPathSuffixes].some((rule) => !RULE.test(rule)) || !integer(result.maximumChangedTests, 0, 1e4) || !integer(result.maximumPatchBytes, 1, 4 * 1024 * 1024) || !integer(result.maximumFiles, 1, 1e5) || !integer(result.maximumBytes, 1, 2 * 1024 * 1024 * 1024)) {
8196
+ if ([...result.testPathPrefixes, ...result.testPathSuffixes].some((rule) => !RULE.test(rule)) || !integer2(result.maximumChangedTests, 0, 1e4) || !integer2(result.maximumPatchBytes, 1, 4 * 1024 * 1024) || !integer2(result.maximumFiles, 1, 1e5) || !integer2(result.maximumBytes, 1, 2 * 1024 * 1024 * 1024)) {
7997
8197
  throw new TypeError("clean verification policy exceeds its bounds");
7998
8198
  }
7999
8199
  return result;
@@ -8044,7 +8244,7 @@ function hashFile(path) {
8044
8244
  });
8045
8245
  }
8046
8246
  function checkedResult(result, maximumOutputBytes) {
8047
- if (!integer(result.exitCode, 0, 255) || !integer(result.durationMs, 0, 30 * 6e4) || typeof result.stdout !== "string" || typeof result.stderr !== "string" || typeof result.outputLimitExceeded !== "boolean" || typeof result.timedOut !== "boolean") {
8247
+ if (!integer2(result.exitCode, 0, 255) || !integer2(result.durationMs, 0, 30 * 6e4) || typeof result.stdout !== "string" || typeof result.stderr !== "string" || typeof result.outputLimitExceeded !== "boolean" || typeof result.timedOut !== "boolean") {
8048
8248
  throw new TypeError("recipe executor returned an invalid result");
8049
8249
  }
8050
8250
  const bytes = Buffer.byteLength(result.stdout) + Buffer.byteLength(result.stderr);
@@ -8094,7 +8294,7 @@ function digestJson(value2) {
8094
8294
  function digestBytes(value2) {
8095
8295
  return `sha256:${createHash22("sha256").update(value2).digest("hex")}`;
8096
8296
  }
8097
- function integer(value2, minimum, maximum) {
8297
+ function integer2(value2, minimum, maximum) {
8098
8298
  return Number.isSafeInteger(value2) && value2 >= minimum && value2 <= maximum;
8099
8299
  }
8100
8300
  async function prepareRuntimeCheckpoint(input) {
@@ -8121,9 +8321,9 @@ async function prepareRuntimeCheckpoint(input) {
8121
8321
  if (evidence.receipt.outcome === "passed") {
8122
8322
  verification = evidence.receipt;
8123
8323
  review = await input.review(patch2, verification);
8124
- note = review.verdict === "approved" ? "Clean verification and independent review passed" : "Clean verification passed; independent review rejected the candidate";
8324
+ note = describeReview(review);
8125
8325
  } else {
8126
- note = `Clean verification failed: ${evidence.receipt.recipes.filter((recipe2) => recipe2.status !== "passed").map((recipe2) => `${recipe2.recipeId}=${recipe2.status}`).join(", ")}`;
8326
+ note = describeGateFailure(evidence);
8127
8327
  }
8128
8328
  } catch (cause) {
8129
8329
  note = `Candidate verification or review failed closed: ${message(cause)}`;
@@ -8148,6 +8348,23 @@ async function prepareRuntimeCheckpoint(input) {
8148
8348
  });
8149
8349
  return { checkpoint, verification, review, note };
8150
8350
  }
8351
+ function describeReview(review) {
8352
+ const findings = review.findings.map((finding) => ` - [${finding.severity}] ${finding.detail}`).join("\n");
8353
+ const head = review.verdict === "approved" ? `Clean verification and independent review passed (score ${review.score}/100).` : `Clean verification passed; independent review REJECTED this candidate (score ${review.score}/100). Address the findings and checkpoint again.`;
8354
+ return [head, review.summary, findings].filter(Boolean).join("\n").slice(0, 4e3);
8355
+ }
8356
+ function describeGateFailure(evidence) {
8357
+ const failed = evidence.receipt.recipes.filter((recipe2) => recipe2.status !== "passed");
8358
+ const detail = failed.map((recipe2) => {
8359
+ const log = evidence.logs.find((entry) => entry.recipeId === recipe2.recipeId);
8360
+ const output = `${log?.stdout ?? ""}
8361
+ ${log?.stderr ?? ""}`.trim();
8362
+ return `${recipe2.recipeId}=${recipe2.status}${output ? `
8363
+ ${output.slice(0, 1500)}` : ""}`;
8364
+ }).join("\n\n");
8365
+ return `Clean verification failed. Fix this and checkpoint again:
8366
+ ${detail}`.slice(0, 4e3);
8367
+ }
8151
8368
  var message = (value2) => (value2 instanceof Error ? value2.message : String(value2)).slice(0, 500);
8152
8369
  var CodeRuntimeCheckpointManager = class {
8153
8370
  constructor(options) {
@@ -9166,8 +9383,14 @@ function optionalInteger(value2) {
9166
9383
  if (!Number.isSafeInteger(value2) || value2 < 1) throw new TypeError("line bounds must be positive integers");
9167
9384
  return value2;
9168
9385
  }
9386
+ var DEFAULT_FAILURE_REASON2 = {
9387
+ "tool denied by CaMeL policy": "tool denied by CaMeL policy",
9388
+ "review sessions are read-only": "review session is read-only; workspace changes are not permitted"
9389
+ };
9169
9390
  function response(request3, ok, content2, details) {
9170
- return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
9391
+ if (ok) return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
9392
+ const failureReason = typeof details?.failureReason === "string" && details.failureReason || DEFAULT_FAILURE_REASON2[content2] || content2 || "tool request failed; inspect the tool input and workspace state";
9393
+ return { requestId: request3.requestId, ok, content: content2, details: { ...details, failureReason } };
9171
9394
  }
9172
9395
  var cache = /* @__PURE__ */ new Map();
9173
9396
  function workspaceGraphs(workspaceDir, paths2) {
@@ -9743,120 +9966,6 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
9743
9966
  }
9744
9967
  var digestRuntimeValue = (value2) => `sha256:${createHash32("sha256").update(value2).digest("hex")}`;
9745
9968
  var runtimeErrorMessage = (value2) => value2 instanceof Error ? value2.message : String(value2);
9746
- var text3 = (value2, maximum) => {
9747
- if (typeof value2 !== "string") return void 0;
9748
- const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
9749
- return bounded ? bounded.slice(0, maximum) : void 0;
9750
- };
9751
- var integer2 = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
9752
- var record32 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
9753
- var excerpt = (value2, tail = false) => {
9754
- if (typeof value2 !== "string") return void 0;
9755
- const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
9756
- const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
9757
- if (!source) return void 0;
9758
- const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
9759
- const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
9760
- return text3(selected.join("\n"), 2400);
9761
- };
9762
- var paths = (value2) => {
9763
- if (!Array.isArray(value2)) return void 0;
9764
- const items = value2.flatMap((item) => {
9765
- const path = text3(item, 1024);
9766
- return path ? [path] : [];
9767
- }).slice(0, 12);
9768
- return items.length ? items : void 0;
9769
- };
9770
- function patchStats(value2) {
9771
- if (typeof value2 !== "string") return {};
9772
- let additions = 0;
9773
- let deletions = 0;
9774
- for (const line2 of value2.slice(0, 262144).split("\n")) {
9775
- if (line2.startsWith("+++") || line2.startsWith("---")) continue;
9776
- if (line2.startsWith("+")) additions += 1;
9777
- else if (line2.startsWith("-")) deletions += 1;
9778
- }
9779
- return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
9780
- }
9781
- function searchResults(value2) {
9782
- if (typeof value2 !== "string") return void 0;
9783
- const results = value2.split("\n").flatMap((line2) => {
9784
- const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
9785
- if (!match) return [];
9786
- const lineNumber = Number(match[2]);
9787
- const itemText = text3(match[3], 240);
9788
- if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
9789
- return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
9790
- }).slice(0, 5);
9791
- return results.length ? results : void 0;
9792
- }
9793
- function codeToolRequestPresentation(request3) {
9794
- const input = request3.input;
9795
- if (request3.tool === "sandbox.read") {
9796
- const path = text3(input.path, 1024);
9797
- if (!path) return void 0;
9798
- const startLine = integer2(input.startLine);
9799
- const endLine = integer2(input.endLine);
9800
- return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
9801
- }
9802
- if (request3.tool === "sandbox.list") {
9803
- const scope = text3(input.prefix, 1024);
9804
- return { kind: "list", ...scope ? { scope } : {} };
9805
- }
9806
- if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
9807
- const query = text3(input.query, 512);
9808
- const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
9809
- if (request3.tool === "sandbox.search" && !query) return void 0;
9810
- return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
9811
- }
9812
- if (request3.tool === "sandbox.apply_patch") {
9813
- return { kind: "patch", ...patchStats(input.patch) };
9814
- }
9815
- const recipeId = text3(input.recipeId, 120);
9816
- return recipeId ? { kind: "recipe", recipeId } : void 0;
9817
- }
9818
- function codeToolResultPresentation(request3, response2) {
9819
- const started = codeToolRequestPresentation(request3);
9820
- if (!started || !response2.ok) return started;
9821
- const details = record32(response2.details);
9822
- if (started.kind === "read") {
9823
- return {
9824
- ...started,
9825
- ...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
9826
- ...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
9827
- ...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
9828
- };
9829
- }
9830
- if (started.kind === "list") {
9831
- const listed = response2.content.split("\n").filter((line2) => line2 && !line2.startsWith("\u2026") && !line2.startsWith("Workspace ")).map((line2) => text3(line2, 1024)).filter((line2) => Boolean(line2)).slice(0, 8);
9832
- return {
9833
- ...started,
9834
- ...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
9835
- ...listed.length ? { paths: listed } : {}
9836
- };
9837
- }
9838
- if (started.kind === "query") {
9839
- const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
9840
- const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
9841
- return {
9842
- ...started,
9843
- ...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
9844
- ...results ? { results } : {},
9845
- ...resultExcerpt ? { excerpt: resultExcerpt } : {}
9846
- };
9847
- }
9848
- if (started.kind === "patch") {
9849
- return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
9850
- }
9851
- const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
9852
- return {
9853
- ...started,
9854
- ...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
9855
- ...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
9856
- ...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
9857
- ...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
9858
- };
9859
- }
9860
9969
  function codeRuntimeAcknowledgementGate(signal) {
9861
9970
  let settle;
9862
9971
  let settled = false;
@@ -10158,36 +10267,11 @@ var TheseusRuntimeEngine = class {
10158
10267
  }
10159
10268
  /** Report every brokered effect as it starts and finishes. */
10160
10269
  #observed(command, active, broker) {
10161
- return {
10162
- execute: async (context, request3) => {
10163
- const startedAt = Date.now();
10164
- const operationId = digestRuntimeValue(`${command.commandId}:${request3.requestId}`);
10165
- const startedPresentation = codeToolRequestPresentation(request3);
10166
- await this.#event(
10167
- command,
10168
- {
10169
- type: "tool",
10170
- phase: "started",
10171
- tool: request3.tool,
10172
- operationId,
10173
- ...startedPresentation ? { presentation: startedPresentation } : {}
10174
- },
10175
- active.conversationRefs
10176
- ).catch(() => void 0);
10177
- const response2 = await broker.execute(context, request3);
10178
- const completedPresentation = codeToolResultPresentation(request3, response2);
10179
- await this.#event(command, {
10180
- type: "tool",
10181
- phase: "completed",
10182
- tool: request3.tool,
10183
- ok: response2.ok,
10184
- durationMs: Date.now() - startedAt,
10185
- operationId,
10186
- ...completedPresentation ? { presentation: completedPresentation } : {}
10187
- }, active.conversationRefs).catch(() => void 0);
10188
- return response2;
10189
- }
10190
- };
10270
+ return observedBroker({
10271
+ broker,
10272
+ operationIdFor: (requestId) => digestRuntimeValue(`${command.commandId}:${requestId}`),
10273
+ emit: (event) => this.#event(command, event, active.conversationRefs).catch(() => void 0)
10274
+ });
10191
10275
  }
10192
10276
  async #checkpoint(command) {
10193
10277
  const active = this.#active.get(command.sessionId);
@@ -10497,6 +10581,19 @@ function digestText(value2) {
10497
10581
  // src/code-runtime-config.ts
10498
10582
  var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
10499
10583
  var CODE_BUILD_RECIPES = Object.freeze([{
10584
+ id: "odla-code-gates",
10585
+ image: CODE_NODE_IMAGE,
10586
+ // Selects the repository's fast, no-capability gates from the same manifest
10587
+ // CI and `npm run preflight` read, so a new gate is picked up without a
10588
+ // second list to keep in sync. The container has no shell: this is argv.
10589
+ command: ["node", "scripts/recipe-gates.mjs"],
10590
+ timeoutMs: 18e4,
10591
+ maxOutputBytes: 1024 * 1024,
10592
+ cpus: 1,
10593
+ // check:secrets holds ~2,500 source files in memory at once.
10594
+ memory: "1g",
10595
+ pids: 128
10596
+ }, {
10500
10597
  id: "odla-code-contracts",
10501
10598
  image: CODE_NODE_IMAGE,
10502
10599
  command: [
@@ -10676,20 +10773,20 @@ async function runCodeRuntime(input) {
10676
10773
  }
10677
10774
  }
10678
10775
  function parseConnection(value2, appId, appEnv) {
10679
- const root = record6(value2);
10680
- const host = record6(root?.host);
10681
- const offer = record6(root?.offer);
10682
- const binding = record6(root?.binding);
10776
+ const root = record7(value2);
10777
+ const host = record7(root?.host);
10778
+ const offer = record7(root?.offer);
10779
+ const binding = record7(root?.binding);
10683
10780
  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)) {
10684
10781
  throw new Error("connect Code host returned an invalid response");
10685
10782
  }
10686
10783
  return root;
10687
10784
  }
10688
10785
  function apiFailure(action2, status, value2) {
10689
- const message2 = record6(record6(value2)?.error)?.message;
10786
+ const message2 = record7(record7(value2)?.error)?.message;
10690
10787
  return `${action2} failed (${status})${typeof message2 === "string" ? `: ${message2}` : ""}`;
10691
10788
  }
10692
- function record6(value2) {
10789
+ function record7(value2) {
10693
10790
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
10694
10791
  }
10695
10792
 
@@ -11195,6 +11292,14 @@ Usage:
11195
11292
  odla-ai ai models [--config odla.config.mjs] [--env dev] [--provider <id>] [--json]
11196
11293
  odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
11197
11294
  odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
11295
+ odla-ai promotion plan --file <promotion-plan.json> [--env dev] [--json]
11296
+ odla-ai promotion inspect <plan-id> [--env dev] [--json]
11297
+ odla-ai promotion status <plan-id> [--env dev] [--json]
11298
+ odla-ai promotion apply <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
11299
+ odla-ai promotion rollback <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
11300
+ odla-ai promotion next <plan-id> <operation-id> [--env dev] [--json]
11301
+ odla-ai promotion record <plan-id> <operation-id> <step-id> --file <step-receipt.json> [--env dev] [--json]
11302
+ odla-ai promotion cancel <plan-id> <operation-id> [--env dev] [--json]
11198
11303
  odla-ai operations get <operation-id> [--json]
11199
11304
  odla-ai operations wait <operation-id> [--interval <seconds>] [--timeout <seconds>] [--json]
11200
11305
  odla-ai calendar status [--env dev] [--email <odla-account>] [--json]
@@ -11361,6 +11466,11 @@ Commands:
11361
11466
  init Create a generic odla.config.mjs plus starter schema/rules files.
11362
11467
  doctor Validate and summarize the project config without network calls.
11363
11468
  config Diff Registry intent, freeze a CAS-bound plan, and conditionally apply its safe actions.
11469
+ promotion Plan and inspect exact selective-release bundles without target
11470
+ mutation, then queue and resume a human-approved apply or rollback
11471
+ from an enrolled device. Each target step is leased and receipt-
11472
+ journaled; approval remains bound to the immutable plan digest,
11473
+ target set, action, and expiry, so every mismatch fails closed.
11364
11474
  operations Inspect or wait on one exact, durable config-operation receipt.
11365
11475
  calendar Inspect, connect, or disconnect the live Google booking connection.
11366
11476
  app Archive (suspend, data retained), restore, export, import, or
@@ -12236,17 +12346,17 @@ function collectEntityFields(entity, parsed, allowClear) {
12236
12346
  if (entity === "task" && fields.column === "ready") fields.column = "todo";
12237
12347
  return fields;
12238
12348
  }
12239
- function statusCol(entity, record11) {
12240
- if (entity === "bug") return `${record11.status ?? ""}/${record11.severity ?? ""}`;
12349
+ function statusCol(entity, record12) {
12350
+ if (entity === "bug") return `${record12.status ?? ""}/${record12.severity ?? ""}`;
12241
12351
  if (entity === "task") {
12242
- const state2 = record11.column === "todo" ? "ready" : String(record11.column ?? "");
12243
- return record11.revision ? `${state2}; r${record11.revision}` : state2;
12352
+ const state2 = record12.column === "todo" ? "ready" : String(record12.column ?? "");
12353
+ return record12.revision ? `${state2}; r${record12.revision}` : state2;
12244
12354
  }
12245
- return String(record11.status ?? "");
12355
+ return String(record12.status ?? "");
12246
12356
  }
12247
- function referenceMarkup(entity, record11) {
12248
- const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
12249
- return `@[${label}](pm:${entity}/${record11.id})`;
12357
+ function referenceMarkup(entity, record12) {
12358
+ const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
12359
+ return `@[${label}](pm:${entity}/${record12.id})`;
12250
12360
  }
12251
12361
  var STUDIO_SECTION = {
12252
12362
  goal: "goals",
@@ -12260,13 +12370,13 @@ function studioRecordUrl(ctx, entity, id2) {
12260
12370
  ctx.platformUrl
12261
12371
  ).href;
12262
12372
  }
12263
- function studioRecordLink(ctx, entity, record11) {
12264
- const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
12265
- return `[${label}](${studioRecordUrl(ctx, entity, record11.id)})`;
12373
+ function studioRecordLink(ctx, entity, record12) {
12374
+ const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
12375
+ return `[${label}](${studioRecordUrl(ctx, entity, record12.id)})`;
12266
12376
  }
12267
- function printRecord(ctx, entity, record11) {
12377
+ function printRecord(ctx, entity, record12) {
12268
12378
  ctx.out.log(
12269
- `${record11.id} [${statusCol(entity, record11)}] ${record11.appId} ${studioRecordLink(ctx, entity, record11)}`
12379
+ `${record12.id} [${statusCol(entity, record12)}] ${record12.appId} ${studioRecordLink(ctx, entity, record12)}`
12270
12380
  );
12271
12381
  }
12272
12382
  function emit2(ctx, value2, human) {
@@ -12320,8 +12430,8 @@ async function pmAdd(ctx, entity, parsed) {
12320
12430
  input,
12321
12431
  mutationId: writeMutationId2(parsed)
12322
12432
  });
12323
- const record11 = { id: res.id, appId, title: String(input.title) };
12324
- emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record11)}`));
12433
+ const record12 = { id: res.id, appId, title: String(input.title) };
12434
+ emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record12)}`));
12325
12435
  }
12326
12436
  var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
12327
12437
  var UUID_PREFIX = /^[0-9a-f-]{8,35}$/i;
@@ -12341,7 +12451,7 @@ async function resolvePmId(ctx, entity, supplied) {
12341
12451
  "GET",
12342
12452
  `/${entity}?limit=${limit}&offset=${offset}`
12343
12453
  );
12344
- matches.push(...page2.records.map((record11) => record11.id).filter((id2) => id2.toLowerCase().startsWith(prefix)));
12454
+ matches.push(...page2.records.map((record12) => record12.id).filter((id2) => id2.toLowerCase().startsWith(prefix)));
12345
12455
  offset += page2.records.length;
12346
12456
  if (matches.length > 1 || page2.records.length === 0 || offset >= page2.total) break;
12347
12457
  }
@@ -12355,17 +12465,17 @@ async function resolvePmId(ctx, entity, supplied) {
12355
12465
  }
12356
12466
  async function pmGet(ctx, entity, id2) {
12357
12467
  const resolved = await resolvePmId(ctx, entity, id2);
12358
- const { record: record11 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(resolved)}`);
12359
- emit2(ctx, record11, () => printRecord(ctx, entity, record11));
12468
+ const { record: record12 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(resolved)}`);
12469
+ emit2(ctx, record12, () => printRecord(ctx, entity, record12));
12360
12470
  }
12361
12471
  async function pmReference(ctx, entity, id2) {
12362
- const { record: record11 } = await pmRequest(
12472
+ const { record: record12 } = await pmRequest(
12363
12473
  ctx,
12364
12474
  "GET",
12365
12475
  `/${entity}/${encodeURIComponent(id2)}`
12366
12476
  );
12367
- const markup = referenceMarkup(entity, record11);
12368
- emit2(ctx, { kind: `pm:${entity}`, id: record11.id, label: record11.title ?? "", markup }, () => {
12477
+ const markup = referenceMarkup(entity, record12);
12478
+ emit2(ctx, { kind: `pm:${entity}`, id: record12.id, label: record12.title ?? "", markup }, () => {
12369
12479
  ctx.out.log(markup);
12370
12480
  });
12371
12481
  }
@@ -12450,16 +12560,16 @@ var PER_ENTITY = {
12450
12560
  decision: ["status", "body", "supersedesId"],
12451
12561
  bug: ["status", "severity", "description", "goalId", "assigneeId", "decisionId"]
12452
12562
  };
12453
- function leanRecord(entity, record11) {
12563
+ function leanRecord(entity, record12) {
12454
12564
  const out = {};
12455
12565
  for (const key of [...COMMON, ...PER_ENTITY[entity]]) {
12456
- const value2 = record11[key];
12566
+ const value2 = record12[key];
12457
12567
  if (value2 !== void 0 && value2 !== null) out[key] = value2;
12458
12568
  }
12459
12569
  return out;
12460
12570
  }
12461
12571
  function leanRecords(entity, records, verbose) {
12462
- return verbose ? records : records.map((record11) => leanRecord(entity, record11));
12572
+ return verbose ? records : records.map((record12) => leanRecord(entity, record12));
12463
12573
  }
12464
12574
 
12465
12575
  // src/pm-intake.ts
@@ -12492,8 +12602,8 @@ async function pmNext(ctx, parsed) {
12492
12602
  // One request for both live columns; the split below is over open work only.
12493
12603
  listFiltered(ctx, "task", appId, projectId, { column: `${READY_COLUMN},doing` })
12494
12604
  ]);
12495
- const ready = tasks.filter((record11) => record11.column === READY_COLUMN);
12496
- const doing = tasks.filter((record11) => record11.column === "doing");
12605
+ const ready = tasks.filter((record12) => record12.column === READY_COLUMN);
12606
+ const doing = tasks.filter((record12) => record12.column === "doing");
12497
12607
  const guidance = !goals.length ? "discuss alignment with the user before creating or claiming project work" : !ready.length ? 'refine a linked Backlog task and mark it Ready with "pm task ready <id>"' : `claim the top Ready task in one call: odla-ai pm start --app ${appId}`;
12498
12608
  const monitor = `odla-ai pm watch --app ${appId} --jsonl`;
12499
12609
  const result = {
@@ -12514,7 +12624,7 @@ async function pmNext(ctx, parsed) {
12514
12624
  ]) {
12515
12625
  ctx.out.log(`${label}:`);
12516
12626
  if (!records.length) ctx.out.log("- (none)");
12517
- else for (const record11 of records) printRecord(ctx, entity, record11);
12627
+ else for (const record12 of records) printRecord(ctx, entity, record12);
12518
12628
  }
12519
12629
  ctx.out.log(`next: ${guidance}`);
12520
12630
  ctx.out.log(`monitor: ${monitor}`);
@@ -12553,7 +12663,7 @@ async function pmHandoff(ctx, parsed) {
12553
12663
  ]) {
12554
12664
  ctx.out.log(`${label}:`);
12555
12665
  if (!records.length) ctx.out.log("- (none)");
12556
- else for (const record11 of records) printRecord(ctx, entity, record11);
12666
+ else for (const record12 of records) printRecord(ctx, entity, record12);
12557
12667
  }
12558
12668
  ctx.out.log(`monitor: ${monitor}`);
12559
12669
  });
@@ -12628,14 +12738,14 @@ async function pmStart(ctx, parsed) {
12628
12738
 
12629
12739
  // src/pm-links.ts
12630
12740
  async function pmLink(ctx, entity, id2) {
12631
- const { record: record11 } = await pmRequest(
12741
+ const { record: record12 } = await pmRequest(
12632
12742
  ctx,
12633
12743
  "GET",
12634
12744
  `/${entity}/${encodeURIComponent(id2)}`
12635
12745
  );
12636
- const url = studioRecordUrl(ctx, entity, record11.id);
12637
- const markdown = studioRecordLink(ctx, entity, record11);
12638
- emit2(ctx, { kind: entity, id: record11.id, label: record11.title ?? "", url, markdown }, () => {
12746
+ const url = studioRecordUrl(ctx, entity, record12.id);
12747
+ const markdown = studioRecordLink(ctx, entity, record12);
12748
+ emit2(ctx, { kind: entity, id: record12.id, label: record12.title ?? "", url, markdown }, () => {
12639
12749
  ctx.out.log(markdown);
12640
12750
  });
12641
12751
  }
@@ -12762,16 +12872,16 @@ async function page(ctx, appId, cursor) {
12762
12872
  }
12763
12873
  return data;
12764
12874
  }
12765
- function recordState(record11) {
12766
- if (record11.column) return record11.column === "todo" ? "ready" : record11.column;
12767
- return String(record11.status ?? "");
12875
+ function recordState(record12) {
12876
+ if (record12.column) return record12.column === "todo" ? "ready" : record12.column;
12877
+ return String(record12.status ?? "");
12768
12878
  }
12769
12879
  function eventRecord(event) {
12770
12880
  return event.payload.payload;
12771
12881
  }
12772
12882
  function eventLabel(event) {
12773
- const record11 = eventRecord(event);
12774
- if (record11) return String(record11.title ?? event.payload.entityId);
12883
+ const record12 = eventRecord(event);
12884
+ if (record12) return String(record12.title ?? event.payload.entityId);
12775
12885
  const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
12776
12886
  return body || event.payload.entityId;
12777
12887
  }
@@ -12779,10 +12889,10 @@ function report3(ctx, parsed, result) {
12779
12889
  if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
12780
12890
  else if (parsed.options.jsonl !== true && result.found) {
12781
12891
  for (const event of result.events ?? []) {
12782
- const record11 = eventRecord(event);
12783
- const state2 = record11 ? recordState(record11) : "comment";
12892
+ const record12 = eventRecord(event);
12893
+ const state2 = record12 ? recordState(record12) : "comment";
12784
12894
  ctx.out.log(
12785
- `${event.id} ${event.type} ${state2}${record11?.revision ? `; r${record11.revision}` : ""} ${eventLabel(event)}`
12895
+ `${event.id} ${event.type} ${state2}${record12?.revision ? `; r${record12.revision}` : ""} ${eventLabel(event)}`
12786
12896
  );
12787
12897
  }
12788
12898
  }
@@ -12856,8 +12966,8 @@ async function pmWatch(ctx, parsed) {
12856
12966
  }
12857
12967
  firstSuccess = false;
12858
12968
  const matching = current.events.filter((event) => {
12859
- const record11 = eventRecord(event);
12860
- const state2 = record11 ? recordState(record11).toLowerCase() : "";
12969
+ const record12 = eventRecord(event);
12970
+ const state2 = record12 ? recordState(record12).toLowerCase() : "";
12861
12971
  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);
12862
12972
  });
12863
12973
  for (const event of matching) {
@@ -13288,17 +13398,17 @@ async function platformStatus(parsed, deps) {
13288
13398
  }
13289
13399
  }
13290
13400
  function isPlatformStatus(value2) {
13291
- if (!record7(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
13292
- if (!record7(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
13293
- if (!record7(value2.catalog) || !record7(value2.summary)) return false;
13401
+ if (!record8(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
13402
+ if (!record8(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
13403
+ if (!record8(value2.catalog) || !record8(value2.summary)) return false;
13294
13404
  return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
13295
13405
  }
13296
13406
  function apiMessage(value2) {
13297
- if (!record7(value2)) return "request failed";
13298
- const error = record7(value2.error) ? value2.error : value2;
13407
+ if (!record8(value2)) return "request failed";
13408
+ const error = record8(value2.error) ? value2.error : value2;
13299
13409
  return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
13300
13410
  }
13301
- function record7(value2) {
13411
+ function record8(value2) {
13302
13412
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
13303
13413
  }
13304
13414
 
@@ -13339,7 +13449,7 @@ function statusVerdict(reads) {
13339
13449
  severity: "degraded"
13340
13450
  });
13341
13451
  }
13342
- const performance = record8(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
13452
+ const performance = record9(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
13343
13453
  if (performance?.status === "unavailable") {
13344
13454
  reasons.push({
13345
13455
  source: "liveSync",
@@ -13420,7 +13530,7 @@ function statusVerdict(reads) {
13420
13530
  reasons
13421
13531
  };
13422
13532
  }
13423
- function record8(value2) {
13533
+ function record9(value2) {
13424
13534
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
13425
13535
  }
13426
13536
  function numeric2(value2) {
@@ -13448,7 +13558,7 @@ function printO11yStatus(status, out) {
13448
13558
  out.log(
13449
13559
  `o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
13450
13560
  );
13451
- const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record9) : [];
13561
+ const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record10) : [];
13452
13562
  const requests = routes.reduce(
13453
13563
  (total, row) => total + numeric3(row.requests),
13454
13564
  0
@@ -13460,39 +13570,39 @@ function printO11yStatus(status, out) {
13460
13570
  out.log(
13461
13571
  `application ${status.application.httpStatus} ${requests} requests ${errors} errors`
13462
13572
  );
13463
- const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record9) : [];
13573
+ const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record10) : [];
13464
13574
  out.log(
13465
13575
  `application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
13466
13576
  (row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
13467
13577
  ).join(", ") : "none observed"}`
13468
13578
  );
13469
13579
  out.log(liveSyncLine(status.liveSync));
13470
- const canaryDurations = record9(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
13580
+ const canaryDurations = record10(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
13471
13581
  out.log(
13472
13582
  `canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
13473
13583
  );
13474
- const collectorIngest = record9(status.collector.body.ingest) ? status.collector.body.ingest : {};
13475
- const collectorStorage = record9(collectorIngest.storage) ? collectorIngest.storage : {};
13584
+ const collectorIngest = record10(status.collector.body.ingest) ? status.collector.body.ingest : {};
13585
+ const collectorStorage = record10(collectorIngest.storage) ? collectorIngest.storage : {};
13476
13586
  out.log(
13477
13587
  `collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
13478
13588
  );
13479
- const providerMetrics = record9(status.provider.body.metrics) ? status.provider.body.metrics : {};
13480
- const providerCapacity = record9(status.provider.body.capacity) ? status.provider.body.capacity : {};
13481
- const workerMemory = record9(providerCapacity.memory) ? providerCapacity.memory : {};
13589
+ const providerMetrics = record10(status.provider.body.metrics) ? status.provider.body.metrics : {};
13590
+ const providerCapacity = record10(status.provider.body.capacity) ? status.provider.body.capacity : {};
13591
+ const workerMemory = record10(providerCapacity.memory) ? providerCapacity.memory : {};
13482
13592
  out.log(
13483
13593
  `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`
13484
13594
  );
13485
13595
  for (const line2 of providerCapacityLines(status.providerCapacity)) {
13486
13596
  out.log(line2);
13487
13597
  }
13488
- const coverage = record9(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
13489
- const coverageCounts = record9(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
13490
- const coverageBudget = record9(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
13598
+ const coverage = record10(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
13599
+ const coverageCounts = record10(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
13600
+ const coverageBudget = record10(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
13491
13601
  out.log(
13492
13602
  `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`
13493
13603
  );
13494
13604
  const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
13495
- const providerFreshness = record9(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
13605
+ const providerFreshness = record10(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
13496
13606
  out.log(
13497
13607
  `cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
13498
13608
  );
@@ -13501,17 +13611,17 @@ function printO11yStatus(status, out) {
13501
13611
  );
13502
13612
  }
13503
13613
  function providerCapacityLines(read3) {
13504
- const resources = record9(read3.body.resources) ? read3.body.resources : {};
13505
- const durableObjects = record9(resources.durableObjects) ? resources.durableObjects : {};
13506
- const periodic = record9(durableObjects.periodic) ? durableObjects.periodic : {};
13507
- const storage = record9(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
13508
- const d1 = record9(resources.d1) ? resources.d1 : {};
13509
- const d1Activity = record9(d1.activity) ? d1.activity : {};
13510
- const d1Storage = record9(d1.storage) ? d1.storage : {};
13511
- const d1Latency = record9(d1Activity.latency) ? d1Activity.latency : {};
13512
- const r2 = record9(resources.r2) ? resources.r2 : {};
13513
- const r2Operations = record9(r2.operations) ? r2.operations : {};
13514
- const r2Storage = record9(r2.storage) ? r2.storage : {};
13614
+ const resources = record10(read3.body.resources) ? read3.body.resources : {};
13615
+ const durableObjects = record10(resources.durableObjects) ? resources.durableObjects : {};
13616
+ const periodic = record10(durableObjects.periodic) ? durableObjects.periodic : {};
13617
+ const storage = record10(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
13618
+ const d1 = record10(resources.d1) ? resources.d1 : {};
13619
+ const d1Activity = record10(d1.activity) ? d1.activity : {};
13620
+ const d1Storage = record10(d1.storage) ? d1.storage : {};
13621
+ const d1Latency = record10(d1Activity.latency) ? d1Activity.latency : {};
13622
+ const r2 = record10(resources.r2) ? resources.r2 : {};
13623
+ const r2Operations = record10(r2.operations) ? r2.operations : {};
13624
+ const r2Storage = record10(r2.storage) ? r2.storage : {};
13515
13625
  const status = String(
13516
13626
  read3.body.status ?? read3.body.error ?? "unavailable"
13517
13627
  );
@@ -13522,11 +13632,11 @@ function providerCapacityLines(read3) {
13522
13632
  ];
13523
13633
  }
13524
13634
  function liveSyncLine(read3) {
13525
- const performance = record9(read3.body.performance) ? read3.body.performance : {};
13526
- const commitToSend = record9(performance.commitToSend) ? performance.commitToSend : {};
13635
+ const performance = record10(read3.body.performance) ? read3.body.performance : {};
13636
+ const commitToSend = record10(performance.commitToSend) ? performance.commitToSend : {};
13527
13637
  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`;
13528
13638
  }
13529
- function record9(value2) {
13639
+ function record10(value2) {
13530
13640
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
13531
13641
  }
13532
13642
  function numeric3(value2) {
@@ -13864,7 +13974,7 @@ async function monitorCommand(parsed, deps = {}) {
13864
13974
  if (action2 === "plan" || action2 === "apply") {
13865
13975
  const desired = monitoringWireConfig(context.cfg, env);
13866
13976
  const live = await request2(`${base}?env=${encodeURIComponent(env)}`, { headers }, doFetch);
13867
- const currentRevision = record10(live.config) ? string(live.config.revision) : null;
13977
+ const currentRevision = record11(live.config) ? string(live.config.revision) : null;
13868
13978
  const changed = currentRevision !== desired.revision;
13869
13979
  const plan = {
13870
13980
  schemaVersion: 1,
@@ -13909,7 +14019,7 @@ async function monitorCommand(parsed, deps = {}) {
13909
14019
  headers
13910
14020
  }, doFetch);
13911
14021
  emit3(result2, jsonOutput, out, () => {
13912
- const run = record10(result2.run) ? result2.run : {};
14022
+ const run = record11(result2.run) ? result2.run : {};
13913
14023
  out.log(`monitor run ${appId}/${env}/${probeId}: ${string(run.outcome) ?? "unknown"}${run.failure_code ? ` (${String(run.failure_code)})` : ""}`);
13914
14024
  });
13915
14025
  return;
@@ -13934,12 +14044,12 @@ async function request2(url, init, doFetch) {
13934
14044
  let body = {};
13935
14045
  try {
13936
14046
  const parsed = text4 ? JSON.parse(text4) : {};
13937
- body = record10(parsed) ? parsed : { value: parsed };
14047
+ body = record11(parsed) ? parsed : { value: parsed };
13938
14048
  } catch {
13939
14049
  body = { message: text4.slice(0, 500) };
13940
14050
  }
13941
14051
  if (!response2.ok) {
13942
- const error = record10(body.error) ? body.error : body;
14052
+ const error = record11(body.error) ? body.error : body;
13943
14053
  throw new Error(string(error.message) ?? string(error.code) ?? `monitor request failed (${response2.status})`);
13944
14054
  }
13945
14055
  return body;
@@ -13947,7 +14057,7 @@ async function request2(url, init, doFetch) {
13947
14057
  function printRead(action2, appId, env, result, out) {
13948
14058
  if (action2 === "status") {
13949
14059
  out.log(`monitor status ${appId}/${env}: ${String(result.overall ?? (result.configured === false ? "not configured" : "unknown"))}`);
13950
- const slos = Array.isArray(result.slos) ? result.slos.filter(record10) : [];
14060
+ const slos = Array.isArray(result.slos) ? result.slos.filter(record11) : [];
13951
14061
  for (const slo of slos) out.log(`slo ${String(slo.id)} ${String(slo.state)} ${percent(slo.observed)} observed ${percent(slo.budgetRemaining)} budget remaining`);
13952
14062
  const incidents = Array.isArray(result.openIncidents) ? result.openIncidents.length : 0;
13953
14063
  const gaps = Array.isArray(result.monitoringGaps) ? result.monitoringGaps.length : 0;
@@ -13956,20 +14066,20 @@ function printRead(action2, appId, env, result, out) {
13956
14066
  return;
13957
14067
  }
13958
14068
  if (action2 === "incidents") {
13959
- const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record10) : [];
14069
+ const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record11) : [];
13960
14070
  out.log(`monitor incidents ${appId}/${env}: ${incidents.length}`);
13961
14071
  for (const incident2 of incidents) out.log(`${String(incident2.state)} ${String(incident2.kind)} ${String(incident2.slo_id)} ${new Date(Number(incident2.opened_at)).toISOString()}`);
13962
14072
  return;
13963
14073
  }
13964
14074
  out.log(`monitor report ${appId}/${env}: ${String(result.period)} ${String(result.overall)}`);
13965
- const probes = Array.isArray(result.probes) ? result.probes.filter(record10) : [];
14075
+ const probes = Array.isArray(result.probes) ? result.probes.filter(record11) : [];
13966
14076
  for (const probe of probes) out.log(`probe ${String(probe.id)} ${Number(probe.good)} good ${Number(probe.bad)} bad ${Number(probe.unknown)} unknown`);
13967
14077
  }
13968
14078
  function emit3(value2, json, out, human) {
13969
14079
  if (json) out.log(JSON.stringify(value2, null, 2));
13970
14080
  else human();
13971
14081
  }
13972
- function record10(value2) {
14082
+ function record11(value2) {
13973
14083
  return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
13974
14084
  }
13975
14085
  function string(value2) {
@@ -14460,6 +14570,122 @@ async function provision(options) {
14460
14570
  }
14461
14571
  }
14462
14572
 
14573
+ // src/promotion-command.ts
14574
+ import { readFile as readFile5 } from "fs/promises";
14575
+ import { createAppsClient as createAppsClient4 } from "@odla-ai/apps";
14576
+
14577
+ // src/security-command-context.ts
14578
+ async function hostedSecurityContext(parsed, dependencies) {
14579
+ const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
14580
+ const cfg = await loadProjectConfig(configPath);
14581
+ const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
14582
+ if (!env || !cfg.envs.includes(env)) {
14583
+ throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
14584
+ }
14585
+ const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
14586
+ if (platformAudience(cfg.platformUrl) !== platform) {
14587
+ throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
14588
+ }
14589
+ const doFetch = dependencies.fetch ?? fetch;
14590
+ const stdout = dependencies.stdout ?? console;
14591
+ const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
14592
+ const token = await getDeveloperToken(
14593
+ cfg,
14594
+ { configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
14595
+ doFetch,
14596
+ stdout,
14597
+ { optionalProjectCapabilities: ["app.manage"] }
14598
+ );
14599
+ return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
14600
+ }
14601
+ function requiredSecurityPositional(parsed, index, label) {
14602
+ const value2 = parsed.positionals[index];
14603
+ if (!value2) throw new Error(`${label} is required`);
14604
+ return value2;
14605
+ }
14606
+ function securityProfile(value2) {
14607
+ if (value2 === void 0) return void 0;
14608
+ if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
14609
+ throw new Error("--profile must be odla, cloudflare-app, or generic");
14610
+ }
14611
+
14612
+ // src/promotion-command.ts
14613
+ var render = (value2, json, out) => {
14614
+ if (json) out.log(JSON.stringify(value2));
14615
+ else out.log(JSON.stringify(value2, null, 2));
14616
+ };
14617
+ async function promotionCommand(parsed, dependencies) {
14618
+ const action2 = parsed.positionals[1];
14619
+ if (!action2 || !["plan", "inspect", "status", "apply", "rollback", "next", "record", "cancel"].includes(action2)) rejectWord(["promotion"], action2);
14620
+ assertArgs(parsed, ["config", "env", "platform", "email", "open", "json", "file", "approval", "idempotency-key"], 5);
14621
+ const context = await hostedSecurityContext(parsed, dependencies);
14622
+ const client = createAppsClient4({
14623
+ token: context.token,
14624
+ fetcher: { fetch: (request3, init) => {
14625
+ const internal = new URL(typeof request3 === "string" ? request3 : request3.url);
14626
+ return context.fetch(`${context.platform}${internal.pathname}${internal.search}`, init);
14627
+ } }
14628
+ });
14629
+ const json = parsed.options.json === true;
14630
+ if (action2 === "plan") {
14631
+ if (parsed.positionals[2]) throw new Error("promotion plan takes --file, not a positional plan id");
14632
+ const file = stringOpt(parsed.options.file);
14633
+ if (!file) throw new Error("--file <promotion-plan.json> is required");
14634
+ const source = JSON.parse(await readFile5(file, "utf8"));
14635
+ const plan2 = await client.createPromotionReleasePlan(context.appId, source);
14636
+ render(plan2, json, context.stdout);
14637
+ return;
14638
+ }
14639
+ const planId = requiredSecurityPositional(parsed, 2, "plan id");
14640
+ if (action2 === "inspect") {
14641
+ if (parsed.positionals[3]) throw new Error("promotion inspect takes only a plan id");
14642
+ const plan2 = await client.getPromotionReleasePlan(context.appId, planId);
14643
+ if (!plan2) throw new Error(`promotion release ${planId} was not found`);
14644
+ render(plan2, json, context.stdout);
14645
+ return;
14646
+ }
14647
+ if (action2 === "status") {
14648
+ if (parsed.positionals[3]) throw new Error("promotion status takes only a plan id");
14649
+ const status = await client.getPromotionReleaseStatus(context.appId, planId);
14650
+ if (!status) throw new Error(`promotion release ${planId} was not found`);
14651
+ render(status, json, context.stdout);
14652
+ return;
14653
+ }
14654
+ if (action2 === "next" || action2 === "cancel" || action2 === "record") {
14655
+ const operationId = requiredSecurityPositional(parsed, 3, "operation id");
14656
+ if (action2 === "next") {
14657
+ if (parsed.positionals[4]) throw new Error("promotion next takes only plan and operation ids");
14658
+ render(await client.claimPromotionReleaseWork(context.appId, planId, operationId), json, context.stdout);
14659
+ return;
14660
+ }
14661
+ if (action2 === "cancel") {
14662
+ if (parsed.positionals[4]) throw new Error("promotion cancel takes only plan and operation ids");
14663
+ render(await client.cancelPromotionReleaseOperation(context.appId, planId, operationId), json, context.stdout);
14664
+ return;
14665
+ }
14666
+ const stepId = requiredSecurityPositional(parsed, 4, "step id");
14667
+ const file = stringOpt(parsed.options.file);
14668
+ if (!file) throw new Error("--file <step-receipt.json> is required");
14669
+ const receipt = JSON.parse(await readFile5(file, "utf8"));
14670
+ render(await client.recordPromotionReleaseWork(context.appId, planId, operationId, stepId, receipt), json, context.stdout);
14671
+ return;
14672
+ }
14673
+ const approvalId = stringOpt(parsed.options.approval);
14674
+ if (parsed.positionals[3]) throw new Error(`promotion ${action2} takes only a plan id`);
14675
+ if (!approvalId) throw new Error(`--approval <approval-id> is required for promotion ${action2}`);
14676
+ const plan = await client.getPromotionReleasePlan(context.appId, planId);
14677
+ if (!plan) throw new Error(`promotion release ${planId} was not found`);
14678
+ if (plan.state !== "reviewable") throw new Error(`promotion release is ${plan.state}: ${plan.blockers.join("; ")}`);
14679
+ const operationAction = action2;
14680
+ const operation = await client.requestPromotionReleaseOperation(context.appId, planId, operationAction, {
14681
+ approvalId,
14682
+ planDigest: plan.planDigest,
14683
+ targetSetDigest: plan.targetSetDigest,
14684
+ idempotencyKey: stringOpt(parsed.options["idempotency-key"]) ?? `${operationAction}:${planId}`
14685
+ });
14686
+ render(operation, json, context.stdout);
14687
+ }
14688
+
14463
14689
  // src/record.ts
14464
14690
  import { appendFileSync } from "fs";
14465
14691
  import process18 from "process";
@@ -14747,7 +14973,7 @@ async function bySlug(ctx, slug) {
14747
14973
  "GET",
14748
14974
  `/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
14749
14975
  );
14750
- const filtered = page2.records.find((record11) => record11.slug === slug);
14976
+ const filtered = page2.records.find((record12) => record12.slug === slug);
14751
14977
  if (filtered) return filtered;
14752
14978
  const limit = 100;
14753
14979
  for (let offset = 0; ; offset += limit) {
@@ -14756,7 +14982,7 @@ async function bySlug(ctx, slug) {
14756
14982
  "GET",
14757
14983
  `/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
14758
14984
  );
14759
- const found = fallback.records.find((record11) => record11.slug === slug);
14985
+ const found = fallback.records.find((record12) => record12.slug === slug);
14760
14986
  if (found) return found;
14761
14987
  if (!fallback.records.length || offset + fallback.records.length >= fallback.total) break;
14762
14988
  }
@@ -15556,41 +15782,6 @@ async function runbookCommand(parsed, deps = {}) {
15556
15782
  }
15557
15783
  }
15558
15784
 
15559
- // src/security-command-context.ts
15560
- async function hostedSecurityContext(parsed, dependencies) {
15561
- const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
15562
- const cfg = await loadProjectConfig(configPath);
15563
- const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
15564
- if (!env || !cfg.envs.includes(env)) {
15565
- throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
15566
- }
15567
- const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
15568
- if (platformAudience(cfg.platformUrl) !== platform) {
15569
- throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
15570
- }
15571
- const doFetch = dependencies.fetch ?? fetch;
15572
- const stdout = dependencies.stdout ?? console;
15573
- const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
15574
- const token = await getDeveloperToken(
15575
- cfg,
15576
- { configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
15577
- doFetch,
15578
- stdout,
15579
- { optionalProjectCapabilities: ["app.manage"] }
15580
- );
15581
- return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
15582
- }
15583
- function requiredSecurityPositional(parsed, index, label) {
15584
- const value2 = parsed.positionals[index];
15585
- if (!value2) throw new Error(`${label} is required`);
15586
- return value2;
15587
- }
15588
- function securityProfile(value2) {
15589
- if (value2 === void 0) return void 0;
15590
- if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
15591
- throw new Error("--profile must be odla, cloudflare-app, or generic");
15592
- }
15593
-
15594
15785
  // src/security-command-output.ts
15595
15786
  function printHostedSecurityPlan(out, plan, appId) {
15596
15787
  out.log(`Hosted security plan for ${appId}/${plan.env}: ${plan.ready ? "ready" : "not ready"}`);
@@ -16328,6 +16519,10 @@ async function dispatchCli(argv, dependencies) {
16328
16519
  await securityCommand(parsed, runtime);
16329
16520
  return;
16330
16521
  }
16522
+ if (command === "promotion") {
16523
+ await promotionCommand(parsed, runtime);
16524
+ return;
16525
+ }
16331
16526
  if (command === "brand") {
16332
16527
  await brandCommand(parsed, runtime);
16333
16528
  return;
@@ -16492,4 +16687,4 @@ export {
16492
16687
  isTerminalHostedSecurityStatus,
16493
16688
  runCli
16494
16689
  };
16495
- //# sourceMappingURL=chunk-Q4BGAUEH.js.map
16690
+ //# sourceMappingURL=chunk-BD5KIG63.js.map