@code-partner/codepipe-hub 0.14.1-dev.399.g96d6330f → 0.14.1-dev.404.g2e5e29f1

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.
@@ -27,7 +27,7 @@
27
27
  localStorage.getItem('theme') ?? 'dark'
28
28
  );
29
29
  </script>
30
- <script type="module" crossorigin src="/assets/index-Cpy8aKZb.js"></script>
30
+ <script type="module" crossorigin src="/assets/index-T-kK12wa.js"></script>
31
31
  <link rel="stylesheet" crossorigin href="/assets/index-CD8JrtmW.css">
32
32
  </head>
33
33
  <body>
package/hub/dist/index.js CHANGED
@@ -13,7 +13,7 @@ var PLATFORM_VERSION;
13
13
  var init_version_generated = __esm({
14
14
  "../packages/shared/dist/version.generated.js"() {
15
15
  "use strict";
16
- PLATFORM_VERSION = "0.14.1-dev.399.g96d6330f";
16
+ PLATFORM_VERSION = "0.14.1-dev.404.g2e5e29f1";
17
17
  }
18
18
  });
19
19
 
@@ -394,22 +394,25 @@ var init_job = __esm({
394
394
  });
395
395
 
396
396
  // ../packages/shared/dist/llm-models.js
397
+ function modelForHarness(model, harness) {
398
+ if (!harness)
399
+ return model;
400
+ return HARNESS_MODEL_LADDER[harness]?.[model] ?? model;
401
+ }
397
402
  function parseWorkflowPreset(v) {
398
403
  return typeof v === "string" && WORKFLOW_PRESETS.includes(v.trim()) ? v.trim() : null;
399
404
  }
400
405
  function emptyModelConfig() {
401
406
  return { default: null, roles: {} };
402
407
  }
