@code-partner/codepipe-hub 0.14.1-dev.399.g96d6330f → 0.14.1-dev.405.gb14c9aa1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/dist/assets/{index-C1be47HC.js → index-B2EvJfMD.js} +8 -8
- package/admin/dist/index.html +1 -1
- package/dashboard/dist/assets/{ModelCanvas-B2M4zQnV.js → ModelCanvas-3KSH8-83.js} +1 -1
- package/dashboard/dist/assets/{index-Cpy8aKZb.js → index-T-kK12wa.js} +121 -121
- package/dashboard/dist/index.html +1 -1
- package/hub/dist/index.js +251 -51
- package/package.json +1 -1
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
localStorage.getItem('theme') ?? 'dark'
|
|
28
28
|
);
|
|
29
29
|
</script>
|
|
30
|
-
<script type="module" crossorigin src="/assets/index-
|
|
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.
|
|
16
|
+
PLATFORM_VERSION = "0.14.1-dev.405.gb14c9aa1";
|
|
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
|
-
|
|
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
|
-
|
|
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"() {
|
|
@@ -3434,6 +3547,7 @@ function pgJobRepository(db, d = postgresDialect) {
|
|
|
3434
3547
|
const harnessClause = q.harnesses.length > 0 ? `AND (${harnessField} IS NULL OR ${harnessField} IN (${q.harnesses.map(() => "?").join(",")}))` : `AND ${harnessField} IS NULL`;
|
|
3435
3548
|
const heldClause = q.heldTaskIds.length > 0 ? `AND NOT (t.id IS NOT NULL AND j.role NOT IN (${CLUSTER_GATE_EXEMPT_SQL})
|
|
3436
3549
|
AND t.id IN (${q.heldTaskIds.map(() => "?").join(",")}))` : "";
|
|
3550
|
+
const heldProjectClause = q.heldProjectIds.length > 0 ? `AND j.project_id NOT IN (${q.heldProjectIds.map(() => "?").join(",")})` : "";
|
|
3437
3551
|
return await db.get(`SELECT ${jobColumns("j.")}
|
|
3438
3552
|
FROM jobs j
|
|
3439
3553
|
JOIN projects p ON p.id = j.project_id
|
|
@@ -3535,6 +3649,7 @@ function pgJobRepository(db, d = postgresDialect) {
|
|
|
3535
3649
|
-- code-review (DEV-686: they judge the task's own delivery) are
|
|
3536
3650
|
-- never gated. Empty held set this tick -> the clause is omitted.
|
|
3537
3651
|
${heldClause}
|
|
3652
|
+
${heldProjectClause}
|
|
3538
3653
|
-- DEV-686: eager build/code-review fill FREE slots only \u2014 an unheld
|
|
3539
3654
|
-- implement-family job (the cluster's critical path) always outranks
|
|
3540
3655
|
-- them; within a rank it stays FIFO.
|
|
@@ -3545,7 +3660,7 @@ function pgJobRepository(db, d = postgresDialect) {
|
|
|
3545
3660
|
-- same queued row before either updates it. Lock the candidate (jobs
|
|
3546
3661
|
-- side only \u2014 the LEFT-JOINed tasks row may be null) and let a
|
|
3547
3662
|
-- parallel claimer skip past it instead of blocking.
|
|
3548
|
-
${d.claimLock}`, ...projectFilter.params, ...ownerFilter.params, ...laneFilter.params, ...q.harnesses, q.now, ...roleParams, ...roleParams, PLANNING_LANE_MAX_JOBS, ...implCap.params, ...q.heldTaskIds);
|
|
3663
|
+
${d.claimLock}`, ...projectFilter.params, ...ownerFilter.params, ...laneFilter.params, ...q.harnesses, q.now, ...roleParams, ...roleParams, PLANNING_LANE_MAX_JOBS, ...implCap.params, ...q.heldTaskIds, ...q.heldProjectIds);
|
|
3549
3664
|
},
|
|
3550
3665
|
async markClaimed(jobId, sandboxId, executionId, now) {
|
|
3551
3666
|
await db.run(`UPDATE jobs
|
|
@@ -6457,6 +6572,17 @@ function mergeSandboxCapabilities(stored, live) {
|
|
|
6457
6572
|
}
|
|
6458
6573
|
return merged;
|
|
6459
6574
|
}
|
|
6575
|
+
async function claimCapabilities(db, sandboxId, stored, live) {
|
|
6576
|
+
const merged = mergeSandboxCapabilities(stored, live);
|
|
6577
|
+
if (JSON.stringify(merged) !== JSON.stringify(stored)) {
|
|
6578
|
+
await db.run(
|
|
6579
|
+
`UPDATE sandboxes SET capabilities_json = ? WHERE id = ?`,
|
|
6580
|
+
JSON.stringify(merged),
|
|
6581
|
+
sandboxId
|
|
6582
|
+
);
|
|
6583
|
+
}
|
|
6584
|
+
return merged;
|
|
6585
|
+
}
|
|
6460
6586
|
async function refreshSandboxCapabilities(db, sandboxId, live) {
|
|
6461
6587
|
const row = await db.get(
|
|
6462
6588
|
`SELECT capabilities_json as capabilitiesJson FROM sandboxes WHERE id = ?`,
|
|
@@ -10691,6 +10817,63 @@ async function heldByTrancheDeferral(db) {
|
|
|
10691
10817
|
);
|
|
10692
10818
|
return rows.map((r) => r.id);
|
|
10693
10819
|
}
|
|
10820
|
+
async function resolveJobAxes(db, projectId, role, taskWorkflowPreset) {
|
|
10821
|
+
const mc = await loadModelConfig(db, projectId);
|
|
10822
|
+
const provider = resolveProvider(mc);
|
|
10823
|
+
const { preset: projectPreset } = await loadWorkflowSettings(db, projectId);
|
|
10824
|
+
const workflowPreset = parseWorkflowPreset(taskWorkflowPreset) ?? projectPreset;
|
|
10825
|
+
const routing = resolveRoutingForRole(await loadRoutingConfig(db, projectId), role, {
|
|
10826
|
+
defaultHarness: harnessForProvider(provider)
|
|
10827
|
+
});
|
|
10828
|
+
return {
|
|
10829
|
+
routing,
|
|
10830
|
+
provider: HARNESS_PROVIDER[routing.harness] ?? provider,
|
|
10831
|
+
model: routing.model || resolveModelForRole(mc, role, workflowPreset, routing.harness),
|
|
10832
|
+
modelDiscovery: role === "build" ? resolveModelForRole(mc, "build-discovery", workflowPreset, routing.harness) : void 0
|
|
10833
|
+
};
|
|
10834
|
+
}
|
|
10835
|
+
function axesOnSpec(axes) {
|
|
10836
|
+
return {
|
|
10837
|
+
harness: axes.routing.harness,
|
|
10838
|
+
model: axes.model,
|
|
10839
|
+
endpoint: axes.routing.endpoint,
|
|
10840
|
+
account: axes.routing.account,
|
|
10841
|
+
// The harness a run needs is a capability of the worker (DEV-758 matches
|
|
10842
|
+
// on these).
|
|
10843
|
+
requiredCapabilities: [`harness:${axes.routing.harness}`, `provider:${axes.provider}`]
|
|
10844
|
+
};
|
|
10845
|
+
}
|
|
10846
|
+
async function restampQueuedJobs(db, projectId) {
|
|
10847
|
+
const rows = await db.all(
|
|
10848
|
+
`SELECT id, role, task_id AS taskId, spec_json AS specJson
|
|
10849
|
+
FROM jobs WHERE project_id = ? AND state = 'queued'`,
|
|
10850
|
+
projectId
|
|
10851
|
+
);
|
|
10852
|
+
let restamped = 0;
|
|
10853
|
+
for (const row of rows) {
|
|
10854
|
+
let spec;
|
|
10855
|
+
try {
|
|
10856
|
+
spec = JSON.parse(row.specJson);
|
|
10857
|
+
} catch {
|
|
10858
|
+
continue;
|
|
10859
|
+
}
|
|
10860
|
+
if (!spec.executionSpec) continue;
|
|
10861
|
+
const task = await getTask(db, row.taskId);
|
|
10862
|
+
const axes = await resolveJobAxes(db, projectId, row.role, task?.workflowPreset);
|
|
10863
|
+
const next = {
|
|
10864
|
+
...spec,
|
|
10865
|
+
model: axes.model,
|
|
10866
|
+
provider: axes.provider,
|
|
10867
|
+
...axes.modelDiscovery ? { modelDiscovery: axes.modelDiscovery } : {},
|
|
10868
|
+
executionSpec: { ...spec.executionSpec, ...axesOnSpec(axes) }
|
|
10869
|
+
};
|
|
10870
|
+
const nextJson = JSON.stringify(next);
|
|
10871
|
+
if (nextJson === row.specJson) continue;
|
|
10872
|
+
await engineStore(db).jobs.patchSpec(row.id, nextJson);
|
|
10873
|
+
restamped += 1;
|
|
10874
|
+
}
|
|
10875
|
+
return restamped;
|
|
10876
|
+
}
|
|
10694
10877
|
async function createJob(db, input) {
|
|
10695
10878
|
const id = `job_${nanoid12(16)}`;
|
|
10696
10879
|
const now = Date.now();
|
|
@@ -10707,16 +10890,12 @@ async function createJob(db, input) {
|
|
|
10707
10890
|
if (!isAutoLanguage(responseLanguage)) metadata["responseLanguage"] = responseLanguage;
|
|
10708
10891
|
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
10892
|
const deliveries = attachDeliveries ? Object.entries(await loadDeliveries(db, input.task.id)).map(([name, d]) => ({ name, ...d })) : [];
|
|
10710
|
-
const
|
|
10711
|
-
const {
|
|
10712
|
-
const workflowPreset = parseWorkflowPreset(input.task.workflowPreset) ?? projectPreset;
|
|
10713
|
-
const model = resolveModelForRole(mc, input.role, workflowPreset);
|
|
10893
|
+
const axes = await resolveJobAxes(db, input.projectId, input.role, input.task.workflowPreset);
|
|
10894
|
+
const { modelDiscovery } = axes;
|
|
10714
10895
|
const skillsRow = await db.get(
|
|
10715
10896
|
`SELECT skills_json AS json FROM projects WHERE id = ?`,
|
|
10716
10897
|
input.projectId
|
|
10717
10898
|
);
|
|
10718
|
-
const provider = resolveProvider(mc);
|
|
10719
|
-
const modelDiscovery = input.role === "build" ? resolveModelForRole(mc, "build-discovery", workflowPreset) : void 0;
|
|
10720
10899
|
const ec = await loadEffortConfig(db, input.projectId);
|
|
10721
10900
|
const effort = resolveEffortForRole(ec, input.role);
|
|
10722
10901
|
const effortDiscovery = input.role === "build" ? resolveEffortForRole(ec, "build-discovery") : void 0;
|
|
@@ -10725,23 +10904,13 @@ async function createJob(db, input) {
|
|
|
10725
10904
|
const isReviewVerify = input.metadata?.["reviewVerify"] === true || input.metadata?.["externalReviewVerify"] === true;
|
|
10726
10905
|
const councilPhase = input.role === "council" ? "questions" : input.role === "code-review" && !isReviewVerify ? "codeReview" : null;
|
|
10727
10906
|
const council = councilPhase ? await loadCouncilSettings(db, input.projectId) : null;
|
|
10728
|
-
const
|
|
10729
|
-
defaultHarness: harnessForProvider(provider)
|
|
10730
|
-
});
|
|
10731
|
-
const effectiveModel = routing.model || model;
|
|
10732
|
-
const effectiveProvider = HARNESS_PROVIDER[routing.harness] ?? provider;
|
|
10907
|
+
const { model: effectiveModel, provider: effectiveProvider } = axes;
|
|
10733
10908
|
const executionSpec = {
|
|
10734
10909
|
schemaVersion: EXECUTION_SPEC_SCHEMA_VERSION,
|
|
10735
10910
|
jobId: id,
|
|
10736
10911
|
role: input.role,
|
|
10737
|
-
|
|
10738
|
-
|
|
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}`]
|
|
10912
|
+
...axesOnSpec(axes),
|
|
10913
|
+
budgets: {}
|
|
10745
10914
|
};
|
|
10746
10915
|
const spec = {
|
|
10747
10916
|
jobId: id,
|
|
@@ -10879,6 +11048,14 @@ async function claimNextJob(db, sandboxId, capabilities, lane) {
|
|
|
10879
11048
|
for (const id of await heldByClusterGate(db, projectId)) heldIds.push(id);
|
|
10880
11049
|
}
|
|
10881
11050
|
for (const id of await heldByTrancheDeferral(db)) heldIds.push(id);
|
|
11051
|
+
const heldProjectIds = [];
|
|
11052
|
+
const queuedProjects = await db.all(`SELECT DISTINCT j.project_id AS projectId
|
|
11053
|
+
FROM jobs j JOIN projects p ON p.id = j.project_id
|
|
11054
|
+
WHERE j.state = 'queued'
|
|
11055
|
+
AND ${projectFilter.clause} AND ${ownerFilter.clause} AND ${laneFilter.clause}`, ...projectFilter.params, ...ownerFilter.params, ...laneFilter.params);
|
|
11056
|
+
for (const { projectId } of queuedProjects) {
|
|
11057
|
+
if ((await unpinnedSourceRepos(db, projectId)).length > 0) heldProjectIds.push(projectId);
|
|
11058
|
+
}
|
|
10882
11059
|
const claimed = await claimJob({
|
|
10883
11060
|
store: pgEngineStore(db),
|
|
10884
11061
|
audit: async (tx, e) => {
|
|
@@ -10952,7 +11129,7 @@ async function claimNextJob(db, sandboxId, capabilities, lane) {
|
|
|
10952
11129
|
}
|
|
10953
11130
|
return changed ? JSON.stringify(spec2) : null;
|
|
10954
11131
|
}
|
|
10955
|
-
}, { ...claimInput, heldTaskIds: heldIds, now: Date.now() });
|
|
11132
|
+
}, { ...claimInput, heldTaskIds: heldIds, heldProjectIds, now: Date.now() });
|
|
10956
11133
|
if (!claimed) return null;
|
|
10957
11134
|
const spec = JSON.parse(claimed.job.specJson);
|
|
10958
11135
|
spec.executionId = claimed.executionId;
|
|
@@ -16569,7 +16746,7 @@ async function enqueueExternalReviewVerifyJob(db, task, meta, firstVerdict, log)
|
|
|
16569
16746
|
metadata: { externalReviewVerify: true, reviewPanel: false, externalReview: true }
|
|
16570
16747
|
});
|
|
16571
16748
|
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);
|
|
16749
|
+
patched.model = resolveModelForRole(await loadModelConfig(db, task.projectId), "code-review-verify", (await loadWorkflowSettings(db, task.projectId)).preset, patched.executionSpec?.harness);
|
|
16573
16750
|
const verifyEffort = resolveEffortForRole(
|
|
16574
16751
|
await loadEffortConfig(db, task.projectId),
|
|
16575
16752
|
"code-review-verify"
|
|
@@ -19563,7 +19740,9 @@ async function enqueueInternalReviewVerifyJob(db, task, firstVerdict, log) {
|
|
|
19563
19740
|
const patched = {
|
|
19564
19741
|
...specRest,
|
|
19565
19742
|
// DEV-660: the verifier runs on its own (cheaper) model key.
|
|
19566
|
-
|
|
19743
|
+
// DEV-930: resolved for the harness THIS job was routed to — the cheaper
|
|
19744
|
+
// key is a tier, and the tier means a different model per vendor.
|
|
19745
|
+
model: resolveModelForRole(await loadModelConfig(db, task.projectId), "code-review-verify", (await loadWorkflowSettings(db, task.projectId)).preset, spec.executionSpec?.harness),
|
|
19567
19746
|
...verifyEffort !== void 0 ? { effort: verifyEffort } : {}
|
|
19568
19747
|
};
|
|
19569
19748
|
await engineStore(db).jobs.patchSpec(spec.jobId, JSON.stringify(patched));
|
|
@@ -25173,6 +25352,8 @@ async function rowToSummary3(db, row) {
|
|
|
25173
25352
|
const mc = await loadModelConfig(db, row.id);
|
|
25174
25353
|
summary.llmProvider = resolveProvider(mc);
|
|
25175
25354
|
if (mc.default !== null || Object.keys(mc.roles).length > 0) summary.modelConfig = mc;
|
|
25355
|
+
const routing = await loadRoutingConfig(db, row.id);
|
|
25356
|
+
if (routing) summary.routing = routing;
|
|
25176
25357
|
const ec = await loadEffortConfig(db, row.id);
|
|
25177
25358
|
if (ec.default !== null || Object.keys(ec.roles).length > 0) summary.effortConfig = ec;
|
|
25178
25359
|
const intake = await loadObserverIntakeSettings(db, row.id);
|
|
@@ -27209,6 +27390,7 @@ init_lifecycle();
|
|
|
27209
27390
|
init_cluster_recovery();
|
|
27210
27391
|
|
|
27211
27392
|
// src/projects/context-observation.ts
|
|
27393
|
+
init_queue2();
|
|
27212
27394
|
init_prompts();
|
|
27213
27395
|
init_prompt_cache();
|
|
27214
27396
|
init_ws();
|
|
@@ -27257,17 +27439,29 @@ function installContextObserver(db, log, opts = {}) {
|
|
|
27257
27439
|
} catch (err) {
|
|
27258
27440
|
log.warn({ err, projectId }, "config_observation: project.yaml apply failed");
|
|
27259
27441
|
}
|
|
27442
|
+
let regimenChanged = false;
|
|
27260
27443
|
try {
|
|
27261
|
-
await applyRegimenContent(
|
|
27444
|
+
const res = await applyRegimenContent(
|
|
27262
27445
|
db,
|
|
27263
27446
|
projectId,
|
|
27264
27447
|
typeof obs.regimenYaml === "string" ? obs.regimenYaml : null,
|
|
27265
27448
|
log,
|
|
27266
27449
|
sameDoc ? sharedParsed : void 0
|
|
27267
27450
|
);
|
|
27451
|
+
regimenChanged = res.changed;
|
|
27268
27452
|
} catch (err) {
|
|
27269
27453
|
log.warn({ err, projectId }, "config_observation: regimen.yaml apply failed");
|
|
27270
27454
|
}
|
|
27455
|
+
if (regimenChanged) {
|
|
27456
|
+
try {
|
|
27457
|
+
const restamped = await restampQueuedJobs(db, projectId);
|
|
27458
|
+
if (restamped > 0) {
|
|
27459
|
+
log.info({ projectId, jobs: restamped }, "queued jobs re-stamped for the project's routing");
|
|
27460
|
+
}
|
|
27461
|
+
} catch (err) {
|
|
27462
|
+
log.warn({ err, projectId }, "config_observation: re-stamping queued jobs failed");
|
|
27463
|
+
}
|
|
27464
|
+
}
|
|
27271
27465
|
try {
|
|
27272
27466
|
applyActionsContent(projectId, obs.actions, obs.actionErrors);
|
|
27273
27467
|
} catch (err) {
|
|
@@ -31049,6 +31243,7 @@ function latestQuestionsContent(artifacts) {
|
|
|
31049
31243
|
// src/routes/jobs.ts
|
|
31050
31244
|
init_context_outbox();
|
|
31051
31245
|
init_deliveries();
|
|
31246
|
+
init_capabilities();
|
|
31052
31247
|
init_sandbox_keys();
|
|
31053
31248
|
init_sandbox2();
|
|
31054
31249
|
init_ws();
|
|
@@ -31065,7 +31260,12 @@ async function jobsRoutes(app, deps) {
|
|
|
31065
31260
|
app.log.warn({ sandboxId: sandbox.id }, "claim denied \u2014 SANDBOX protocol incompatible");
|
|
31066
31261
|
return reply.status(204).send();
|
|
31067
31262
|
}
|
|
31068
|
-
const capabilities =
|
|
31263
|
+
const capabilities = await claimCapabilities(
|
|
31264
|
+
db,
|
|
31265
|
+
sandbox.id,
|
|
31266
|
+
JSON.parse(sandbox.capabilitiesJson),
|
|
31267
|
+
req.body?.capabilities
|
|
31268
|
+
);
|
|
31069
31269
|
const laneParam = req.query?.lane;
|
|
31070
31270
|
const lane = laneParam === "planning" || laneParam === "implementation" ? laneParam : void 0;
|
|
31071
31271
|
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.
|
|
3
|
+
"version": "0.14.1-dev.405.gb14c9aa1",
|
|
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",
|