@brainbase-labs/cli 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1267 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35184,7 +35184,7 @@ var require_dist2 = __commonJS((exports, module) => {
|
|
|
35184
35184
|
// src/index.ts
|
|
35185
35185
|
var import_picocolors81 = __toESM(require_picocolors(), 1);
|
|
35186
35186
|
import process14 from "node:process";
|
|
35187
|
-
import
|
|
35187
|
+
import fs96 from "node:fs";
|
|
35188
35188
|
|
|
35189
35189
|
// src/cli/template.ts
|
|
35190
35190
|
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
@@ -36091,7 +36091,7 @@ function padStart(s, n) {
|
|
|
36091
36091
|
// package.json
|
|
36092
36092
|
var package_default = {
|
|
36093
36093
|
name: "@brainbase-labs/cli",
|
|
36094
|
-
version: "0.
|
|
36094
|
+
version: "0.55.0",
|
|
36095
36095
|
description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
|
|
36096
36096
|
type: "module",
|
|
36097
36097
|
bin: {
|
|
@@ -82445,6 +82445,10 @@ var SyncedScheduleTriggerSchema = exports_external.object({
|
|
|
82445
82445
|
node_id: exports_external.string(),
|
|
82446
82446
|
hash: exports_external.string()
|
|
82447
82447
|
});
|
|
82448
|
+
var SyncedDecisionSchema = exports_external.object({
|
|
82449
|
+
id: exports_external.string(),
|
|
82450
|
+
hash: exports_external.string()
|
|
82451
|
+
});
|
|
82448
82452
|
var SyncedOrchestrationMetaSchema = exports_external.object({
|
|
82449
82453
|
name: exports_external.string().optional(),
|
|
82450
82454
|
description: exports_external.string().optional(),
|
|
@@ -82460,6 +82464,7 @@ var OrchestrationSyncStateSchema = exports_external.object({
|
|
|
82460
82464
|
members: exports_external.array(SyncedMemberSchema),
|
|
82461
82465
|
edges: exports_external.array(SyncedEdgeSchema),
|
|
82462
82466
|
triggers: exports_external.array(SyncedScheduleTriggerSchema).optional(),
|
|
82467
|
+
decisions: exports_external.array(SyncedDecisionSchema).optional(),
|
|
82463
82468
|
meta: SyncedOrchestrationMetaSchema.optional()
|
|
82464
82469
|
});
|
|
82465
82470
|
function orchLinkPath(cwd2) {
|
|
@@ -82881,6 +82886,9 @@ var TriggerEdgeSchema = exports_external.object({
|
|
|
82881
82886
|
payload_schema: exports_external.record(exports_external.unknown()).optional()
|
|
82882
82887
|
});
|
|
82883
82888
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
82889
|
+
function canonicalUuid2(value2) {
|
|
82890
|
+
return UUID_RE.test(value2) ? value2.toLowerCase() : value2;
|
|
82891
|
+
}
|
|
82884
82892
|
var ScheduleTriggerSchema = exports_external.object({
|
|
82885
82893
|
type: exports_external.literal("schedule"),
|
|
82886
82894
|
node_id: exports_external.string().regex(UUID_RE, "must be a UUID"),
|
|
@@ -82898,12 +82906,52 @@ var ReadOnlyTriggerSchema = exports_external.object({
|
|
|
82898
82906
|
to: exports_external.array(TriggerEdgeSchema).default([])
|
|
82899
82907
|
});
|
|
82900
82908
|
var TriggerSchema = exports_external.union([ScheduleTriggerSchema, ReadOnlyTriggerSchema]);
|
|
82909
|
+
var DECISION_MODELS = [
|
|
82910
|
+
"jev",
|
|
82911
|
+
"kev-4b",
|
|
82912
|
+
"clef",
|
|
82913
|
+
"clef-flash",
|
|
82914
|
+
"gpt-6-luna"
|
|
82915
|
+
];
|
|
82916
|
+
var DEFAULT_DECISION_MODEL = "clef-flash";
|
|
82917
|
+
var DECISION_LIMITS = {
|
|
82918
|
+
name: 200,
|
|
82919
|
+
model: 100,
|
|
82920
|
+
instructions: 4000,
|
|
82921
|
+
answersMin: 2,
|
|
82922
|
+
answersMax: 20,
|
|
82923
|
+
answer: 200,
|
|
82924
|
+
criterion: 1000
|
|
82925
|
+
};
|
|
82926
|
+
var DecisionTargetSchema = exports_external.object({
|
|
82927
|
+
agent: exports_external.string().min(1).optional(),
|
|
82928
|
+
decision: exports_external.string().regex(UUID_RE, "must be a UUID").optional(),
|
|
82929
|
+
description: exports_external.string().optional()
|
|
82930
|
+
}).refine((t) => t.agent === undefined !== (t.decision === undefined), {
|
|
82931
|
+
message: "set exactly one of agent or decision"
|
|
82932
|
+
});
|
|
82933
|
+
var DecisionAnswerSchema = exports_external.object({
|
|
82934
|
+
answer: exports_external.string(),
|
|
82935
|
+
criterion: exports_external.string().optional(),
|
|
82936
|
+
to: exports_external.array(DecisionTargetSchema).default([])
|
|
82937
|
+
});
|
|
82938
|
+
var DecisionSchema = exports_external.object({
|
|
82939
|
+
id: exports_external.string().regex(UUID_RE, "must be a UUID"),
|
|
82940
|
+
name: exports_external.string(),
|
|
82941
|
+
model: exports_external.string().optional(),
|
|
82942
|
+
instructions: exports_external.string().optional(),
|
|
82943
|
+
confidence_threshold: exports_external.number().min(0).max(1).nullable().optional(),
|
|
82944
|
+
from: exports_external.array(exports_external.object({ trigger: exports_external.string().regex(UUID_RE, "must be a UUID") })).default([]),
|
|
82945
|
+
answers: exports_external.array(DecisionAnswerSchema),
|
|
82946
|
+
fallback: exports_external.array(DecisionTargetSchema).default([])
|
|
82947
|
+
});
|
|
82901
82948
|
var OrchestrationManifestSchema = exports_external.object({
|
|
82902
82949
|
schema: exports_external.literal(1),
|
|
82903
82950
|
orchestration: OrchMetaSchema,
|
|
82904
82951
|
members: exports_external.array(MemberSchema).default([]),
|
|
82905
82952
|
edges: exports_external.array(EdgeSchema).default([]),
|
|
82906
|
-
triggers: exports_external.array(TriggerSchema).optional()
|
|
82953
|
+
triggers: exports_external.array(TriggerSchema).optional(),
|
|
82954
|
+
decisions: exports_external.array(DecisionSchema).optional()
|
|
82907
82955
|
});
|
|
82908
82956
|
function orchManifestPath(cwd2) {
|
|
82909
82957
|
return path96.join(cwd2, ORCH_MANIFEST_FILE);
|
|
@@ -83328,6 +83376,132 @@ function syncedScheduleTriggers(triggers) {
|
|
|
83328
83376
|
hash: scheduleTriggerHash(t)
|
|
83329
83377
|
}));
|
|
83330
83378
|
}
|
|
83379
|
+
var DECISION = "decision";
|
|
83380
|
+
function manifestDecisions(manifest, slugToAgentId) {
|
|
83381
|
+
const target = (t) => {
|
|
83382
|
+
const agentId = t.agent !== undefined ? slugToAgentId?.get(t.agent) : undefined;
|
|
83383
|
+
return {
|
|
83384
|
+
...t.agent !== undefined ? { agent: t.agent, ...agentId ? { agent_id: agentId } : {} } : { decision: canonicalUuid2(t.decision) },
|
|
83385
|
+
description: t.description ?? ""
|
|
83386
|
+
};
|
|
83387
|
+
};
|
|
83388
|
+
return (manifest?.decisions ?? []).map((d3) => ({
|
|
83389
|
+
id: canonicalUuid2(d3.id),
|
|
83390
|
+
name: d3.name.trim(),
|
|
83391
|
+
model: (d3.model ?? DEFAULT_DECISION_MODEL).trim(),
|
|
83392
|
+
instructions: d3.instructions ?? "",
|
|
83393
|
+
confidence_threshold: d3.confidence_threshold ?? null,
|
|
83394
|
+
triggers: d3.from.map((f4) => canonicalUuid2(f4.trigger)),
|
|
83395
|
+
answers: d3.answers.map((a3) => ({
|
|
83396
|
+
answer: a3.answer.trim(),
|
|
83397
|
+
criterion: (a3.criterion ?? "").trim(),
|
|
83398
|
+
to: a3.to.map(target)
|
|
83399
|
+
})),
|
|
83400
|
+
fallback: d3.fallback.map(target)
|
|
83401
|
+
}));
|
|
83402
|
+
}
|
|
83403
|
+
function liveDecisionLinks(cloud) {
|
|
83404
|
+
const agents = new Set(cloud.members.map((m3) => m3.agent_id));
|
|
83405
|
+
const decisions = new Set((cloud.decisions ?? []).map((d3) => canonicalUuid2(d3.id)));
|
|
83406
|
+
const triggers = new Set((cloud.triggers ?? []).map((t) => canonicalUuid2(t.node_id)));
|
|
83407
|
+
return (d3) => {
|
|
83408
|
+
const answers = new Set(d3.answers.map((a3) => a3.answer));
|
|
83409
|
+
return {
|
|
83410
|
+
routes: d3.routes.filter((r2) => (r2.is_fallback || r2.answer != null && answers.has(r2.answer)) && (r2.to_agent_id ? agents.has(r2.to_agent_id) : r2.to_decision_id != null && decisions.has(canonicalUuid2(r2.to_decision_id)))),
|
|
83411
|
+
inputs: d3.inputs.filter((i) => triggers.has(canonicalUuid2(i.from_trigger_node_id)))
|
|
83412
|
+
};
|
|
83413
|
+
};
|
|
83414
|
+
}
|
|
83415
|
+
function cloudDecisions(cloud, slugForAgent) {
|
|
83416
|
+
if (cloud.decisions == null)
|
|
83417
|
+
return null;
|
|
83418
|
+
const live = liveDecisionLinks(cloud);
|
|
83419
|
+
return cloud.decisions.map((d3) => {
|
|
83420
|
+
const { routes, inputs } = live(d3);
|
|
83421
|
+
const target = (r2) => ({
|
|
83422
|
+
...r2.to_agent_id ? {
|
|
83423
|
+
agent: slugForAgent(r2.to_agent_id, r2.to_slug ?? r2.to_agent_id),
|
|
83424
|
+
agent_id: r2.to_agent_id
|
|
83425
|
+
} : { decision: canonicalUuid2(r2.to_decision_id) },
|
|
83426
|
+
description: r2.description ?? ""
|
|
83427
|
+
});
|
|
83428
|
+
return {
|
|
83429
|
+
id: canonicalUuid2(d3.id),
|
|
83430
|
+
name: d3.name.trim(),
|
|
83431
|
+
model: d3.model.trim(),
|
|
83432
|
+
instructions: d3.instructions ?? "",
|
|
83433
|
+
confidence_threshold: d3.confidence_threshold ?? null,
|
|
83434
|
+
triggers: inputs.map((i) => canonicalUuid2(i.from_trigger_node_id)),
|
|
83435
|
+
answers: d3.answers.map((a3) => ({
|
|
83436
|
+
answer: a3.answer.trim(),
|
|
83437
|
+
criterion: (a3.criterion ?? "").trim(),
|
|
83438
|
+
to: routes.filter((r2) => !r2.is_fallback && r2.answer === a3.answer).map(target)
|
|
83439
|
+
})),
|
|
83440
|
+
fallback: routes.filter((r2) => r2.is_fallback).map(target)
|
|
83441
|
+
};
|
|
83442
|
+
});
|
|
83443
|
+
}
|
|
83444
|
+
function deadDecisionLinks(cloud) {
|
|
83445
|
+
const live = liveDecisionLinks(cloud);
|
|
83446
|
+
const out = new Map;
|
|
83447
|
+
for (const d3 of cloud.decisions ?? []) {
|
|
83448
|
+
const { routes, inputs } = live(d3);
|
|
83449
|
+
const dead = d3.routes.length - routes.length + d3.inputs.length - inputs.length;
|
|
83450
|
+
if (dead > 0)
|
|
83451
|
+
out.set(d3.id, dead);
|
|
83452
|
+
}
|
|
83453
|
+
return out;
|
|
83454
|
+
}
|
|
83455
|
+
function decisionHash(d3) {
|
|
83456
|
+
const targets = (ts2) => ts2.map(({ agent, agent_id, ...rest2 }) => stableJson(agent_id !== undefined ? { agent_id, ...rest2 } : { agent, ...rest2 })).sort();
|
|
83457
|
+
const canonical = {
|
|
83458
|
+
name: d3.name,
|
|
83459
|
+
model: d3.model,
|
|
83460
|
+
instructions: d3.instructions,
|
|
83461
|
+
confidence_threshold: d3.confidence_threshold,
|
|
83462
|
+
triggers: [...d3.triggers].sort(),
|
|
83463
|
+
answers: d3.answers.map((a3) => ({
|
|
83464
|
+
answer: a3.answer,
|
|
83465
|
+
criterion: a3.criterion,
|
|
83466
|
+
to: targets(a3.to)
|
|
83467
|
+
})),
|
|
83468
|
+
fallback: targets(d3.fallback)
|
|
83469
|
+
};
|
|
83470
|
+
return crypto6.createHash("sha256").update(stableJson(canonical)).digest("hex");
|
|
83471
|
+
}
|
|
83472
|
+
function decisionLabel(d3) {
|
|
83473
|
+
return `decision "${d3.name}" (${d3.id})`;
|
|
83474
|
+
}
|
|
83475
|
+
function decideDecisionReconcile(input) {
|
|
83476
|
+
const rows = threeWayDiff({
|
|
83477
|
+
local: input.local.map((d3) => ({
|
|
83478
|
+
type: DECISION,
|
|
83479
|
+
slug: d3.id,
|
|
83480
|
+
hash: decisionHash(d3)
|
|
83481
|
+
})),
|
|
83482
|
+
lock: (input.baseline ?? []).map((d3) => ({
|
|
83483
|
+
type: DECISION,
|
|
83484
|
+
slug: d3.id,
|
|
83485
|
+
hash: d3.hash,
|
|
83486
|
+
installedPaths: []
|
|
83487
|
+
})),
|
|
83488
|
+
cloud: input.cloud.map((d3) => ({
|
|
83489
|
+
type: DECISION,
|
|
83490
|
+
slug: d3.id,
|
|
83491
|
+
hash: decisionHash(d3),
|
|
83492
|
+
files: []
|
|
83493
|
+
}))
|
|
83494
|
+
});
|
|
83495
|
+
return decideCollectionReconcile({
|
|
83496
|
+
rows,
|
|
83497
|
+
type: DECISION,
|
|
83498
|
+
claims: input.local.length > 0,
|
|
83499
|
+
force: input.force
|
|
83500
|
+
});
|
|
83501
|
+
}
|
|
83502
|
+
function syncedDecisions(decisions) {
|
|
83503
|
+
return decisions.map((d3) => ({ id: d3.id, hash: decisionHash(d3) }));
|
|
83504
|
+
}
|
|
83331
83505
|
var ORCH_META_FIELDS = [
|
|
83332
83506
|
"name",
|
|
83333
83507
|
"description",
|
|
@@ -83410,6 +83584,26 @@ function triggersForManifest(triggers, slugFor) {
|
|
|
83410
83584
|
};
|
|
83411
83585
|
});
|
|
83412
83586
|
}
|
|
83587
|
+
function decisionsForManifest(decisions) {
|
|
83588
|
+
const target = (t) => ({
|
|
83589
|
+
...t.agent !== undefined ? { agent: t.agent } : { decision: t.decision },
|
|
83590
|
+
...t.description ? { description: t.description } : {}
|
|
83591
|
+
});
|
|
83592
|
+
return decisions.map((d3) => ({
|
|
83593
|
+
id: d3.id,
|
|
83594
|
+
name: d3.name,
|
|
83595
|
+
model: d3.model,
|
|
83596
|
+
...d3.instructions ? { instructions: d3.instructions } : {},
|
|
83597
|
+
...d3.confidence_threshold != null ? { confidence_threshold: d3.confidence_threshold } : {},
|
|
83598
|
+
...d3.triggers.length ? { from: d3.triggers.map((trigger) => ({ trigger })) } : {},
|
|
83599
|
+
answers: d3.answers.map((a3) => ({
|
|
83600
|
+
answer: a3.answer,
|
|
83601
|
+
...a3.criterion ? { criterion: a3.criterion } : {},
|
|
83602
|
+
...a3.to.length ? { to: a3.to.map(target) } : {}
|
|
83603
|
+
})),
|
|
83604
|
+
...d3.fallback.length ? { fallback: d3.fallback.map(target) } : {}
|
|
83605
|
+
}));
|
|
83606
|
+
}
|
|
83413
83607
|
async function runOrchestrationPull(cwd2, args) {
|
|
83414
83608
|
banner("orchestration pull — fetch orchestration + all member agents");
|
|
83415
83609
|
let orchId = null;
|
|
@@ -83460,6 +83654,14 @@ async function runOrchestrationPull(cwd2, args) {
|
|
|
83460
83654
|
planLines.push(` ${import_picocolors52.default.cyan(from)} ${import_picocolors52.default.dim("→")} ${import_picocolors52.default.cyan(to2)}${desc}`);
|
|
83461
83655
|
}
|
|
83462
83656
|
}
|
|
83657
|
+
const pulledDecisions = cloudDecisions(cloud, (agentId, fallback) => slugByAgent.get(agentId) ?? fallback);
|
|
83658
|
+
if (pulledDecisions?.length) {
|
|
83659
|
+
planLines.push("");
|
|
83660
|
+
planLines.push(` ${import_picocolors52.default.dim("decisions:")}`);
|
|
83661
|
+
for (const d3 of pulledDecisions) {
|
|
83662
|
+
planLines.push(` ${import_picocolors52.default.cyan("◆")} ${import_picocolors52.default.bold(d3.name)} ${import_picocolors52.default.dim(`(${d3.model}, ${d3.answers.length} answers)`)}`);
|
|
83663
|
+
}
|
|
83664
|
+
}
|
|
83463
83665
|
planLines.push("");
|
|
83464
83666
|
console.log(planLines.join(`
|
|
83465
83667
|
`));
|
|
@@ -83525,6 +83727,19 @@ async function runOrchestrationPull(cwd2, args) {
|
|
|
83525
83727
|
}
|
|
83526
83728
|
}
|
|
83527
83729
|
const manifestTriggers = triggersForManifest(cloud.triggers ?? [], slugFor);
|
|
83730
|
+
let manifestDecisions2 = pulledDecisions ? decisionsForManifest(pulledDecisions) : undefined;
|
|
83731
|
+
let decisionBaseline = pulledDecisions ? syncedDecisions(pulledDecisions) : undefined;
|
|
83732
|
+
if (!pulledDecisions) {
|
|
83733
|
+
try {
|
|
83734
|
+
manifestDecisions2 = readOrchManifest(cwd2)?.decisions;
|
|
83735
|
+
} catch {}
|
|
83736
|
+
const lock = readOrchSyncState(cwd2);
|
|
83737
|
+
decisionBaseline = lock?.orchestration_id === cloud.id ? lock.decisions : undefined;
|
|
83738
|
+
}
|
|
83739
|
+
for (const [decisionId, count] of deadDecisionLinks(cloud)) {
|
|
83740
|
+
const name = pulledDecisions?.find((d3) => d3.id === decisionId)?.name;
|
|
83741
|
+
f2.warn(`Decision ${import_picocolors52.default.bold(name ?? decisionId)} has ${count} route${count === 1 ? "" : "s"} or input${count === 1 ? "" : "s"} that lead${count === 1 ? "s" : ""} nowhere: on an answer it no longer has, or to an agent, decision or trigger no longer in this orchestration. They never run and are not in ${import_picocolors52.default.bold("brainbase-orchestration.yaml")}; the next push of decisions removes them.`);
|
|
83742
|
+
}
|
|
83528
83743
|
const manifest = {
|
|
83529
83744
|
schema: 1,
|
|
83530
83745
|
orchestration: {
|
|
@@ -83548,7 +83763,8 @@ async function runOrchestrationPull(cwd2, args) {
|
|
|
83548
83763
|
...e2.payload_schema && Object.keys(e2.payload_schema).length ? { payload_schema: e2.payload_schema } : {},
|
|
83549
83764
|
...e2.settings && Object.keys(e2.settings).length ? { settings: e2.settings } : {}
|
|
83550
83765
|
})),
|
|
83551
|
-
...manifestTriggers.length ? { triggers: manifestTriggers } : {}
|
|
83766
|
+
...manifestTriggers.length ? { triggers: manifestTriggers } : {},
|
|
83767
|
+
...manifestDecisions2?.length ? { decisions: manifestDecisions2 } : {}
|
|
83552
83768
|
};
|
|
83553
83769
|
writeOrchManifest(cwd2, manifest);
|
|
83554
83770
|
writeOrchLink(cwd2, {
|
|
@@ -83574,6 +83790,7 @@ async function runOrchestrationPull(cwd2, args) {
|
|
|
83574
83790
|
payload_schema: e2.payload_schema ?? {}
|
|
83575
83791
|
})),
|
|
83576
83792
|
triggers: syncedScheduleTriggers(cloudScheduleTriggers(cloud, (agentId, fallback) => slugFor(agentId) || fallback)),
|
|
83793
|
+
...decisionBaseline ? { decisions: decisionBaseline } : {},
|
|
83577
83794
|
meta: syncedOrchestrationMeta(cloud)
|
|
83578
83795
|
});
|
|
83579
83796
|
if (missingMembers.length > 0) {
|
|
@@ -83601,6 +83818,83 @@ function handleApiError5(err) {
|
|
|
83601
83818
|
var import_picocolors53 = __toESM(require_picocolors(), 1);
|
|
83602
83819
|
|
|
83603
83820
|
// src/core/orchestration-outgoing.ts
|
|
83821
|
+
function charLength(value2) {
|
|
83822
|
+
return Array.from(value2).length;
|
|
83823
|
+
}
|
|
83824
|
+
function validateDecisions(decisions, memberSlugs, triggerNodeIds) {
|
|
83825
|
+
const ids = new Set;
|
|
83826
|
+
for (const d3 of decisions) {
|
|
83827
|
+
if (ids.has(d3.id))
|
|
83828
|
+
throw new Error(`Decision ${d3.id} is duplicated.`);
|
|
83829
|
+
ids.add(d3.id);
|
|
83830
|
+
}
|
|
83831
|
+
for (const d3 of decisions) {
|
|
83832
|
+
const label2 = `Decision "${d3.name || d3.id}"`;
|
|
83833
|
+
const fail3 = (message) => {
|
|
83834
|
+
throw new Error(`${label2}: ${message}`);
|
|
83835
|
+
};
|
|
83836
|
+
if (!d3.name || charLength(d3.name) > DECISION_LIMITS.name) {
|
|
83837
|
+
fail3(`name must be 1 to ${DECISION_LIMITS.name} characters.`);
|
|
83838
|
+
}
|
|
83839
|
+
if (!d3.model || charLength(d3.model) > DECISION_LIMITS.model) {
|
|
83840
|
+
fail3(`model must be 1 to ${DECISION_LIMITS.model} characters.`);
|
|
83841
|
+
}
|
|
83842
|
+
if (charLength(d3.instructions) > DECISION_LIMITS.instructions) {
|
|
83843
|
+
fail3(`instructions must be ${DECISION_LIMITS.instructions} characters or fewer.`);
|
|
83844
|
+
}
|
|
83845
|
+
const count = d3.answers.length;
|
|
83846
|
+
if (count < DECISION_LIMITS.answersMin || count > DECISION_LIMITS.answersMax) {
|
|
83847
|
+
fail3(`needs ${DECISION_LIMITS.answersMin} to ${DECISION_LIMITS.answersMax} answers (has ${count}).`);
|
|
83848
|
+
}
|
|
83849
|
+
const answers = new Set;
|
|
83850
|
+
for (const a3 of d3.answers) {
|
|
83851
|
+
if (!a3.answer || charLength(a3.answer) > DECISION_LIMITS.answer) {
|
|
83852
|
+
fail3(`each answer must be 1 to ${DECISION_LIMITS.answer} characters.`);
|
|
83853
|
+
}
|
|
83854
|
+
if (answers.has(a3.answer))
|
|
83855
|
+
fail3(`answer "${a3.answer}" appears twice.`);
|
|
83856
|
+
answers.add(a3.answer);
|
|
83857
|
+
if (charLength(a3.criterion) > DECISION_LIMITS.criterion) {
|
|
83858
|
+
fail3(`the criterion for "${a3.answer}" must be ${DECISION_LIMITS.criterion} characters or fewer.`);
|
|
83859
|
+
}
|
|
83860
|
+
}
|
|
83861
|
+
const fed = new Set;
|
|
83862
|
+
for (const nodeId of d3.triggers) {
|
|
83863
|
+
if (!triggerNodeIds.has(nodeId)) {
|
|
83864
|
+
fail3(`from trigger ${nodeId}, which isn't in triggers.`);
|
|
83865
|
+
}
|
|
83866
|
+
if (fed.has(nodeId))
|
|
83867
|
+
fail3(`trigger ${nodeId} is listed twice in from.`);
|
|
83868
|
+
fed.add(nodeId);
|
|
83869
|
+
}
|
|
83870
|
+
const checkTargets = (targets, branch) => {
|
|
83871
|
+
const seen = new Set;
|
|
83872
|
+
for (const t of targets) {
|
|
83873
|
+
if (t.agent !== undefined && !memberSlugs.has(t.agent)) {
|
|
83874
|
+
fail3(`${branch} routes to "${t.agent}", which isn't in members.`);
|
|
83875
|
+
}
|
|
83876
|
+
if (t.decision !== undefined) {
|
|
83877
|
+
if (t.decision === d3.id)
|
|
83878
|
+
fail3(`${branch} routes to the decision itself.`);
|
|
83879
|
+
if (!ids.has(t.decision)) {
|
|
83880
|
+
fail3(`${branch} routes to decision ${t.decision}, which isn't in decisions.`);
|
|
83881
|
+
}
|
|
83882
|
+
}
|
|
83883
|
+
const key2 = t.agent !== undefined ? `agent:${t.agent}` : `decision:${t.decision}`;
|
|
83884
|
+
if (seen.has(key2))
|
|
83885
|
+
fail3(`${branch} routes to ${key2.replace(":", " ")} twice.`);
|
|
83886
|
+
seen.add(key2);
|
|
83887
|
+
}
|
|
83888
|
+
};
|
|
83889
|
+
for (const a3 of d3.answers)
|
|
83890
|
+
checkTargets(a3.to, `answer "${a3.answer}"`);
|
|
83891
|
+
checkTargets(d3.fallback, "fallback");
|
|
83892
|
+
}
|
|
83893
|
+
}
|
|
83894
|
+
function unknownDecisionModels(manifest) {
|
|
83895
|
+
const known = new Set(DECISION_MODELS);
|
|
83896
|
+
return manifestDecisions(manifest).filter((d3) => !known.has(d3.model)).map((d3) => ({ name: d3.name, model: d3.model }));
|
|
83897
|
+
}
|
|
83604
83898
|
function buildOrchestrationGraphPayload(manifest, slugToAgentId) {
|
|
83605
83899
|
const memberSlugs = new Set(manifest.members.map((m3) => m3.slug));
|
|
83606
83900
|
for (const e2 of manifest.edges) {
|
|
@@ -83661,6 +83955,32 @@ function buildOrchestrationGraphPayload(manifest, slugToAgentId) {
|
|
|
83661
83955
|
}))
|
|
83662
83956
|
};
|
|
83663
83957
|
}
|
|
83958
|
+
function buildDecisionsPayload(manifest, slugToAgentId) {
|
|
83959
|
+
const decisions = manifestDecisions(manifest, slugToAgentId);
|
|
83960
|
+
validateDecisions(decisions, new Set(manifest.members.map((m3) => m3.slug)), new Set((manifest.triggers ?? []).map((t) => canonicalUuid2(t.node_id))));
|
|
83961
|
+
return decisions.map((d3) => decisionPayload(d3, slugToAgentId));
|
|
83962
|
+
}
|
|
83963
|
+
function decisionPayload(d3, slugToAgentId) {
|
|
83964
|
+
const route = (answer, t) => ({
|
|
83965
|
+
answer,
|
|
83966
|
+
is_fallback: answer === null,
|
|
83967
|
+
...t.agent !== undefined ? { to_agent_id: slugToAgentId.get(t.agent) } : { to_decision_id: t.decision },
|
|
83968
|
+
description: t.description
|
|
83969
|
+
});
|
|
83970
|
+
return {
|
|
83971
|
+
id: d3.id,
|
|
83972
|
+
name: d3.name,
|
|
83973
|
+
model: d3.model,
|
|
83974
|
+
instructions: d3.instructions,
|
|
83975
|
+
answers: d3.answers.map((a3) => ({ answer: a3.answer, criterion: a3.criterion })),
|
|
83976
|
+
confidence_threshold: d3.confidence_threshold,
|
|
83977
|
+
inputs: d3.triggers.map((nodeId) => ({ from_trigger_node_id: nodeId })),
|
|
83978
|
+
routes: [
|
|
83979
|
+
...d3.answers.flatMap((a3) => a3.to.map((t) => route(a3.answer, t))),
|
|
83980
|
+
...d3.fallback.map((t) => route(null, t))
|
|
83981
|
+
]
|
|
83982
|
+
};
|
|
83983
|
+
}
|
|
83664
83984
|
|
|
83665
83985
|
// src/cli/orchestration-push.ts
|
|
83666
83986
|
function joinList(items) {
|
|
@@ -83788,7 +84108,21 @@ async function runOrchestrationPush(cwd2, args) {
|
|
|
83788
84108
|
const metaOverwrites = metaDecisions.filter((d3) => d3.kind === "push");
|
|
83789
84109
|
const metaKept = metaDecisions.filter((d3) => d3.kind === "keep");
|
|
83790
84110
|
const blockedTriggers = triggerDecision.kind === "blocked" ? triggerDecision : null;
|
|
83791
|
-
|
|
84111
|
+
const localDecisions = manifestDecisions(manifest, slugToAgentId);
|
|
84112
|
+
const cloudDecisionList = cloudDecisions(cloud, slugForAgent);
|
|
84113
|
+
const decisionPlan = cloudDecisionList ? decideDecisionReconcile({
|
|
84114
|
+
local: localDecisions,
|
|
84115
|
+
cloud: cloudDecisionList,
|
|
84116
|
+
baseline: lock?.decisions,
|
|
84117
|
+
force: !!args.force
|
|
84118
|
+
}) : null;
|
|
84119
|
+
const decisionById = new Map([...cloudDecisionList ?? [], ...localDecisions].map((d3) => [d3.id, d3]));
|
|
84120
|
+
const decisionLabelFor = (id) => {
|
|
84121
|
+
const d3 = decisionById.get(id);
|
|
84122
|
+
return d3 ? decisionLabel(d3) : id;
|
|
84123
|
+
};
|
|
84124
|
+
const blockedDecisions = decisionPlan?.kind === "blocked" ? decisionPlan : null;
|
|
84125
|
+
if (blockedTriggers || blockedDecisions || metaConflicts.length > 0) {
|
|
83792
84126
|
const pullTargets = [];
|
|
83793
84127
|
const forceEffects = [];
|
|
83794
84128
|
if (blockedTriggers) {
|
|
@@ -83807,6 +84141,22 @@ async function runOrchestrationPush(cwd2, args) {
|
|
|
83807
84141
|
pullTargets.push(`${total} schedule trigger${total === 1 ? "" : "s"}`);
|
|
83808
84142
|
forceEffects.push("make your local schedule triggers authoritative (deleting the cloud ones above)");
|
|
83809
84143
|
}
|
|
84144
|
+
if (blockedDecisions) {
|
|
84145
|
+
const { unseen, cloudModified, staleArchives } = blockedDecisions;
|
|
84146
|
+
f2.error(unseen.length > 0 ? "Cannot push: the cloud has decisions this folder has never seen." : "Cannot push: decisions changed both locally and in the cloud.");
|
|
84147
|
+
for (const id of unseen) {
|
|
84148
|
+
console.error(` ${import_picocolors53.default.red("!")} ${import_picocolors53.default.bold(decisionLabelFor(id))} ${import_picocolors53.default.dim("(only in the cloud — pushing would delete it)")}`);
|
|
84149
|
+
}
|
|
84150
|
+
for (const id of cloudModified) {
|
|
84151
|
+
console.error(` ${import_picocolors53.default.red("!")} ${import_picocolors53.default.bold(decisionLabelFor(id))} ${import_picocolors53.default.dim("(edited in the cloud — pushing would overwrite that edit)")}`);
|
|
84152
|
+
}
|
|
84153
|
+
for (const id of staleArchives) {
|
|
84154
|
+
console.error(` ${import_picocolors53.default.red("!")} ${import_picocolors53.default.bold(decisionLabelFor(id))} ${import_picocolors53.default.dim("(deleted here, edited in the cloud since — pushing would delete that edit)")}`);
|
|
84155
|
+
}
|
|
84156
|
+
const total = unseen.length + cloudModified.length + staleArchives.length;
|
|
84157
|
+
pullTargets.push(`${total} decision${total === 1 ? "" : "s"}`);
|
|
84158
|
+
forceEffects.push("make your local decisions authoritative (deleting or overwriting the cloud ones above)");
|
|
84159
|
+
}
|
|
83810
84160
|
if (metaConflicts.length > 0) {
|
|
83811
84161
|
f2.error("Cannot push: orchestration metadata changed both locally and in the cloud.");
|
|
83812
84162
|
for (const d3 of metaConflicts) {
|
|
@@ -83839,9 +84189,30 @@ async function runOrchestrationPush(cwd2, args) {
|
|
|
83839
84189
|
f2.warn(`${cloudTriggers.length} cloud schedule trigger${cloudTriggers.length === 1 ? "" : "s"} ${cloudTriggers.length === 1 ? "is" : "are"} not in ${import_picocolors53.default.bold(ORCH_MANIFEST_FILE)} and will be left untouched. Run ${import_picocolors53.default.cyan("brainbase orchestration pull")} to manage ${cloudTriggers.length === 1 ? "it" : "them"} from here.`);
|
|
83840
84190
|
}
|
|
83841
84191
|
const triggerDeletes = triggerDecision.kind === "reconcile" ? triggerDecision.archives : [];
|
|
84192
|
+
const decisionDeletes = decisionPlan?.kind === "reconcile" ? decisionPlan.archives : [];
|
|
84193
|
+
const sendDecisions = decisionPlan?.kind === "reconcile" && (localDecisions.length > 0 || decisionDeletes.length > 0);
|
|
84194
|
+
if (!cloudDecisionList && localDecisions.length > 0) {
|
|
84195
|
+
f2.warn(`The cloud did not return decision nodes for this account, so the ${localDecisions.length} decision${localDecisions.length === 1 ? "" : "s"} in ${import_picocolors53.default.bold(ORCH_MANIFEST_FILE)} ${localDecisions.length === 1 ? "is" : "are"} left out of this push and the cloud's are left alone.`);
|
|
84196
|
+
} else if (decisionPlan?.kind === "skip") {
|
|
84197
|
+
const left = decisionPlan.unseen.length + decisionPlan.cloudModified.length;
|
|
84198
|
+
f2.warn(`${left} cloud decision${left === 1 ? "" : "s"} ${left === 1 ? "is" : "are"} not in ${import_picocolors53.default.bold(ORCH_MANIFEST_FILE)} and will be left untouched. Run ${import_picocolors53.default.cyan("brainbase orchestration pull")} to manage ${left === 1 ? "it" : "them"} from here.`);
|
|
84199
|
+
}
|
|
84200
|
+
let decisionsPayload = [];
|
|
84201
|
+
if (sendDecisions) {
|
|
84202
|
+
try {
|
|
84203
|
+
decisionsPayload = buildDecisionsPayload(manifest, slugToAgentId);
|
|
84204
|
+
} catch (err) {
|
|
84205
|
+
f2.error(err.message);
|
|
84206
|
+
process.exitCode = 1;
|
|
84207
|
+
return;
|
|
84208
|
+
}
|
|
84209
|
+
for (const { name, model } of unknownDecisionModels(manifest)) {
|
|
84210
|
+
f2.warn(`Decision ${import_picocolors53.default.bold(name)} uses model ${import_picocolors53.default.bold(model)}, which is not one the canvas offers. It is pushed as written; if the gateway does not know it, the decision fails when its trigger fires.`);
|
|
84211
|
+
}
|
|
84212
|
+
}
|
|
83842
84213
|
const plan = [""];
|
|
83843
84214
|
plan.push(` ${import_picocolors53.default.bold(link2.name)} ${import_picocolors53.default.dim(`(${link2.orchestration_id})`)}`);
|
|
83844
|
-
plan.push(` ${import_picocolors53.default.dim(`${manifest.members.length} member${manifest.members.length === 1 ? "" : "s"}, ${manifest.edges.length} edge${manifest.edges.length === 1 ? "" : "s"}, ${graph.triggers.length} trigger${graph.triggers.length === 1 ? "" : "s"}`)}`);
|
|
84215
|
+
plan.push(` ${import_picocolors53.default.dim(`${manifest.members.length} member${manifest.members.length === 1 ? "" : "s"}, ${manifest.edges.length} edge${manifest.edges.length === 1 ? "" : "s"}, ${graph.triggers.length} trigger${graph.triggers.length === 1 ? "" : "s"}${sendDecisions ? `, ${decisionsPayload.length} decision${decisionsPayload.length === 1 ? "" : "s"}` : ""}`)}`);
|
|
83845
84216
|
plan.push("");
|
|
83846
84217
|
if (!args.graphOnly) {
|
|
83847
84218
|
plan.push(` ${import_picocolors53.default.dim("per-member agent push:")}`);
|
|
@@ -83857,6 +84228,13 @@ async function runOrchestrationPush(cwd2, args) {
|
|
|
83857
84228
|
}
|
|
83858
84229
|
plan.push("");
|
|
83859
84230
|
}
|
|
84231
|
+
if (decisionDeletes.length) {
|
|
84232
|
+
plan.push(` ${import_picocolors53.default.bold("decisions this push will delete")} ${import_picocolors53.default.dim(`(${decisionDeletes.length})`)}`);
|
|
84233
|
+
for (const id of decisionDeletes) {
|
|
84234
|
+
plan.push(` ${import_picocolors53.default.yellow("⨯")} ${decisionLabelFor(id)}`);
|
|
84235
|
+
}
|
|
84236
|
+
plan.push("");
|
|
84237
|
+
}
|
|
83860
84238
|
if (metaOverwrites.length) {
|
|
83861
84239
|
plan.push(` ${import_picocolors53.default.bold("orchestration metadata this push will overwrite")}`);
|
|
83862
84240
|
for (const m3 of metaOverwrites) {
|
|
@@ -83875,9 +84253,13 @@ async function runOrchestrationPush(cwd2, args) {
|
|
|
83875
84253
|
console.log(plan.join(`
|
|
83876
84254
|
`));
|
|
83877
84255
|
if (!autoProceed(args.yes)) {
|
|
84256
|
+
const deletions = [
|
|
84257
|
+
...triggerDeletes.length ? [`${triggerDeletes.length} schedule trigger${triggerDeletes.length === 1 ? "" : "s"}`] : [],
|
|
84258
|
+
...decisionDeletes.length ? [`${decisionDeletes.length} decision${decisionDeletes.length === 1 ? "" : "s"}`] : []
|
|
84259
|
+
];
|
|
83878
84260
|
const ok = await se({
|
|
83879
|
-
message:
|
|
83880
|
-
initialValue:
|
|
84261
|
+
message: deletions.length ? `Push, deleting ${joinList(deletions)}?` : args.graphOnly ? "Push graph (members, edges + triggers) only?" : "Push each member, then update the graph?",
|
|
84262
|
+
initialValue: deletions.length === 0
|
|
83881
84263
|
});
|
|
83882
84264
|
if (!ensureNotCancelled(ok)) {
|
|
83883
84265
|
$e("Aborted.");
|
|
@@ -83923,9 +84305,11 @@ async function runOrchestrationPush(cwd2, args) {
|
|
|
83923
84305
|
members: graph.memberIds,
|
|
83924
84306
|
edges: graph.edges,
|
|
83925
84307
|
triggers: graph.triggers,
|
|
84308
|
+
...sendDecisions ? { decisions: decisionsPayload } : {},
|
|
83926
84309
|
...lock?.revision != null ? { base_revision: lock.revision } : {}
|
|
83927
84310
|
});
|
|
83928
84311
|
sp.stop(`Graph updated. New cloud revision ${updated.revision}.`);
|
|
84312
|
+
const settledDecisions = sendDecisions ? cloudDecisions(updated, slugForAgent) : decisionPlan?.kind === "reconcile" ? cloudDecisionList : null;
|
|
83929
84313
|
writeOrchSyncState(cwd2, {
|
|
83930
84314
|
schemaVersion: 1,
|
|
83931
84315
|
orchestration_id: updated.id,
|
|
@@ -83943,6 +84327,7 @@ async function runOrchestrationPush(cwd2, args) {
|
|
|
83943
84327
|
payload_schema: e2.payload_schema ?? {}
|
|
83944
84328
|
})),
|
|
83945
84329
|
triggers: triggerDecision.kind === "skip" ? lock?.triggers ?? [] : syncedScheduleTriggers(cloudScheduleTriggers(updated, slugForAgent)),
|
|
84330
|
+
...settledDecisions ? { decisions: syncedDecisions(settledDecisions) } : lock?.decisions ? { decisions: lock.decisions } : {},
|
|
83946
84331
|
meta: nextOrchestrationMeta({
|
|
83947
84332
|
applied: updated,
|
|
83948
84333
|
decisions: metaDecisions,
|
|
@@ -84115,6 +84500,64 @@ async function runOrchestrationStatus(cwd2) {
|
|
|
84115
84500
|
lines.push(` ${import_picocolors54.default.dim(`${left.length} cloud schedule trigger${left.length === 1 ? "" : "s"} not managed by this manifest — push leaves ${left.length === 1 ? "it" : "them"} untouched`)}`);
|
|
84116
84501
|
lines.push("");
|
|
84117
84502
|
}
|
|
84503
|
+
const localDecisions = manifestDecisions(localManifest, new Map([...localSlugByAgentId].map(([agentId, slug]) => [slug, agentId])));
|
|
84504
|
+
const cloudDecisionList = cloudDecisions(cloud, slugForAgent);
|
|
84505
|
+
let decisionsToPush = [];
|
|
84506
|
+
let decisionsToPull = [];
|
|
84507
|
+
if (!cloudDecisionList) {
|
|
84508
|
+
if (localDecisions.length) {
|
|
84509
|
+
lines.push(` ${import_picocolors54.default.dim(`${localDecisions.length} decision${localDecisions.length === 1 ? "" : "s"} in yaml, but the cloud does not show decision nodes to this account — push leaves ${localDecisions.length === 1 ? "it" : "them"} alone`)}`);
|
|
84510
|
+
lines.push("");
|
|
84511
|
+
}
|
|
84512
|
+
} else {
|
|
84513
|
+
const decisionPlan = decideDecisionReconcile({
|
|
84514
|
+
local: localDecisions,
|
|
84515
|
+
cloud: cloudDecisionList,
|
|
84516
|
+
baseline: lock?.decisions,
|
|
84517
|
+
force: false
|
|
84518
|
+
});
|
|
84519
|
+
const byId = new Map([...cloudDecisionList, ...localDecisions].map((d3) => [d3.id, d3]));
|
|
84520
|
+
const label2 = (id) => {
|
|
84521
|
+
const d3 = byId.get(id);
|
|
84522
|
+
return d3 ? decisionLabel(d3) : id;
|
|
84523
|
+
};
|
|
84524
|
+
const localIds = new Set(localDecisions.map((d3) => d3.id));
|
|
84525
|
+
const cloudById = new Map(cloudDecisionList.map((d3) => [d3.id, d3]));
|
|
84526
|
+
decisionsToPush = localDecisions.filter((d3) => {
|
|
84527
|
+
const remote = cloudById.get(d3.id);
|
|
84528
|
+
return !remote || decisionHash(remote) !== decisionHash(d3);
|
|
84529
|
+
});
|
|
84530
|
+
decisionsToPull = cloudDecisionList.filter((d3) => !localIds.has(d3.id));
|
|
84531
|
+
const decisionDeletes = decisionPlan.kind === "reconcile" ? decisionPlan.archives : [];
|
|
84532
|
+
if (decisionsToPush.length || decisionsToPull.length) {
|
|
84533
|
+
lines.push(` ${import_picocolors54.default.bold("decisions")}`);
|
|
84534
|
+
for (const d3 of decisionsToPush) {
|
|
84535
|
+
lines.push(` ${import_picocolors54.default.yellow("→ push")} added/changed in yaml: ${decisionLabel(d3)}`);
|
|
84536
|
+
}
|
|
84537
|
+
for (const d3 of decisionsToPull) {
|
|
84538
|
+
lines.push(decisionDeletes.includes(d3.id) ? ` ${import_picocolors54.default.red("⨯ push deletes")} ${decisionLabel(d3)}` : ` ${import_picocolors54.default.cyan("← pull")} only on cloud: ${decisionLabel(d3)}`);
|
|
84539
|
+
}
|
|
84540
|
+
lines.push("");
|
|
84541
|
+
}
|
|
84542
|
+
if (decisionPlan.kind === "blocked") {
|
|
84543
|
+
lines.push(` ${import_picocolors54.default.bold(import_picocolors54.default.red("decisions: push blocked"))}`);
|
|
84544
|
+
for (const id of decisionPlan.unseen) {
|
|
84545
|
+
lines.push(` ${import_picocolors54.default.red("!")} ${label2(id)} ${import_picocolors54.default.dim("— only in the cloud; pushing would delete it")}`);
|
|
84546
|
+
}
|
|
84547
|
+
for (const id of decisionPlan.cloudModified) {
|
|
84548
|
+
lines.push(` ${import_picocolors54.default.red("!")} ${label2(id)} ${import_picocolors54.default.dim("— edited in the cloud; pushing would overwrite that edit")}`);
|
|
84549
|
+
}
|
|
84550
|
+
for (const id of decisionPlan.staleArchives) {
|
|
84551
|
+
lines.push(` ${import_picocolors54.default.red("!")} ${label2(id)} ${import_picocolors54.default.dim("— deleted here, edited in the cloud since; pushing would delete that edit")}`);
|
|
84552
|
+
}
|
|
84553
|
+
lines.push(` ${import_picocolors54.default.dim("run")} ${import_picocolors54.default.cyan("brainbase orchestration pull")} ${import_picocolors54.default.dim("first")}`);
|
|
84554
|
+
lines.push("");
|
|
84555
|
+
} else if (decisionPlan.kind === "skip") {
|
|
84556
|
+
const left = untouchedRows(decisionPlan);
|
|
84557
|
+
lines.push(` ${import_picocolors54.default.dim(`${left.length} cloud decision${left.length === 1 ? "" : "s"} not managed by this manifest — push leaves ${left.length === 1 ? "it" : "them"} untouched`)}`);
|
|
84558
|
+
lines.push("");
|
|
84559
|
+
}
|
|
84560
|
+
}
|
|
84118
84561
|
const lockByAgentId = new Map((lock?.members ?? []).map((m3) => [m3.agent_id, m3]));
|
|
84119
84562
|
const memberDrift = [];
|
|
84120
84563
|
for (const m3 of cloud.members) {
|
|
@@ -84148,7 +84591,7 @@ async function runOrchestrationStatus(cwd2) {
|
|
|
84148
84591
|
lines.push(` ${import_picocolors54.default.cyan("← pull")} cloud changed since last pull: ${import_picocolors54.default.dim(`lock ${lock.revision} → cloud ${cloud.revision}`)}`);
|
|
84149
84592
|
lines.push("");
|
|
84150
84593
|
}
|
|
84151
|
-
if (!membersAdded.length && !membersRemoved.length && !edgesAdded.length && !edgesRemoved.length && !triggersToPush.length && !triggersToPull.length && !metaDecisions.length && !memberDrift.length && !revisionDrift) {
|
|
84594
|
+
if (!membersAdded.length && !membersRemoved.length && !edgesAdded.length && !edgesRemoved.length && !triggersToPush.length && !triggersToPull.length && !decisionsToPush.length && !decisionsToPull.length && !metaDecisions.length && !memberDrift.length && !revisionDrift) {
|
|
84152
84595
|
lines.push(` ${import_picocolors54.default.green("✓")} everything is in sync`);
|
|
84153
84596
|
lines.push("");
|
|
84154
84597
|
console.log(lines.join(`
|
|
@@ -84503,6 +84946,10 @@ async function runOrchestrationCreate(cwd2, args) {
|
|
|
84503
84946
|
payload_schema: e2.payload_schema ?? {}
|
|
84504
84947
|
}))
|
|
84505
84948
|
});
|
|
84949
|
+
const decisionCount = manifest.decisions?.length ?? 0;
|
|
84950
|
+
if (decisionCount) {
|
|
84951
|
+
f2.info(`Run ${import_picocolors57.default.cyan("brainbase orchestration push")} to add the ${decisionCount} decision${decisionCount === 1 ? "" : "s"} in this manifest.`);
|
|
84952
|
+
}
|
|
84506
84953
|
$e(`Created ${created.name} at revision ${created.revision}.`);
|
|
84507
84954
|
} catch (err) {
|
|
84508
84955
|
sp.stop("Failed.");
|
|
@@ -97544,6 +97991,806 @@ async function runBenchmark(sub, args, write3 = (value2) => process.stdout.write
|
|
|
97544
97991
|
}
|
|
97545
97992
|
}
|
|
97546
97993
|
|
|
97994
|
+
// src/cli/decide.ts
|
|
97995
|
+
import fs95 from "node:fs";
|
|
97996
|
+
import path107 from "node:path";
|
|
97997
|
+
|
|
97998
|
+
// src/core/decide-api.ts
|
|
97999
|
+
import path105 from "node:path";
|
|
98000
|
+
function decideKeysFile() {
|
|
98001
|
+
return path105.join(process.env.BRAINBASE_HOME ?? BRAINBASE_HOME, "decide-keys.json");
|
|
98002
|
+
}
|
|
98003
|
+
var ATTEMPTS = 3;
|
|
98004
|
+
var RETRY_STATUSES = new Set([429, 502, 503, 504]);
|
|
98005
|
+
var MAX_RETRY_DELAY_MS = 30000;
|
|
98006
|
+
var REQUEST_TIMEOUT_MS3 = 5 * 60000;
|
|
98007
|
+
|
|
98008
|
+
class DecideHttpError extends Error {
|
|
98009
|
+
status;
|
|
98010
|
+
constructor(message3, status) {
|
|
98011
|
+
super(message3);
|
|
98012
|
+
this.status = status;
|
|
98013
|
+
this.name = "DecideHttpError";
|
|
98014
|
+
}
|
|
98015
|
+
}
|
|
98016
|
+
function cacheKey(base2, agentId) {
|
|
98017
|
+
return `${base2}|${agentId}`;
|
|
98018
|
+
}
|
|
98019
|
+
async function resolveDecideCredential(cwd2, agentFlag) {
|
|
98020
|
+
const base2 = proxyBaseUrl(authStatus().session ?? null);
|
|
98021
|
+
const taskKey = process.env.BB_TASK_KEY?.trim();
|
|
98022
|
+
if (taskKey)
|
|
98023
|
+
return { key: taskKey, base: base2, source: "task_key" };
|
|
98024
|
+
const agentId = agentFlag?.trim() || readLink(cwd2)?.agent_id;
|
|
98025
|
+
if (!agentId) {
|
|
98026
|
+
throw new DecideHttpError("No agent to bill. Run inside a linked folder, or pass --agent <id> (see `brainbase agent list`).", null);
|
|
98027
|
+
}
|
|
98028
|
+
const cached2 = readJsonOr(decideKeysFile(), {})[cacheKey(base2, agentId)];
|
|
98029
|
+
if (cached2?.key)
|
|
98030
|
+
return { key: cached2.key, base: base2, source: "agent_key", agentId };
|
|
98031
|
+
return mintAgentKey(base2, agentId);
|
|
98032
|
+
}
|
|
98033
|
+
async function mintAgentKey(base2, agentId) {
|
|
98034
|
+
const created = await api.createCliKey(agentId, { name: "brainbase decide" });
|
|
98035
|
+
const cache3 = readJsonOr(decideKeysFile(), {});
|
|
98036
|
+
cache3[cacheKey(base2, agentId)] = { key: created.key, key_id: created.id };
|
|
98037
|
+
writeJsonAtomic(decideKeysFile(), cache3);
|
|
98038
|
+
return { key: created.key, base: base2, source: "agent_key", agentId };
|
|
98039
|
+
}
|
|
98040
|
+
async function replaceAgentKey(credential) {
|
|
98041
|
+
if (credential.source !== "agent_key" || !credential.agentId)
|
|
98042
|
+
return credential;
|
|
98043
|
+
const cache3 = readJsonOr(decideKeysFile(), {});
|
|
98044
|
+
delete cache3[cacheKey(credential.base, credential.agentId)];
|
|
98045
|
+
writeJsonAtomic(decideKeysFile(), cache3);
|
|
98046
|
+
return mintAgentKey(credential.base, credential.agentId);
|
|
98047
|
+
}
|
|
98048
|
+
function retryDelayMs(attempt2, retryAfter) {
|
|
98049
|
+
const seconds = retryAfter === null ? Number.NaN : Number(retryAfter);
|
|
98050
|
+
const base2 = Number.isFinite(seconds) ? seconds * 1000 : 1000 * 2 ** attempt2;
|
|
98051
|
+
return Math.min(Math.max(base2, 0), MAX_RETRY_DELAY_MS) + Math.random() * 500;
|
|
98052
|
+
}
|
|
98053
|
+
function errorMessage2(status, text4) {
|
|
98054
|
+
try {
|
|
98055
|
+
const body = JSON.parse(text4);
|
|
98056
|
+
const message3 = body.error?.message ?? body.detail;
|
|
98057
|
+
if (typeof message3 === "string" && message3)
|
|
98058
|
+
return message3;
|
|
98059
|
+
} catch {}
|
|
98060
|
+
return `HTTP ${status}`;
|
|
98061
|
+
}
|
|
98062
|
+
async function postDecide(credential, body, options = {}) {
|
|
98063
|
+
const sleep3 = options.sleep ?? ((ms2) => new Promise((resolve) => setTimeout(resolve, ms2)));
|
|
98064
|
+
const payload = JSON.stringify(body);
|
|
98065
|
+
let failure = new DecideHttpError("The decide request failed", null);
|
|
98066
|
+
for (let attempt2 = 0;attempt2 < ATTEMPTS; attempt2 += 1) {
|
|
98067
|
+
let retryAfter = null;
|
|
98068
|
+
try {
|
|
98069
|
+
const response = await fetch(`${credential.base}/v1/decide`, {
|
|
98070
|
+
method: "POST",
|
|
98071
|
+
headers: {
|
|
98072
|
+
Authorization: `Bearer ${credential.key}`,
|
|
98073
|
+
"Content-Type": "application/json"
|
|
98074
|
+
},
|
|
98075
|
+
body: payload,
|
|
98076
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
|
|
98077
|
+
});
|
|
98078
|
+
const text4 = await response.text();
|
|
98079
|
+
if (response.ok) {
|
|
98080
|
+
try {
|
|
98081
|
+
return JSON.parse(text4);
|
|
98082
|
+
} catch {
|
|
98083
|
+
throw new DecideHttpError("The server sent an unreadable response", response.status);
|
|
98084
|
+
}
|
|
98085
|
+
}
|
|
98086
|
+
failure = new DecideHttpError(errorMessage2(response.status, text4), response.status);
|
|
98087
|
+
if (!RETRY_STATUSES.has(response.status))
|
|
98088
|
+
throw failure;
|
|
98089
|
+
retryAfter = response.headers.get("retry-after");
|
|
98090
|
+
} catch (error2) {
|
|
98091
|
+
if (error2 instanceof DecideHttpError)
|
|
98092
|
+
throw error2;
|
|
98093
|
+
failure = new DecideHttpError(`Network error: ${error2.message}`, null);
|
|
98094
|
+
}
|
|
98095
|
+
if (attempt2 + 1 < ATTEMPTS)
|
|
98096
|
+
await sleep3(retryDelayMs(attempt2, retryAfter));
|
|
98097
|
+
}
|
|
98098
|
+
throw failure;
|
|
98099
|
+
}
|
|
98100
|
+
|
|
98101
|
+
// src/core/decide-items.ts
|
|
98102
|
+
import fs94 from "node:fs";
|
|
98103
|
+
import path106 from "node:path";
|
|
98104
|
+
|
|
98105
|
+
class DecideInputError extends Error {
|
|
98106
|
+
constructor(message3) {
|
|
98107
|
+
super(message3);
|
|
98108
|
+
this.name = "DecideInputError";
|
|
98109
|
+
}
|
|
98110
|
+
}
|
|
98111
|
+
var GLOB_CHARS = /[*?[{]/;
|
|
98112
|
+
function fileId(cwd2, file) {
|
|
98113
|
+
return path106.relative(cwd2, file).split(path106.sep).join("/") || path106.basename(file);
|
|
98114
|
+
}
|
|
98115
|
+
function isHidden2(name) {
|
|
98116
|
+
return name.startsWith(".") || name === "node_modules";
|
|
98117
|
+
}
|
|
98118
|
+
function appendAll(into, from) {
|
|
98119
|
+
for (const value2 of from)
|
|
98120
|
+
into.push(value2);
|
|
98121
|
+
}
|
|
98122
|
+
function walk(dir, out = []) {
|
|
98123
|
+
for (const entry of fs94.readdirSync(dir, { withFileTypes: true })) {
|
|
98124
|
+
if (isHidden2(entry.name))
|
|
98125
|
+
continue;
|
|
98126
|
+
const full = path106.join(dir, entry.name);
|
|
98127
|
+
if (entry.isDirectory())
|
|
98128
|
+
walk(full, out);
|
|
98129
|
+
else if (entry.isFile())
|
|
98130
|
+
out.push(full);
|
|
98131
|
+
else if (entry.isSymbolicLink()) {
|
|
98132
|
+
try {
|
|
98133
|
+
if (fs94.statSync(full).isFile())
|
|
98134
|
+
out.push(full);
|
|
98135
|
+
} catch {}
|
|
98136
|
+
}
|
|
98137
|
+
}
|
|
98138
|
+
return out;
|
|
98139
|
+
}
|
|
98140
|
+
function walkSorted(dir) {
|
|
98141
|
+
return walk(dir).sort();
|
|
98142
|
+
}
|
|
98143
|
+
function globToRegExp(pattern2) {
|
|
98144
|
+
let source = "";
|
|
98145
|
+
for (let i = 0;i < pattern2.length; i += 1) {
|
|
98146
|
+
const char = pattern2[i];
|
|
98147
|
+
if (char === "*") {
|
|
98148
|
+
if (pattern2[i + 1] === "*") {
|
|
98149
|
+
const slashFollows = pattern2[i + 2] === "/";
|
|
98150
|
+
source += slashFollows ? "(?:.*/)?" : ".*";
|
|
98151
|
+
i += slashFollows ? 2 : 1;
|
|
98152
|
+
} else {
|
|
98153
|
+
source += "[^/]*";
|
|
98154
|
+
}
|
|
98155
|
+
} else if (char === "?") {
|
|
98156
|
+
source += "[^/]";
|
|
98157
|
+
} else if (char === "{") {
|
|
98158
|
+
const close = pattern2.indexOf("}", i);
|
|
98159
|
+
if (close === -1) {
|
|
98160
|
+
source += "\\{";
|
|
98161
|
+
continue;
|
|
98162
|
+
}
|
|
98163
|
+
const choices = pattern2.slice(i + 1, close).split(",");
|
|
98164
|
+
source += `(?:${choices.map((choice) => globToRegExp(choice).source.slice(1, -1)).join("|")})`;
|
|
98165
|
+
i = close;
|
|
98166
|
+
} else if (char === "[") {
|
|
98167
|
+
const close = pattern2.indexOf("]", i + 1);
|
|
98168
|
+
if (close === -1) {
|
|
98169
|
+
source += "\\[";
|
|
98170
|
+
continue;
|
|
98171
|
+
}
|
|
98172
|
+
let body = pattern2.slice(i + 1, close);
|
|
98173
|
+
if (body.startsWith("!"))
|
|
98174
|
+
body = `^${body.slice(1)}`;
|
|
98175
|
+
source += `[${body.replace(/\\/g, "\\\\")}]`;
|
|
98176
|
+
i = close;
|
|
98177
|
+
} else {
|
|
98178
|
+
source += char.replace(/[.+^$()|\\/]/g, "\\$&");
|
|
98179
|
+
}
|
|
98180
|
+
}
|
|
98181
|
+
return new RegExp(`^${source}$`);
|
|
98182
|
+
}
|
|
98183
|
+
function expandGlob(cwd2, pattern2) {
|
|
98184
|
+
const absolute = path106.isAbsolute(pattern2);
|
|
98185
|
+
const segments = pattern2.split("/");
|
|
98186
|
+
const fixed = [];
|
|
98187
|
+
for (const segment of segments) {
|
|
98188
|
+
if (GLOB_CHARS.test(segment))
|
|
98189
|
+
break;
|
|
98190
|
+
fixed.push(segment);
|
|
98191
|
+
}
|
|
98192
|
+
if (fixed.length === segments.length) {
|
|
98193
|
+
return [path106.resolve(cwd2, pattern2)];
|
|
98194
|
+
}
|
|
98195
|
+
const root = path106.resolve(cwd2, fixed.join("/") || ".");
|
|
98196
|
+
if (!fs94.existsSync(root) || !fs94.statSync(root).isDirectory())
|
|
98197
|
+
return [];
|
|
98198
|
+
const matcher = globToRegExp(absolute ? pattern2 : path106.posix.normalize(segments.join("/")));
|
|
98199
|
+
return walkSorted(root).filter((file) => {
|
|
98200
|
+
const candidate = absolute ? file.split(path106.sep).join("/") : fileId(cwd2, file);
|
|
98201
|
+
return matcher.test(candidate);
|
|
98202
|
+
});
|
|
98203
|
+
}
|
|
98204
|
+
function readFileItem(cwd2, file) {
|
|
98205
|
+
const id = fileId(cwd2, file);
|
|
98206
|
+
let bytes;
|
|
98207
|
+
try {
|
|
98208
|
+
bytes = fs94.readFileSync(file);
|
|
98209
|
+
} catch (error2) {
|
|
98210
|
+
return { id, error: `Could not read the file: ${error2.message}` };
|
|
98211
|
+
}
|
|
98212
|
+
if (bytes.includes(0))
|
|
98213
|
+
return { id, error: "Not a text file" };
|
|
98214
|
+
let text4;
|
|
98215
|
+
try {
|
|
98216
|
+
text4 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
98217
|
+
} catch {
|
|
98218
|
+
return { id, error: "Not UTF-8 text" };
|
|
98219
|
+
}
|
|
98220
|
+
if (!text4.trim())
|
|
98221
|
+
return { id, error: "Empty file" };
|
|
98222
|
+
return { id, text: text4 };
|
|
98223
|
+
}
|
|
98224
|
+
function fileItems(cwd2, paths, globs) {
|
|
98225
|
+
const files = [];
|
|
98226
|
+
for (const entry of paths) {
|
|
98227
|
+
const full = path106.resolve(cwd2, entry);
|
|
98228
|
+
let stat;
|
|
98229
|
+
try {
|
|
98230
|
+
stat = fs94.statSync(full);
|
|
98231
|
+
} catch {
|
|
98232
|
+
throw new DecideInputError(`No such file or directory: ${entry}`);
|
|
98233
|
+
}
|
|
98234
|
+
if (stat.isDirectory())
|
|
98235
|
+
appendAll(files, walkSorted(full));
|
|
98236
|
+
else
|
|
98237
|
+
files.push(full);
|
|
98238
|
+
}
|
|
98239
|
+
for (const pattern2 of globs) {
|
|
98240
|
+
const matched = expandGlob(cwd2, pattern2);
|
|
98241
|
+
if (matched.length === 0 || !matched.some((file) => fs94.existsSync(file))) {
|
|
98242
|
+
throw new DecideInputError(`No files match ${pattern2}`);
|
|
98243
|
+
}
|
|
98244
|
+
appendAll(files, matched);
|
|
98245
|
+
}
|
|
98246
|
+
return [...new Set(files)];
|
|
98247
|
+
}
|
|
98248
|
+
function readFileItems(cwd2, files) {
|
|
98249
|
+
const set3 = { items: [], failures: [] };
|
|
98250
|
+
for (const file of files) {
|
|
98251
|
+
const item = readFileItem(cwd2, file);
|
|
98252
|
+
if ("error" in item)
|
|
98253
|
+
set3.failures.push(item);
|
|
98254
|
+
else
|
|
98255
|
+
set3.items.push(item);
|
|
98256
|
+
}
|
|
98257
|
+
return set3;
|
|
98258
|
+
}
|
|
98259
|
+
function isRecord2(value2) {
|
|
98260
|
+
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
98261
|
+
}
|
|
98262
|
+
function rowItem(row, options, fallbackId) {
|
|
98263
|
+
const idField = options.idField ?? "id";
|
|
98264
|
+
const rawId = isRecord2(row) ? row[idField] : undefined;
|
|
98265
|
+
let id = fallbackId;
|
|
98266
|
+
if (rawId !== undefined && rawId !== null && rawId !== "")
|
|
98267
|
+
id = String(rawId);
|
|
98268
|
+
else if (options.idField)
|
|
98269
|
+
return { id: fallbackId, error: `Missing ${options.idField}` };
|
|
98270
|
+
if (options.textFields.length === 0)
|
|
98271
|
+
return { id, json: row };
|
|
98272
|
+
if (!isRecord2(row))
|
|
98273
|
+
return { id, error: "Not an object, so --text-field has nothing to select" };
|
|
98274
|
+
if (options.textFields.length === 1) {
|
|
98275
|
+
const field2 = options.textFields[0];
|
|
98276
|
+
const value2 = row[field2];
|
|
98277
|
+
if (value2 === undefined || value2 === null || value2 === "")
|
|
98278
|
+
return { id, error: `Empty ${field2}` };
|
|
98279
|
+
return { id, text: typeof value2 === "string" ? value2 : JSON.stringify(value2) };
|
|
98280
|
+
}
|
|
98281
|
+
const missing = options.textFields.filter((field2) => row[field2] === undefined);
|
|
98282
|
+
if (missing.length)
|
|
98283
|
+
return { id, error: `Missing ${missing.join(", ")}` };
|
|
98284
|
+
const picked = {};
|
|
98285
|
+
for (const field2 of options.textFields)
|
|
98286
|
+
picked[field2] = row[field2];
|
|
98287
|
+
return { id, json: picked };
|
|
98288
|
+
}
|
|
98289
|
+
function sourceLabel(cwd2, file) {
|
|
98290
|
+
return fileId(cwd2, path106.resolve(cwd2, file));
|
|
98291
|
+
}
|
|
98292
|
+
function readSource(cwd2, file) {
|
|
98293
|
+
try {
|
|
98294
|
+
return fs94.readFileSync(path106.resolve(cwd2, file), "utf8");
|
|
98295
|
+
} catch (error2) {
|
|
98296
|
+
throw new DecideInputError(`Could not read ${file}: ${error2.message}`);
|
|
98297
|
+
}
|
|
98298
|
+
}
|
|
98299
|
+
function jsonlItems(cwd2, file, options) {
|
|
98300
|
+
const set3 = { items: [], failures: [] };
|
|
98301
|
+
const label3 = sourceLabel(cwd2, file);
|
|
98302
|
+
readSource(cwd2, file).split(`
|
|
98303
|
+
`).forEach((line3, index) => {
|
|
98304
|
+
if (!line3.trim())
|
|
98305
|
+
return;
|
|
98306
|
+
const fallbackId = `${label3}:${index + 1}`;
|
|
98307
|
+
let row;
|
|
98308
|
+
try {
|
|
98309
|
+
row = JSON.parse(line3);
|
|
98310
|
+
} catch {
|
|
98311
|
+
set3.failures.push({ id: fallbackId, error: "Not valid JSON" });
|
|
98312
|
+
return;
|
|
98313
|
+
}
|
|
98314
|
+
const item = rowItem(row, options, fallbackId);
|
|
98315
|
+
if ("error" in item)
|
|
98316
|
+
set3.failures.push(item);
|
|
98317
|
+
else
|
|
98318
|
+
set3.items.push(item);
|
|
98319
|
+
});
|
|
98320
|
+
return set3;
|
|
98321
|
+
}
|
|
98322
|
+
function parseCsv(text4) {
|
|
98323
|
+
const rows = [];
|
|
98324
|
+
let row = [];
|
|
98325
|
+
let field2 = "";
|
|
98326
|
+
let quoted = false;
|
|
98327
|
+
for (let i = 0;i < text4.length; i += 1) {
|
|
98328
|
+
const char = text4[i];
|
|
98329
|
+
if (quoted) {
|
|
98330
|
+
if (char === '"' && text4[i + 1] === '"') {
|
|
98331
|
+
field2 += '"';
|
|
98332
|
+
i += 1;
|
|
98333
|
+
} else if (char === '"') {
|
|
98334
|
+
quoted = false;
|
|
98335
|
+
} else {
|
|
98336
|
+
field2 += char;
|
|
98337
|
+
}
|
|
98338
|
+
} else if (char === '"' && field2 === "") {
|
|
98339
|
+
quoted = true;
|
|
98340
|
+
} else if (char === ",") {
|
|
98341
|
+
row.push(field2);
|
|
98342
|
+
field2 = "";
|
|
98343
|
+
} else if (char === `
|
|
98344
|
+
` || char === "\r") {
|
|
98345
|
+
if (char === "\r" && text4[i + 1] === `
|
|
98346
|
+
`)
|
|
98347
|
+
i += 1;
|
|
98348
|
+
row.push(field2);
|
|
98349
|
+
rows.push(row);
|
|
98350
|
+
row = [];
|
|
98351
|
+
field2 = "";
|
|
98352
|
+
} else {
|
|
98353
|
+
field2 += char;
|
|
98354
|
+
}
|
|
98355
|
+
}
|
|
98356
|
+
if (field2 !== "" || row.length > 0) {
|
|
98357
|
+
row.push(field2);
|
|
98358
|
+
rows.push(row);
|
|
98359
|
+
}
|
|
98360
|
+
return rows.filter((cells) => cells.some((cell) => cell !== ""));
|
|
98361
|
+
}
|
|
98362
|
+
function csvItems(cwd2, file, options) {
|
|
98363
|
+
const set3 = { items: [], failures: [] };
|
|
98364
|
+
const label3 = sourceLabel(cwd2, file);
|
|
98365
|
+
const [header, ...rows] = parseCsv(readSource(cwd2, file).replace(/^/, ""));
|
|
98366
|
+
if (!header)
|
|
98367
|
+
return set3;
|
|
98368
|
+
rows.forEach((cells, index) => {
|
|
98369
|
+
const row = {};
|
|
98370
|
+
header.forEach((name, column) => {
|
|
98371
|
+
row[name] = cells[column] ?? "";
|
|
98372
|
+
});
|
|
98373
|
+
const item = rowItem(row, options, `${label3}:${index + 2}`);
|
|
98374
|
+
if ("error" in item)
|
|
98375
|
+
set3.failures.push(item);
|
|
98376
|
+
else
|
|
98377
|
+
set3.items.push(item);
|
|
98378
|
+
});
|
|
98379
|
+
return set3;
|
|
98380
|
+
}
|
|
98381
|
+
function readResults(file) {
|
|
98382
|
+
const rows = new Map;
|
|
98383
|
+
if (!fs94.existsSync(file))
|
|
98384
|
+
return rows;
|
|
98385
|
+
for (const line3 of fs94.readFileSync(file, "utf8").split(`
|
|
98386
|
+
`)) {
|
|
98387
|
+
if (!line3.trim())
|
|
98388
|
+
continue;
|
|
98389
|
+
try {
|
|
98390
|
+
const row = JSON.parse(line3);
|
|
98391
|
+
if (row && typeof row.id === "string")
|
|
98392
|
+
rows.set(row.id, row);
|
|
98393
|
+
} catch {}
|
|
98394
|
+
}
|
|
98395
|
+
return rows;
|
|
98396
|
+
}
|
|
98397
|
+
function repairResultsTail(file) {
|
|
98398
|
+
if (!fs94.existsSync(file))
|
|
98399
|
+
return;
|
|
98400
|
+
const text4 = fs94.readFileSync(file, "utf8");
|
|
98401
|
+
if (!text4 || text4.endsWith(`
|
|
98402
|
+
`))
|
|
98403
|
+
return;
|
|
98404
|
+
const cut = text4.lastIndexOf(`
|
|
98405
|
+
`) + 1;
|
|
98406
|
+
const tail2 = text4.slice(cut);
|
|
98407
|
+
let complete = false;
|
|
98408
|
+
try {
|
|
98409
|
+
const row = JSON.parse(tail2);
|
|
98410
|
+
complete = Boolean(row) && typeof row.id === "string";
|
|
98411
|
+
} catch {
|
|
98412
|
+
complete = false;
|
|
98413
|
+
}
|
|
98414
|
+
if (complete)
|
|
98415
|
+
fs94.appendFileSync(file, `
|
|
98416
|
+
`);
|
|
98417
|
+
else
|
|
98418
|
+
fs94.truncateSync(file, Buffer.byteLength(text4.slice(0, cut)));
|
|
98419
|
+
}
|
|
98420
|
+
function parseWhere(value2) {
|
|
98421
|
+
if (value2 === "failed")
|
|
98422
|
+
return { kind: "failed" };
|
|
98423
|
+
if (value2 === "uncertain")
|
|
98424
|
+
return { kind: "uncertain" };
|
|
98425
|
+
const split2 = value2.indexOf("=");
|
|
98426
|
+
if (split2 > 0 && split2 < value2.length - 1) {
|
|
98427
|
+
return { kind: "answer", question: value2.slice(0, split2), answer: value2.slice(split2 + 1) };
|
|
98428
|
+
}
|
|
98429
|
+
throw new DecideInputError(`--where takes failed, uncertain or <question>=<answer>, not ${value2}`);
|
|
98430
|
+
}
|
|
98431
|
+
function rowMatches(row, where) {
|
|
98432
|
+
switch (where.kind) {
|
|
98433
|
+
case "failed":
|
|
98434
|
+
return row.error !== undefined;
|
|
98435
|
+
case "uncertain":
|
|
98436
|
+
return row.error === undefined && row.uncertain === true;
|
|
98437
|
+
case "answer":
|
|
98438
|
+
return row.error === undefined && row.answers?.[where.question]?.choice === where.answer;
|
|
98439
|
+
}
|
|
98440
|
+
}
|
|
98441
|
+
|
|
98442
|
+
// src/cli/decide.ts
|
|
98443
|
+
var DEFAULT_OUT = "decide-results.jsonl";
|
|
98444
|
+
var MAX_ITEMS_PER_REQUEST = 200;
|
|
98445
|
+
var MAX_REQUEST_BYTES = 6 * 1024 * 1024;
|
|
98446
|
+
var DEFAULT_CONCURRENCY = 4;
|
|
98447
|
+
var MAX_CONCURRENCY = 16;
|
|
98448
|
+
var SUMMARY_UNCERTAIN_IDS = 20;
|
|
98449
|
+
var SUMMARY_FAILED = 10;
|
|
98450
|
+
var STOP_STATUSES = new Set([401, 402, 403, 404]);
|
|
98451
|
+
var OPTIONS = [
|
|
98452
|
+
{ key: "questions", names: ["--questions", "-q"], kind: "value" },
|
|
98453
|
+
{ key: "items", names: ["--items"], kind: "value", multiple: true },
|
|
98454
|
+
{ key: "jsonl", names: ["--jsonl"], kind: "value", multiple: true },
|
|
98455
|
+
{ key: "csv", names: ["--csv"], kind: "value", multiple: true },
|
|
98456
|
+
{ key: "idField", names: ["--id-field"], kind: "value" },
|
|
98457
|
+
{ key: "textField", names: ["--text-field"], kind: "value", multiple: true },
|
|
98458
|
+
{ key: "model", names: ["--model"], kind: "value" },
|
|
98459
|
+
{ key: "minConfidence", names: ["--min-confidence"], kind: "value" },
|
|
98460
|
+
{ key: "out", names: ["--out", "-o"], kind: "value" },
|
|
98461
|
+
{ key: "resume", names: ["--resume"], kind: "boolean" },
|
|
98462
|
+
{ key: "overwrite", names: ["--overwrite"], kind: "boolean" },
|
|
98463
|
+
{ key: "selectFrom", names: ["--select-from"], kind: "value" },
|
|
98464
|
+
{ key: "where", names: ["--where"], kind: "value" },
|
|
98465
|
+
{ key: "agent", names: ["--agent"], kind: "value" },
|
|
98466
|
+
{ key: "concurrency", names: ["--concurrency"], kind: "value" },
|
|
98467
|
+
{ key: "help", names: ["--help", "-h"], kind: "boolean" }
|
|
98468
|
+
];
|
|
98469
|
+
var DECIDE_USAGE = `Usage: brainbase decide --questions <file.json> <sources...> [options]
|
|
98470
|
+
|
|
98471
|
+
Answer the same decision-model questions over many items. Items are read on
|
|
98472
|
+
this machine and never pass through your agent's context.
|
|
98473
|
+
|
|
98474
|
+
Sources (at least one):
|
|
98475
|
+
<path>... files, or directories (every file under them)
|
|
98476
|
+
--items <glob> files matching a glob; quote it ('docs/**/*.md')
|
|
98477
|
+
--jsonl <file> one item per line
|
|
98478
|
+
--csv <file> one item per row; the first row names the fields
|
|
98479
|
+
--id-field <name> JSONL/CSV field holding each item's id
|
|
98480
|
+
(default: id when present, else file:line)
|
|
98481
|
+
--text-field <name> field to judge; repeat for several (a row
|
|
98482
|
+
missing any of them fails without being sent)
|
|
98483
|
+
(default: the whole row)
|
|
98484
|
+
|
|
98485
|
+
Questions:
|
|
98486
|
+
--questions, -q <file> JSON object of System One questions, e.g.
|
|
98487
|
+
{"team": {"type": "choice", "instructions": "Which team?",
|
|
98488
|
+
"criteria": {"billing": "...", "infra": "..."}}}
|
|
98489
|
+
--model <slug> jev, kev-4b, clef, clef-flash or gpt-6-luna
|
|
98490
|
+
(default: the server's default)
|
|
98491
|
+
--min-confidence <0-1> mark answers below this confidence as uncertain
|
|
98492
|
+
|
|
98493
|
+
Results:
|
|
98494
|
+
--out, -o <file> one JSON line per item (default: ${DEFAULT_OUT})
|
|
98495
|
+
--resume skip ids already answered in --out; append the rest
|
|
98496
|
+
--overwrite replace an existing --out
|
|
98497
|
+
--select-from <file> only items whose row in this earlier results file
|
|
98498
|
+
--where <filter> matches: failed, uncertain or <question>=<answer>
|
|
98499
|
+
|
|
98500
|
+
Other:
|
|
98501
|
+
--agent <id> agent to bill outside a Brainbase task
|
|
98502
|
+
(default: this folder's linked agent)
|
|
98503
|
+
--concurrency <n> requests in flight, 1-${MAX_CONCURRENCY} (default: ${DEFAULT_CONCURRENCY})
|
|
98504
|
+
|
|
98505
|
+
Prints one JSON summary on stdout. Exits 0 when every item was sent (check
|
|
98506
|
+
counts.failed), 1 when the run stopped early (rerun with --resume), 2 for a
|
|
98507
|
+
usage error.
|
|
98508
|
+
`;
|
|
98509
|
+
function usageError2(stderr, message3) {
|
|
98510
|
+
stderr(`brainbase decide: ${message3}
|
|
98511
|
+
Run \`brainbase decide --help\` for usage.
|
|
98512
|
+
`);
|
|
98513
|
+
return 2;
|
|
98514
|
+
}
|
|
98515
|
+
function readQuestions(cwd2, file) {
|
|
98516
|
+
let parsed;
|
|
98517
|
+
try {
|
|
98518
|
+
parsed = JSON.parse(fs95.readFileSync(path107.resolve(cwd2, file), "utf8"));
|
|
98519
|
+
} catch (error2) {
|
|
98520
|
+
throw new DecideInputError(`Could not read questions from ${file}: ${error2.message}`);
|
|
98521
|
+
}
|
|
98522
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || !Object.keys(parsed).length) {
|
|
98523
|
+
throw new DecideInputError(`${file} must hold a JSON object of questions`);
|
|
98524
|
+
}
|
|
98525
|
+
return parsed;
|
|
98526
|
+
}
|
|
98527
|
+
function mergeSets(sets) {
|
|
98528
|
+
const merged = { items: [], failures: [] };
|
|
98529
|
+
for (const set3 of sets) {
|
|
98530
|
+
appendAll(merged.items, set3.items);
|
|
98531
|
+
appendAll(merged.failures, set3.failures);
|
|
98532
|
+
}
|
|
98533
|
+
const seen = new Set;
|
|
98534
|
+
for (const { id } of [...merged.items, ...merged.failures]) {
|
|
98535
|
+
if (seen.has(id)) {
|
|
98536
|
+
throw new DecideInputError(`Two items share the id ${JSON.stringify(id)}; pass --id-field naming a unique field`);
|
|
98537
|
+
}
|
|
98538
|
+
seen.add(id);
|
|
98539
|
+
}
|
|
98540
|
+
return merged;
|
|
98541
|
+
}
|
|
98542
|
+
function chunk2(items, failures) {
|
|
98543
|
+
const chunks = [];
|
|
98544
|
+
let current = [];
|
|
98545
|
+
let bytes = 0;
|
|
98546
|
+
for (const item of items) {
|
|
98547
|
+
const size2 = Buffer.byteLength(JSON.stringify(item));
|
|
98548
|
+
if (size2 > MAX_REQUEST_BYTES) {
|
|
98549
|
+
failures.push({ id: item.id, error: "Larger than one request can carry (6 MB)" });
|
|
98550
|
+
continue;
|
|
98551
|
+
}
|
|
98552
|
+
if (current.length === MAX_ITEMS_PER_REQUEST || bytes + size2 > MAX_REQUEST_BYTES) {
|
|
98553
|
+
chunks.push(current);
|
|
98554
|
+
current = [];
|
|
98555
|
+
bytes = 0;
|
|
98556
|
+
}
|
|
98557
|
+
current.push(item);
|
|
98558
|
+
bytes += size2;
|
|
98559
|
+
}
|
|
98560
|
+
if (current.length)
|
|
98561
|
+
chunks.push(current);
|
|
98562
|
+
return chunks;
|
|
98563
|
+
}
|
|
98564
|
+
|
|
98565
|
+
class Tally {
|
|
98566
|
+
done = 0;
|
|
98567
|
+
uncertainIds = [];
|
|
98568
|
+
failed = [];
|
|
98569
|
+
answers = {};
|
|
98570
|
+
credits = 0;
|
|
98571
|
+
model = null;
|
|
98572
|
+
addRow(row) {
|
|
98573
|
+
if (row.error !== undefined) {
|
|
98574
|
+
this.failed.push({ id: row.id, reason: row.error });
|
|
98575
|
+
return;
|
|
98576
|
+
}
|
|
98577
|
+
this.done += 1;
|
|
98578
|
+
if (row.uncertain)
|
|
98579
|
+
this.uncertainIds.push(row.id);
|
|
98580
|
+
}
|
|
98581
|
+
addResponse(response) {
|
|
98582
|
+
this.model ??= response.model;
|
|
98583
|
+
this.credits += response.credits;
|
|
98584
|
+
for (const [question, counts] of Object.entries(response.answers)) {
|
|
98585
|
+
const into = this.answers[question] ??= {};
|
|
98586
|
+
for (const [answer, count] of Object.entries(counts)) {
|
|
98587
|
+
into[answer] = (into[answer] ?? 0) + count;
|
|
98588
|
+
}
|
|
98589
|
+
}
|
|
98590
|
+
}
|
|
98591
|
+
}
|
|
98592
|
+
async function runDecide(cwd2, argv, deps = {}) {
|
|
98593
|
+
const stdout = deps.stdout ?? ((text4) => void process.stdout.write(text4));
|
|
98594
|
+
const stderr = deps.stderr ?? ((text4) => void process.stderr.write(text4));
|
|
98595
|
+
const started = Date.now();
|
|
98596
|
+
let parsed;
|
|
98597
|
+
try {
|
|
98598
|
+
parsed = parseArgv(argv, OPTIONS);
|
|
98599
|
+
} catch (error2) {
|
|
98600
|
+
if (error2 instanceof ArgvParseError)
|
|
98601
|
+
return usageError2(stderr, error2.message);
|
|
98602
|
+
throw error2;
|
|
98603
|
+
}
|
|
98604
|
+
if (booleanOption(parsed, "help")) {
|
|
98605
|
+
stdout(DECIDE_USAGE);
|
|
98606
|
+
return 0;
|
|
98607
|
+
}
|
|
98608
|
+
const questionsFile = stringOption(parsed, "questions");
|
|
98609
|
+
if (!questionsFile)
|
|
98610
|
+
return usageError2(stderr, "--questions <file.json> is required");
|
|
98611
|
+
const paths = parsed.positionals;
|
|
98612
|
+
const globs = stringOptions(parsed, "items");
|
|
98613
|
+
const jsonlFiles = stringOptions(parsed, "jsonl");
|
|
98614
|
+
const csvFiles = stringOptions(parsed, "csv");
|
|
98615
|
+
if (!paths.length && !globs.length && !jsonlFiles.length && !csvFiles.length) {
|
|
98616
|
+
return usageError2(stderr, "name at least one source: paths, --items, --jsonl or --csv");
|
|
98617
|
+
}
|
|
98618
|
+
const minConfidenceRaw = stringOption(parsed, "minConfidence");
|
|
98619
|
+
const minConfidence = minConfidenceRaw === undefined ? undefined : Number(minConfidenceRaw);
|
|
98620
|
+
if (minConfidence !== undefined && !(minConfidence >= 0 && minConfidence <= 1)) {
|
|
98621
|
+
return usageError2(stderr, "--min-confidence takes a number from 0 to 1");
|
|
98622
|
+
}
|
|
98623
|
+
const concurrencyRaw = stringOption(parsed, "concurrency");
|
|
98624
|
+
const concurrency = concurrencyRaw === undefined ? DEFAULT_CONCURRENCY : Number(concurrencyRaw);
|
|
98625
|
+
if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency > MAX_CONCURRENCY) {
|
|
98626
|
+
return usageError2(stderr, `--concurrency takes a whole number from 1 to ${MAX_CONCURRENCY}`);
|
|
98627
|
+
}
|
|
98628
|
+
const selectFrom = stringOption(parsed, "selectFrom");
|
|
98629
|
+
const whereRaw = stringOption(parsed, "where");
|
|
98630
|
+
if (Boolean(selectFrom) !== Boolean(whereRaw)) {
|
|
98631
|
+
return usageError2(stderr, "--select-from and --where go together");
|
|
98632
|
+
}
|
|
98633
|
+
const resume = booleanOption(parsed, "resume");
|
|
98634
|
+
const overwrite = booleanOption(parsed, "overwrite");
|
|
98635
|
+
if (resume && overwrite)
|
|
98636
|
+
return usageError2(stderr, "--resume and --overwrite cannot be combined");
|
|
98637
|
+
const outArg = stringOption(parsed, "out") ?? DEFAULT_OUT;
|
|
98638
|
+
const outPath = path107.resolve(cwd2, outArg);
|
|
98639
|
+
let questions;
|
|
98640
|
+
let set3;
|
|
98641
|
+
let skipped = 0;
|
|
98642
|
+
try {
|
|
98643
|
+
questions = readQuestions(cwd2, questionsFile);
|
|
98644
|
+
const rowOptions = {
|
|
98645
|
+
idField: stringOption(parsed, "idField"),
|
|
98646
|
+
textFields: stringOptions(parsed, "textField")
|
|
98647
|
+
};
|
|
98648
|
+
set3 = mergeSets([
|
|
98649
|
+
readFileItems(cwd2, fileItems(cwd2, paths, globs)),
|
|
98650
|
+
...jsonlFiles.map((file) => jsonlItems(cwd2, file, rowOptions)),
|
|
98651
|
+
...csvFiles.map((file) => csvItems(cwd2, file, rowOptions))
|
|
98652
|
+
]);
|
|
98653
|
+
if (selectFrom && whereRaw) {
|
|
98654
|
+
const where = parseWhere(whereRaw);
|
|
98655
|
+
const earlier = path107.resolve(cwd2, selectFrom);
|
|
98656
|
+
if (!fs95.existsSync(earlier))
|
|
98657
|
+
throw new DecideInputError(`No such results file: ${selectFrom}`);
|
|
98658
|
+
const rows = readResults(earlier);
|
|
98659
|
+
const keep = (id) => {
|
|
98660
|
+
const row = rows.get(id);
|
|
98661
|
+
return row !== undefined && rowMatches(row, where);
|
|
98662
|
+
};
|
|
98663
|
+
set3 = { items: set3.items.filter((item) => keep(item.id)), failures: set3.failures.filter((f4) => keep(f4.id)) };
|
|
98664
|
+
}
|
|
98665
|
+
const outHasRows = fs95.existsSync(outPath) && fs95.statSync(outPath).size > 0;
|
|
98666
|
+
if (outHasRows && !resume && !overwrite) {
|
|
98667
|
+
throw new DecideInputError(`${outArg} already has results; pass --resume to continue it or --overwrite to replace it`);
|
|
98668
|
+
}
|
|
98669
|
+
if (resume) {
|
|
98670
|
+
const answered = new Set([...readResults(outPath).values()].filter((row) => row.error === undefined).map((row) => row.id));
|
|
98671
|
+
const before2 = set3.items.length + set3.failures.length;
|
|
98672
|
+
set3 = {
|
|
98673
|
+
items: set3.items.filter((item) => !answered.has(item.id)),
|
|
98674
|
+
failures: set3.failures.filter((failure) => !answered.has(failure.id))
|
|
98675
|
+
};
|
|
98676
|
+
skipped = before2 - set3.items.length - set3.failures.length;
|
|
98677
|
+
}
|
|
98678
|
+
} catch (error2) {
|
|
98679
|
+
if (error2 instanceof DecideInputError)
|
|
98680
|
+
return usageError2(stderr, error2.message);
|
|
98681
|
+
throw error2;
|
|
98682
|
+
}
|
|
98683
|
+
fs95.mkdirSync(path107.dirname(outPath), { recursive: true });
|
|
98684
|
+
if (resume)
|
|
98685
|
+
repairResultsTail(outPath);
|
|
98686
|
+
else
|
|
98687
|
+
fs95.writeFileSync(outPath, "");
|
|
98688
|
+
const append = (rows) => {
|
|
98689
|
+
if (rows.length)
|
|
98690
|
+
fs95.appendFileSync(outPath, rows.map((row) => JSON.stringify(row)).join(`
|
|
98691
|
+
`) + `
|
|
98692
|
+
`);
|
|
98693
|
+
};
|
|
98694
|
+
const tally = new Tally;
|
|
98695
|
+
const localFailures = [...set3.failures];
|
|
98696
|
+
const chunks = chunk2(set3.items, localFailures);
|
|
98697
|
+
const localRows = localFailures.map((failure) => ({ id: failure.id, error: failure.error }));
|
|
98698
|
+
append(localRows);
|
|
98699
|
+
localRows.forEach((row) => tally.addRow(row));
|
|
98700
|
+
const total = set3.items.length + set3.failures.length;
|
|
98701
|
+
let stopError;
|
|
98702
|
+
let unsent = 0;
|
|
98703
|
+
let next = 0;
|
|
98704
|
+
if (chunks.length) {
|
|
98705
|
+
let credential;
|
|
98706
|
+
try {
|
|
98707
|
+
credential = await resolveDecideCredential(cwd2, stringOption(parsed, "agent"));
|
|
98708
|
+
} catch (error2) {
|
|
98709
|
+
stopError = error2.message;
|
|
98710
|
+
}
|
|
98711
|
+
if (credential) {
|
|
98712
|
+
const modelFlag = stringOption(parsed, "model");
|
|
98713
|
+
const body = (items) => ({
|
|
98714
|
+
questions,
|
|
98715
|
+
items,
|
|
98716
|
+
...modelFlag ? { model: modelFlag } : {},
|
|
98717
|
+
...minConfidence !== undefined ? { min_confidence: minConfidence } : {},
|
|
98718
|
+
output: "full"
|
|
98719
|
+
});
|
|
98720
|
+
const progress2 = process.stderr.isTTY && !deps.stderr;
|
|
98721
|
+
let answeredSoFar = localRows.length;
|
|
98722
|
+
let replacement;
|
|
98723
|
+
const send = async (items) => {
|
|
98724
|
+
const used = credential;
|
|
98725
|
+
try {
|
|
98726
|
+
return await postDecide(used, body(items), deps);
|
|
98727
|
+
} catch (error2) {
|
|
98728
|
+
if (!(error2 instanceof DecideHttpError && error2.status === 401 && used.source === "agent_key")) {
|
|
98729
|
+
throw error2;
|
|
98730
|
+
}
|
|
98731
|
+
replacement ??= replaceAgentKey(used).then((fresh2) => credential = fresh2);
|
|
98732
|
+
const fresh = await replacement;
|
|
98733
|
+
if (fresh.key === used.key)
|
|
98734
|
+
throw error2;
|
|
98735
|
+
return postDecide(fresh, body(items), deps);
|
|
98736
|
+
}
|
|
98737
|
+
};
|
|
98738
|
+
const worker = async () => {
|
|
98739
|
+
while (next < chunks.length && stopError === undefined) {
|
|
98740
|
+
const items = chunks[next++];
|
|
98741
|
+
let rows;
|
|
98742
|
+
try {
|
|
98743
|
+
const response = await send(items);
|
|
98744
|
+
tally.addResponse(response);
|
|
98745
|
+
rows = response.rows;
|
|
98746
|
+
} catch (error2) {
|
|
98747
|
+
const message3 = error2.message;
|
|
98748
|
+
const refusal = error2 instanceof DecideHttpError;
|
|
98749
|
+
if (!refusal || error2.status !== null && STOP_STATUSES.has(error2.status)) {
|
|
98750
|
+
stopError ??= refusal && error2.status === 404 ? `/v1/decide is not available on ${credential.base} (${message3})` : message3;
|
|
98751
|
+
unsent += items.length;
|
|
98752
|
+
continue;
|
|
98753
|
+
}
|
|
98754
|
+
rows = items.map((item) => ({ id: item.id, error: `Request failed: ${message3}` }));
|
|
98755
|
+
}
|
|
98756
|
+
append(rows);
|
|
98757
|
+
rows.forEach((row) => tally.addRow(row));
|
|
98758
|
+
answeredSoFar += rows.length;
|
|
98759
|
+
if (progress2)
|
|
98760
|
+
process.stderr.write(`\rdecided ${answeredSoFar}/${total}`);
|
|
98761
|
+
}
|
|
98762
|
+
};
|
|
98763
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, worker));
|
|
98764
|
+
if (progress2)
|
|
98765
|
+
process.stderr.write(`
|
|
98766
|
+
`);
|
|
98767
|
+
}
|
|
98768
|
+
unsent += chunks.slice(next).reduce((count, items) => count + items.length, 0);
|
|
98769
|
+
}
|
|
98770
|
+
const summary = {
|
|
98771
|
+
status: stopError === undefined ? "done" : "stopped",
|
|
98772
|
+
model: tally.model ?? stringOption(parsed, "model") ?? null,
|
|
98773
|
+
counts: {
|
|
98774
|
+
total,
|
|
98775
|
+
done: tally.done,
|
|
98776
|
+
uncertain: tally.uncertainIds.length,
|
|
98777
|
+
failed: tally.failed.length,
|
|
98778
|
+
skipped,
|
|
98779
|
+
unsent
|
|
98780
|
+
},
|
|
98781
|
+
answers: tally.answers,
|
|
98782
|
+
uncertain_ids: tally.uncertainIds.slice(0, SUMMARY_UNCERTAIN_IDS),
|
|
98783
|
+
failed: tally.failed.slice(0, SUMMARY_FAILED),
|
|
98784
|
+
credits: tally.credits,
|
|
98785
|
+
elapsed_ms: Date.now() - started,
|
|
98786
|
+
out: path107.relative(cwd2, outPath) || outArg,
|
|
98787
|
+
...stopError !== undefined ? { error: `${stopError}. Answers so far are in ${outArg}; rerun with --resume to continue.` } : {}
|
|
98788
|
+
};
|
|
98789
|
+
stdout(JSON.stringify(summary, null, 2) + `
|
|
98790
|
+
`);
|
|
98791
|
+
return stopError === undefined ? 0 : 1;
|
|
98792
|
+
}
|
|
98793
|
+
|
|
97547
98794
|
// src/ui/console-streams.ts
|
|
97548
98795
|
import { format as format3 } from "node:util";
|
|
97549
98796
|
function routeConsoleThroughStreams() {
|
|
@@ -97601,6 +98848,7 @@ var COMMANDS3 = [
|
|
|
97601
98848
|
"status",
|
|
97602
98849
|
"mcp",
|
|
97603
98850
|
"integrations",
|
|
98851
|
+
"decide",
|
|
97604
98852
|
"run",
|
|
97605
98853
|
"help",
|
|
97606
98854
|
"version"
|
|
@@ -97728,6 +98976,10 @@ function help() {
|
|
|
97728
98976
|
out.push(` ${import_picocolors81.default.cyan("token rename")} ${import_picocolors81.default.dim("<id>")} ${import_picocolors81.default.dim("relabel a token")}`);
|
|
97729
98977
|
out.push(` ${import_picocolors81.default.cyan("token revoke")} ${import_picocolors81.default.dim("<id>")} ${import_picocolors81.default.dim("revoke a token")}`);
|
|
97730
98978
|
out.push("");
|
|
98979
|
+
out.push(divider("DECISIONS"));
|
|
98980
|
+
out.push("");
|
|
98981
|
+
out.push(` ${import_picocolors81.default.cyan("decide")} ${import_picocolors81.default.dim("-q <questions.json> <sources...>")} ${import_picocolors81.default.dim("answer decision-model questions over many files or rows in a few requests (`decide --help`)")}`);
|
|
98982
|
+
out.push("");
|
|
97731
98983
|
out.push(divider("MCP"));
|
|
97732
98984
|
out.push("");
|
|
97733
98985
|
out.push(` ${import_picocolors81.default.cyan("mcp check")} ${import_picocolors81.default.dim("[--json]")} ${import_picocolors81.default.dim("verify MCP server connectivity through the brainbase proxy (runs at sandbox bootstrap)")}`);
|
|
@@ -97988,7 +99240,7 @@ async function main() {
|
|
|
97988
99240
|
const rawCwd = process14.cwd();
|
|
97989
99241
|
const cwd2 = (() => {
|
|
97990
99242
|
try {
|
|
97991
|
-
return
|
|
99243
|
+
return fs96.realpathSync(rawCwd);
|
|
97992
99244
|
} catch {
|
|
97993
99245
|
return rawCwd;
|
|
97994
99246
|
}
|
|
@@ -98005,7 +99257,7 @@ async function main() {
|
|
|
98005
99257
|
await runRun(cwd2, argv);
|
|
98006
99258
|
return;
|
|
98007
99259
|
}
|
|
98008
|
-
const ownsArgs = new Set(["task", "benchmark", "interface", "interfaces"]);
|
|
99260
|
+
const ownsArgs = new Set(["task", "benchmark", "interface", "interfaces", "decide"]);
|
|
98009
99261
|
if (cmd === "agent")
|
|
98010
99262
|
argv.splice(0, argv.length, ...routeAgentSurfaceArgs(argv));
|
|
98011
99263
|
const sharedArgs = ownsArgs.has(cmd) || cmd === "agent" && ["versions", "surface", "connect", "disconnect", "connections"].includes(argv[0] ?? "") ? [] : argv;
|
|
@@ -98242,6 +99494,10 @@ async function main() {
|
|
|
98242
99494
|
process14.exitCode = code;
|
|
98243
99495
|
break;
|
|
98244
99496
|
}
|
|
99497
|
+
case "decide": {
|
|
99498
|
+
process14.exitCode = await runDecide(cwd2, argv);
|
|
99499
|
+
break;
|
|
99500
|
+
}
|
|
98245
99501
|
case "integration":
|
|
98246
99502
|
case "integrations": {
|
|
98247
99503
|
const sub = argv.shift();
|