403
- function resolveModelForRole(cfg, role, preset = DEFAULT_WORKFLOW_PRESET) {
408
+ function resolveModelForRole(cfg, role, preset = DEFAULT_WORKFLOW_PRESET, harness) {
404
409
  const explicit = cfg?.roles?.[role];
405
410
  if (typeof explicit === "string" && explicit.trim().length > 0)
406
411
  return explicit.trim();
407
412
  if (cfg?.default && cfg.default.trim().length > 0)
408
413
  return cfg.default.trim();
409
414
  const presetModel = PRESET_ROLE_MODELS[preset]?.[role];
410
- if (presetModel)
411
- return presetModel;
412
- return RECOMMENDED_ROLE_MODELS[role] ?? FALLBACK_MODEL;
415
+ return modelForHarness(presetModel ?? RECOMMENDED_ROLE_MODELS[role] ?? FALLBACK_MODEL, harness);
413
416
  }
414
417
  function resolveProvider(cfg) {
415
418
  const p = cfg?.provider;
@@ -532,7 +535,7 @@ function deserializeModelConfig(json) {
532
535
  return emptyModelConfig();
533
536
  }
534
537
  }
535
- var DEFAULT_LLM_PROVIDER, LLM_PROVIDER_SUGGESTIONS, MODEL_OPUS, MODEL_SONNET, MODEL_HAIKU, MODEL_ALIAS_SUGGESTIONS, BUILD_DISCOVERY, CODE_REVIEW_VERIFY, MODEL_SUB_ROLES, ALL_MODEL_ROLE_KEYS, RECOMMENDED_ROLE_MODELS, FALLBACK_MODEL, WORKFLOW_PRESETS, DEFAULT_WORKFLOW_PRESET, PRESET_ROLE_MODELS, DEFAULT_REGIMEN_ALIASES, EFFORT_LEVELS, EFFORT_LEVEL_SUGGESTIONS, RECOMMENDED_ROLE_EFFORT;
538
+ var DEFAULT_LLM_PROVIDER, LLM_PROVIDER_SUGGESTIONS, MODEL_OPUS, MODEL_SONNET, MODEL_HAIKU, MODEL_ALIAS_SUGGESTIONS, BUILD_DISCOVERY, CODE_REVIEW_VERIFY, MODEL_SUB_ROLES, ALL_MODEL_ROLE_KEYS, RECOMMENDED_ROLE_MODELS, FALLBACK_MODEL, HARNESS_MODEL_LADDER, WORKFLOW_PRESETS, DEFAULT_WORKFLOW_PRESET, PRESET_ROLE_MODELS, DEFAULT_REGIMEN_ALIASES, EFFORT_LEVELS, EFFORT_LEVEL_SUGGESTIONS, RECOMMENDED_ROLE_EFFORT;
536
539
  var init_llm_models = __esm({
537
540
  "../packages/shared/dist/llm-models.js"() {
538
541
  "use strict";
@@ -604,6 +607,13 @@ var init_llm_models = __esm({
604
607
  testing: MODEL_OPUS
605
608
  };
606
609
  FALLBACK_MODEL = MODEL_OPUS;
610
+ HARNESS_MODEL_LADDER = {
611
+ codex: {
612
+ [MODEL_OPUS]: "gpt-5.6-sol",
613
+ [MODEL_SONNET]: "gpt-5.6-terra",
614
+ [MODEL_HAIKU]: "gpt-5.6-terra"
615
+ }
616
+ };
607
617
  WORKFLOW_PRESETS = ["express", "balanced", "full"];
608
618
  DEFAULT_WORKFLOW_PRESET = "full";
609
619
  PRESET_ROLE_MODELS = {
@@ -711,6 +721,78 @@ var init_actions = __esm({
711
721
  }
712
722
  });
713
723
 
724
+ // ../packages/shared/dist/capability-registry.js
725
+ function harnessesForBackend(llm) {
726
+ switch (llm) {
727
+ case "claude-headless":
728
+ return ["claude-code"];
729
+ case "codex":
730
+ return ["codex"];
731
+ case "mock":
732
+ return ["mock", "claude-code", "codex"];
733
+ default:
734
+ return [];
735
+ }
736
+ }
737
+ var CAPABILITY_REGISTRY, SELECTABLE_HARNESSES;
738
+ var init_capability_registry = __esm({
739
+ "../packages/shared/dist/capability-registry.js"() {
740
+ "use strict";
741
+ CAPABILITY_REGISTRY = [
742
+ {
743
+ harness: "claude-code",
744
+ endpoint: "anthropic-managed",
745
+ modelPattern: "*",
746
+ credentialKinds: ["subscription_oauth", "api_key"],
747
+ status: "supported",
748
+ capabilities: {
749
+ headless: true,
750
+ // Claude Code can resume, but the product deliberately does not glue
751
+ // sessions across jobs (DEV-682) — so no resume is advertised.
752
+ resume: false,
753
+ streamEvents: true,
754
+ usage: "tokens",
755
+ toolPolicy: "vendor"
756
+ },
757
+ testedWith: { runtimeVersion: "claude-code 2.x (stream-json)", protocolVersion: 7 }
758
+ },
759
+ {
760
+ harness: "codex",
761
+ endpoint: "openai-managed",
762
+ modelPattern: "*",
763
+ credentialKinds: ["subscription_oauth", "api_key"],
764
+ // EXPERIMENTAL until a cross-harness eval run compares it with the Claude
765
+ // path on the same corpus (§12) — claiming "supported" before that would
766
+ // be a promise nothing backs.
767
+ status: "experimental",
768
+ capabilities: {
769
+ headless: true,
770
+ // The CLI can resume a session, but the adapter exposes no resume() and
771
+ // the product does not glue sessions across JOBS — the registry must
772
+ // say what the ADAPTER can do, or a job requiring resume would be
773
+ // matched to a worker that cannot. (DEV-897 uses `exec resume` WITHIN a
774
+ // chat job; that is one session, not glue between jobs.)
775
+ resume: false,
776
+ streamEvents: true,
777
+ usage: "quota",
778
+ toolPolicy: "vendor"
779
+ },
780
+ testedWith: { runtimeVersion: "codex-cli 0.152 (exec --json, exec resume)", protocolVersion: 7 }
781
+ },
782
+ {
783
+ harness: "mock",
784
+ endpoint: "none",
785
+ modelPattern: "*",
786
+ credentialKinds: [],
787
+ status: "supported",
788
+ capabilities: { headless: true, resume: false, streamEvents: false, usage: "none", toolPolicy: "vendor" },
789
+ testedWith: { runtimeVersion: "built-in stub", protocolVersion: 7 }
790
+ }
791
+ ];
792
+ SELECTABLE_HARNESSES = CAPABILITY_REGISTRY.filter((e) => e.harness !== "mock").map((e) => e.harness);
793
+ }
794
+ });
795
+
714
796
  // ../packages/shared/dist/project.js
715
797
  function slugify(input) {
716
798
  const lowered = input.toLowerCase();
@@ -1162,7 +1244,9 @@ var init_project_settings = __esm({
1162
1244
  "../packages/shared/dist/project-settings.js"() {
1163
1245
  "use strict";
1164
1246
  init_actions();
1247
+ init_capability_registry();
1165
1248
  init_git_flow();
1249
+ init_job();
1166
1250
  init_llm_models();
1167
1251
  init_project_map();
1168
1252
  init_project_language();
@@ -1313,10 +1397,58 @@ var init_project_settings = __esm({
1313
1397
  // DEV-896: the old text ("currently only claude is available") outlived
1314
1398
  // its truth and read as "there is nothing to choose" — the one thing
1315
1399
  // help must never say about a live control.
1316
- help: "The agent harness every role runs on by default. Per-role overrides (and the fallback agent) live in the shared config's routing block."
1400
+ // DEV-934: per-role agents are a control now, not a YAML instruction
1401
+ // point at it, and say which of the two wins, because they live in
1402
+ // different files and one is personal.
1403
+ help: "The agent every role runs on by default \u2014 your own choice, not the team's. \xABAgent by role\xBB below is shared and outranks it."
1317
1404
  }
1318
1405
  ],
1319
1406
  groups: [
1407
+ {
1408
+ // DEV-934: the model was pickable per role from the start (below) and
1409
+ // the AGENT was not — a project mixing vendors (analysis on one,
1410
+ // implementation on the other) could only be expressed by hand-editing
1411
+ // project.yaml, which put the whole DEV-759 routing feature out of
1412
+ // reach of anyone who does not edit YAML.
1413
+ id: "routing",
1414
+ label: "Agent by role",
1415
+ help: "Which agent runs which stage. Shared with the team (project.yaml). Empty \u21D2 the role follows the default below it, then your own provider setting above.",
1416
+ fields: [
1417
+ {
1418
+ id: "routing.default.harness",
1419
+ file: "project.yaml",
1420
+ path: ["routing", "default", "harness"],
1421
+ type: "enum",
1422
+ label: "default (whole project)",
1423
+ nullable: true,
1424
+ suggestions: [...SELECTABLE_HARNESSES],
1425
+ help: "The team-wide default. Set, it outranks each member's own provider setting."
1426
+ },
1427
+ ...ALL_JOB_ROLES.map((role) => ({
1428
+ id: `routing.${role}.harness`,
1429
+ file: "project.yaml",
1430
+ path: ["routing", role, "harness"],
1431
+ type: "enum",
1432
+ label: role,
1433
+ nullable: true,
1434
+ suggestions: [...SELECTABLE_HARNESSES],
1435
+ help: "Empty \u21D2 this role follows the default."
1436
+ })),
1437
+ {
1438
+ id: "routing.fallback.harness",
1439
+ file: "project.yaml",
1440
+ path: ["routing", "fallback", "harness"],
1441
+ type: "enum",
1442
+ label: "fallback (retry after a vendor failure)",
1443
+ nullable: true,
1444
+ suggestions: [...SELECTABLE_HARNESSES],
1445
+ // DEV-759 §9: the ONE sanctioned way to change vendor mid-task, and
1446
+ // only for infrastructure trouble — a wrong result is a rework, not
1447
+ // a reason to try the other vendor.
1448
+ help: "Where a retry goes when the vendor itself was unusable (rate limit, 5xx). Empty \u21D2 retries stay on the same agent."
1449
+ }
1450
+ ]
1451
+ },
1320
1452
  {
1321
1453
  id: "llm.models",
1322
1454
  label: "Models by role",
@@ -2988,25 +3120,6 @@ var init_credential_broker = __esm({
2988
3120
  }
2989
3121
  });
2990
3122
 
2991
- // ../packages/shared/dist/capability-registry.js
2992
- function harnessesForBackend(llm) {
2993
- switch (llm) {
2994
- case "claude-headless":
2995
- return ["claude-code"];
2996
- case "codex":
2997
- return ["codex"];
2998
- case "mock":
2999
- return ["mock", "claude-code", "codex"];
3000
- default:
3001
- return [];
3002
- }
3003
- }
3004
- var init_capability_registry = __esm({
3005
- "../packages/shared/dist/capability-registry.js"() {
3006
- "use strict";
3007
- }
3008
- });
3009
-
3010
3123
  // ../packages/shared/dist/structured-llm.js
3011
3124
  var init_structured_llm = __esm({
3012
3125
  "../packages/shared/dist/structured-llm.js"() {
@@ -6457,6 +6570,17 @@ function mergeSandboxCapabilities(stored, live) {
6457
6570
  }
6458
6571
  return merged;
6459
6572
  }
6573
+ async function claimCapabilities(db, sandboxId, stored, live) {
6574
+ const merged = mergeSandboxCapabilities(stored, live);
6575
+ if (JSON.stringify(merged) !== JSON.stringify(stored)) {
6576
+ await db.run(
6577
+ `UPDATE sandboxes SET capabilities_json = ? WHERE id = ?`,
6578
+ JSON.stringify(merged),
6579
+ sandboxId
6580
+ );
6581
+ }
6582
+ return merged;
6583
+ }
6460
6584
  async function refreshSandboxCapabilities(db, sandboxId, live) {
6461
6585
  const row = await db.get(
6462
6586
  `SELECT capabilities_json as capabilitiesJson FROM sandboxes WHERE id = ?`,
@@ -10691,6 +10815,63 @@ async function heldByTrancheDeferral(db) {
10691
10815
  );
10692
10816
  return rows.map((r) => r.id);
10693
10817
  }
10818
+ async function resolveJobAxes(db, projectId, role, taskWorkflowPreset) {
10819
+ const mc = await loadModelConfig(db, projectId);
10820
+ const provider = resolveProvider(mc);
10821
+ const { preset: projectPreset } = await loadWorkflowSettings(db, projectId);
10822
+ const workflowPreset = parseWorkflowPreset(taskWorkflowPreset) ?? projectPreset;
10823
+ const routing = resolveRoutingForRole(await loadRoutingConfig(db, projectId), role, {
10824
+ defaultHarness: harnessForProvider(provider)
10825
+ });
10826
+ return {
10827
+ routing,
10828
+ provider: HARNESS_PROVIDER[routing.harness] ?? provider,
10829
+ model: routing.model || resolveModelForRole(mc, role, workflowPreset, routing.harness),
10830
+ modelDiscovery: role === "build" ? resolveModelForRole(mc, "build-discovery", workflowPreset, routing.harness) : void 0
10831
+ };
10832
+ }
10833
+ function axesOnSpec(axes) {
10834
+ return {
10835
+ harness: axes.routing.harness,
10836
+ model: axes.model,
10837
+ endpoint: axes.routing.endpoint,
10838
+ account: axes.routing.account,
10839
+ // The harness a run needs is a capability of the worker (DEV-758 matches
10840
+ // on these).
10841
+ requiredCapabilities: [`harness:${axes.routing.harness}`, `provider:${axes.provider}`]
10842
+ };
10843
+ }
10844
+ async function restampQueuedJobs(db, projectId) {
10845
+ const rows = await db.all(
10846
+ `SELECT id, role, task_id AS taskId, spec_json AS specJson
10847
+ FROM jobs WHERE project_id = ? AND state = 'queued'`,
10848
+ projectId
10849
+ );
10850
+ let restamped = 0;
10851
+ for (const row of rows) {
10852
+ let spec;
10853
+ try {
10854
+ spec = JSON.parse(row.specJson);
10855
+ } catch {
10856
+ continue;
10857
+ }
10858
+ if (!spec.executionSpec) continue;
10859
+ const task = await getTask(db, row.taskId);
10860
+ const axes = await resolveJobAxes(db, projectId, row.role, task?.workflowPreset);
10861
+ const next = {
10862
+ ...spec,
10863
+ model: axes.model,
10864
+ provider: axes.provider,
10865
+ ...axes.modelDiscovery ? { modelDiscovery: axes.modelDiscovery } : {},
10866
+ executionSpec: { ...spec.executionSpec, ...axesOnSpec(axes) }
10867
+ };
10868
+ const nextJson = JSON.stringify(next);
10869
+ if (nextJson === row.specJson) continue;
10870
+ await engineStore(db).jobs.patchSpec(row.id, nextJson);
10871
+ restamped += 1;
10872
+ }
10873
+ return restamped;
10874
+ }
10694
10875
  async function createJob(db, input) {
10695
10876
  const id = `job_${nanoid12(16)}`;
10696
10877
  const now = Date.now();
@@ -10707,16 +10888,12 @@ async function createJob(db, input) {
10707
10888
  if (!isAutoLanguage(responseLanguage)) metadata["responseLanguage"] = responseLanguage;
10708
10889
  const attachDeliveries = input.role === "build" || input.role === "code-review" || input.attachDeliveries === true || input.role === "implement" && ((input.task.buildIteration ?? 0) > 0 || (input.task.reviewIteration ?? 0) > 0);
10709
10890
  const deliveries = attachDeliveries ? Object.entries(await loadDeliveries(db, input.task.id)).map(([name, d]) => ({ name, ...d })) : [];
10710
- const mc = await loadModelConfig(db, input.projectId);
10711
- const { preset: projectPreset } = await loadWorkflowSettings(db, input.projectId);
10712
- const workflowPreset = parseWorkflowPreset(input.task.workflowPreset) ?? projectPreset;
10713
- const model = resolveModelForRole(mc, input.role, workflowPreset);
10891
+ const axes = await resolveJobAxes(db, input.projectId, input.role, input.task.workflowPreset);
10892
+ const { modelDiscovery } = axes;
10714
10893
  const skillsRow = await db.get(
10715
10894
  `SELECT skills_json AS json FROM projects WHERE id = ?`,
10716
10895
  input.projectId
10717
10896
  );
10718
- const provider = resolveProvider(mc);
10719
- const modelDiscovery = input.role === "build" ? resolveModelForRole(mc, "build-discovery", workflowPreset) : void 0;
10720
10897
  const ec = await loadEffortConfig(db, input.projectId);
10721
10898
  const effort = resolveEffortForRole(ec, input.role);
10722
10899
  const effortDiscovery = input.role === "build" ? resolveEffortForRole(ec, "build-discovery") : void 0;
@@ -10725,23 +10902,13 @@ async function createJob(db, input) {
10725
10902
  const isReviewVerify = input.metadata?.["reviewVerify"] === true || input.metadata?.["externalReviewVerify"] === true;
10726
10903
  const councilPhase = input.role === "council" ? "questions" : input.role === "code-review" && !isReviewVerify ? "codeReview" : null;
10727
10904
  const council = councilPhase ? await loadCouncilSettings(db, input.projectId) : null;
10728
- const routing = resolveRoutingForRole(await loadRoutingConfig(db, input.projectId), input.role, {
10729
- defaultHarness: harnessForProvider(provider)
10730
- });
10731
- const effectiveModel = routing.model || model;
10732
- const effectiveProvider = HARNESS_PROVIDER[routing.harness] ?? provider;
10905
+ const { model: effectiveModel, provider: effectiveProvider } = axes;
10733
10906
  const executionSpec = {
10734
10907
  schemaVersion: EXECUTION_SPEC_SCHEMA_VERSION,
10735
10908
  jobId: id,
10736
10909
  role: input.role,
10737
- harness: routing.harness,
10738
- model: effectiveModel,
10739
- endpoint: routing.endpoint,
10740
- account: routing.account,
10741
- budgets: {},
10742
- // The harness a run needs is a capability of the worker (DEV-758 matches
10743
- // on these).
10744
- requiredCapabilities: [`harness:${routing.harness}`, `provider:${effectiveProvider}`]
10910
+ ...axesOnSpec(axes),
10911
+ budgets: {}
10745
10912
  };
10746
10913
  const spec = {
10747
10914
  jobId: id,
@@ -16569,7 +16736,7 @@ async function enqueueExternalReviewVerifyJob(db, task, meta, firstVerdict, log)
16569
16736
  metadata: { externalReviewVerify: true, reviewPanel: false, externalReview: true }
16570
16737
  });
16571
16738
  const patched = await patchSpecForExternalReview(db, task.projectId, spec, meta, {});
16572
- patched.model = resolveModelForRole(await loadModelConfig(db, task.projectId), "code-review-verify", (await loadWorkflowSettings(db, task.projectId)).preset);
16739
+ patched.model = resolveModelForRole(await loadModelConfig(db, task.projectId), "code-review-verify", (await loadWorkflowSettings(db, task.projectId)).preset, patched.executionSpec?.harness);
16573
16740
  const verifyEffort = resolveEffortForRole(
16574
16741
  await loadEffortConfig(db, task.projectId),
16575
16742
  "code-review-verify"
@@ -19563,7 +19730,9 @@ async function enqueueInternalReviewVerifyJob(db, task, firstVerdict, log) {
19563
19730
  const patched = {
19564
19731
  ...specRest,
19565
19732
  // DEV-660: the verifier runs on its own (cheaper) model key.
19566
- model: resolveModelForRole(await loadModelConfig(db, task.projectId), "code-review-verify", (await loadWorkflowSettings(db, task.projectId)).preset),
19733
+ // DEV-930: resolved for the harness THIS job was routed to — the cheaper
19734
+ // key is a tier, and the tier means a different model per vendor.
19735
+ model: resolveModelForRole(await loadModelConfig(db, task.projectId), "code-review-verify", (await loadWorkflowSettings(db, task.projectId)).preset, spec.executionSpec?.harness),
19567
19736
  ...verifyEffort !== void 0 ? { effort: verifyEffort } : {}
19568
19737
  };
19569
19738
  await engineStore(db).jobs.patchSpec(spec.jobId, JSON.stringify(patched));
@@ -25173,6 +25342,8 @@ async function rowToSummary3(db, row) {
25173
25342
  const mc = await loadModelConfig(db, row.id);
25174
25343
  summary.llmProvider = resolveProvider(mc);
25175
25344
  if (mc.default !== null || Object.keys(mc.roles).length > 0) summary.modelConfig = mc;
25345
+ const routing = await loadRoutingConfig(db, row.id);
25346
+ if (routing) summary.routing = routing;
25176
25347
  const ec = await loadEffortConfig(db, row.id);
25177
25348
  if (ec.default !== null || Object.keys(ec.roles).length > 0) summary.effortConfig = ec;
25178
25349
  const intake = await loadObserverIntakeSettings(db, row.id);
@@ -27209,6 +27380,7 @@ init_lifecycle();
27209
27380
  init_cluster_recovery();
27210
27381
 
27211
27382
  // src/projects/context-observation.ts
27383
+ init_queue2();
27212
27384
  init_prompts();
27213
27385
  init_prompt_cache();
27214
27386
  init_ws();
@@ -27257,17 +27429,29 @@ function installContextObserver(db, log, opts = {}) {
27257
27429
  } catch (err) {
27258
27430
  log.warn({ err, projectId }, "config_observation: project.yaml apply failed");
27259
27431
  }
27432
+ let regimenChanged = false;
27260
27433
  try {
27261
- await applyRegimenContent(
27434
+ const res = await applyRegimenContent(
27262
27435
  db,
27263
27436
  projectId,
27264
27437
  typeof obs.regimenYaml === "string" ? obs.regimenYaml : null,
27265
27438
  log,
27266
27439
  sameDoc ? sharedParsed : void 0
27267
27440
  );
27441
+ regimenChanged = res.changed;
27268
27442
  } catch (err) {
27269
27443
  log.warn({ err, projectId }, "config_observation: regimen.yaml apply failed");
27270
27444
  }
27445
+ if (regimenChanged) {
27446
+ try {
27447
+ const restamped = await restampQueuedJobs(db, projectId);
27448
+ if (restamped > 0) {
27449
+ log.info({ projectId, jobs: restamped }, "queued jobs re-stamped for the project's routing");
27450
+ }
27451
+ } catch (err) {
27452
+ log.warn({ err, projectId }, "config_observation: re-stamping queued jobs failed");
27453
+ }
27454
+ }
27271
27455
  try {
27272
27456
  applyActionsContent(projectId, obs.actions, obs.actionErrors);
27273
27457
  } catch (err) {
@@ -31049,6 +31233,7 @@ function latestQuestionsContent(artifacts) {
31049
31233
  // src/routes/jobs.ts
31050
31234
  init_context_outbox();
31051
31235
  init_deliveries();
31236
+ init_capabilities();
31052
31237
  init_sandbox_keys();
31053
31238
  init_sandbox2();
31054
31239
  init_ws();
@@ -31065,7 +31250,12 @@ async function jobsRoutes(app, deps) {
31065
31250
  app.log.warn({ sandboxId: sandbox.id }, "claim denied \u2014 SANDBOX protocol incompatible");
31066
31251
  return reply.status(204).send();
31067
31252
  }
31068
- const capabilities = JSON.parse(sandbox.capabilitiesJson);
31253
+ const capabilities = await claimCapabilities(
31254
+ db,
31255
+ sandbox.id,
31256
+ JSON.parse(sandbox.capabilitiesJson),
31257
+ req.body?.capabilities
31258
+ );
31069
31259
  const laneParam = req.query?.lane;
31070
31260
  const lane = laneParam === "planning" || laneParam === "implementation" ? laneParam : void 0;
31071
31261
  const deadline = Date.now() + config.jobClaimLongPollMs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-partner/codepipe-hub",
3
- "version": "0.14.1-dev.399.g96d6330f",
3
+ "version": "0.14.1-dev.404.g2e5e29f1",
4
4
  "description": "The CodePipe hub, packaged for one machine: the CLI installs it on demand for the local profile.",
5
5
  "type": "module",
6
6
  "main": "./hub/dist/index.js",