perchai-cli 2.4.24 → 2.4.26
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/perch.mjs +305 -30
- package/package.json +1 -1
package/dist/perch.mjs
CHANGED
|
@@ -76921,6 +76921,7 @@ function buildTranscriptSegments(state) {
|
|
|
76921
76921
|
const terminalSegmentIdx = /* @__PURE__ */ new Map();
|
|
76922
76922
|
const workerRunIdx = /* @__PURE__ */ new Map();
|
|
76923
76923
|
const flockRunIdx = /* @__PURE__ */ new Map();
|
|
76924
|
+
const diagnosticIdxByKey = /* @__PURE__ */ new Map();
|
|
76924
76925
|
const capabilityIdx = /* @__PURE__ */ new Map();
|
|
76925
76926
|
const liveCardIdx = /* @__PURE__ */ new Map();
|
|
76926
76927
|
const workflowCardIdx = /* @__PURE__ */ new Map();
|
|
@@ -77542,6 +77543,38 @@ function buildTranscriptSegments(state) {
|
|
|
77542
77543
|
case "diagnostic": {
|
|
77543
77544
|
flushReasoning();
|
|
77544
77545
|
if (!ev.message.trim()) break;
|
|
77546
|
+
if (ev.code === "tool_call_budget_exhausted") {
|
|
77547
|
+
let attributed = false;
|
|
77548
|
+
if (ev.workerId) {
|
|
77549
|
+
for (const [flockId, idx] of flockRunIdx) {
|
|
77550
|
+
const seg = segments[idx];
|
|
77551
|
+
if (seg?.kind !== "flock_run") continue;
|
|
77552
|
+
if (!seg.workers.some((worker) => worker.workerId === ev.workerId)) continue;
|
|
77553
|
+
segments[idx] = {
|
|
77554
|
+
...seg,
|
|
77555
|
+
workers: seg.workers.map(
|
|
77556
|
+
(worker) => worker.workerId === ev.workerId ? { ...worker, note: "tool limit \u2014 finishing from gathered evidence" } : worker
|
|
77557
|
+
)
|
|
77558
|
+
};
|
|
77559
|
+
attributed = Boolean(flockId);
|
|
77560
|
+
break;
|
|
77561
|
+
}
|
|
77562
|
+
}
|
|
77563
|
+
if (!attributed) {
|
|
77564
|
+
const key = `tool-budget-${ev.workerId ?? "turn"}`;
|
|
77565
|
+
const existingIdx = diagnosticIdxByKey.get(key);
|
|
77566
|
+
if (existingIdx !== void 0 && segments[existingIdx]?.kind === "diagnostic") {
|
|
77567
|
+
segments[existingIdx] = {
|
|
77568
|
+
...segments[existingIdx],
|
|
77569
|
+
message: ev.message
|
|
77570
|
+
};
|
|
77571
|
+
} else {
|
|
77572
|
+
diagnosticIdxByKey.set(key, segments.length);
|
|
77573
|
+
segments.push({ kind: "diagnostic", message: ev.message, seq: seq++, timestamp: ev.ts });
|
|
77574
|
+
}
|
|
77575
|
+
}
|
|
77576
|
+
break;
|
|
77577
|
+
}
|
|
77545
77578
|
segments.push({
|
|
77546
77579
|
kind: "diagnostic",
|
|
77547
77580
|
message: ev.message,
|
|
@@ -77849,6 +77882,7 @@ function buildTranscriptSegments(state) {
|
|
|
77849
77882
|
plannerSource: ev.plannerSource,
|
|
77850
77883
|
workers: ev.workers.map((worker) => ({
|
|
77851
77884
|
flockWorkerId: worker.flockWorkerId,
|
|
77885
|
+
workerId: worker.workerId,
|
|
77852
77886
|
displayName: worker.displayName,
|
|
77853
77887
|
nickname: worker.nickname,
|
|
77854
77888
|
status: "queued"
|
|
@@ -77868,6 +77902,7 @@ function buildTranscriptSegments(state) {
|
|
|
77868
77902
|
...seg.workers,
|
|
77869
77903
|
{
|
|
77870
77904
|
flockWorkerId: ev.flockWorkerId,
|
|
77905
|
+
workerId: ev.workerId,
|
|
77871
77906
|
displayName: ev.displayName,
|
|
77872
77907
|
nickname: ev.nickname,
|
|
77873
77908
|
status: ev.status
|
|
@@ -83022,7 +83057,6 @@ function listFinancialRoleIds() {
|
|
|
83022
83057
|
var FINANCIAL_ROLE_REGISTRY, evidenceScoutManifest;
|
|
83023
83058
|
var init_financialRoles = __esm({
|
|
83024
83059
|
"features/perchTerminal/agentPlatform/financialRoles.ts"() {
|
|
83025
|
-
"use strict";
|
|
83026
83060
|
FINANCIAL_ROLE_REGISTRY = /* @__PURE__ */ new Map();
|
|
83027
83061
|
evidenceScoutManifest = {
|
|
83028
83062
|
workerId: "evidence_scout",
|
|
@@ -86588,7 +86622,6 @@ function truncateHistoryLine(value, max2) {
|
|
|
86588
86622
|
}
|
|
86589
86623
|
var init_operatorTruth = __esm({
|
|
86590
86624
|
"features/perchTerminal/runtime/operatorTruth.ts"() {
|
|
86591
|
-
"use strict";
|
|
86592
86625
|
}
|
|
86593
86626
|
});
|
|
86594
86627
|
|
|
@@ -91554,6 +91587,7 @@ function listFinancialPlaybooks() {
|
|
|
91554
91587
|
var AP_AUDIT_PACKET_DEF, PLAYBOOKS;
|
|
91555
91588
|
var init_registry2 = __esm({
|
|
91556
91589
|
"features/perchTerminal/runtime/financialPlaybooks/registry.ts"() {
|
|
91590
|
+
"use strict";
|
|
91557
91591
|
init_managedWorkflowRegistry2();
|
|
91558
91592
|
init_toolNames();
|
|
91559
91593
|
AP_AUDIT_PACKET_DEF = {
|
|
@@ -217708,10 +217742,18 @@ async function runModelToolLoop(input) {
|
|
|
217708
217742
|
batch.slice(remainingRealToolCalls).map((toolCall) => toolCall.id)
|
|
217709
217743
|
);
|
|
217710
217744
|
if (skippedForToolBudget.size > 0) {
|
|
217745
|
+
const skippedToolNames = [
|
|
217746
|
+
...new Set(
|
|
217747
|
+
batch.filter((toolCall) => skippedForToolBudget.has(toolCall.id)).map((toolCall) => toolCall.name)
|
|
217748
|
+
)
|
|
217749
|
+
];
|
|
217711
217750
|
onEvent({
|
|
217712
217751
|
type: "diagnostic",
|
|
217713
217752
|
code: "tool_call_budget_exhausted",
|
|
217714
|
-
message: `Tool
|
|
217753
|
+
message: `Tool budget reached after ${realToolCallsStarted} call(s) \u2014 finishing from gathered evidence.`,
|
|
217754
|
+
toolNames: skippedToolNames,
|
|
217755
|
+
skippedCount: skippedForToolBudget.size,
|
|
217756
|
+
maxToolCalls: maxToolCalls ?? 0,
|
|
217715
217757
|
ts: now5()
|
|
217716
217758
|
});
|
|
217717
217759
|
}
|
|
@@ -217725,6 +217767,7 @@ async function runModelToolLoop(input) {
|
|
|
217725
217767
|
firstPartyToolIntentUsed = true;
|
|
217726
217768
|
}
|
|
217727
217769
|
const batchTerminalIds = batch.map((toolCall) => {
|
|
217770
|
+
if (skippedForToolBudget.has(toolCall.id)) return null;
|
|
217728
217771
|
onEvent({
|
|
217729
217772
|
type: "tool_call_requested",
|
|
217730
217773
|
toolName: toolCall.name,
|
|
@@ -217896,17 +217939,20 @@ async function runModelToolLoop(input) {
|
|
|
217896
217939
|
consecutiveRequiredArgFailures,
|
|
217897
217940
|
execution
|
|
217898
217941
|
);
|
|
217899
|
-
|
|
217900
|
-
|
|
217901
|
-
|
|
217902
|
-
|
|
217903
|
-
|
|
217904
|
-
|
|
217905
|
-
|
|
217906
|
-
|
|
217907
|
-
|
|
217908
|
-
|
|
217909
|
-
|
|
217942
|
+
const isToolBudgetSuppressed = execution.errorCode === "tool_call_budget_exhausted";
|
|
217943
|
+
if (!isToolBudgetSuppressed) {
|
|
217944
|
+
onEvent({
|
|
217945
|
+
type: execution.ok ? "tool_call_completed" : "tool_call_failed",
|
|
217946
|
+
toolName: toolCall.name,
|
|
217947
|
+
toolCallId: toolCall.id,
|
|
217948
|
+
riskLevel: execution.riskLevel,
|
|
217949
|
+
ok: execution.ok,
|
|
217950
|
+
errorCode: execution.errorCode,
|
|
217951
|
+
error: execution.error,
|
|
217952
|
+
metadata: browserPerceptionMetadata(execution),
|
|
217953
|
+
ts: now5()
|
|
217954
|
+
});
|
|
217955
|
+
}
|
|
217910
217956
|
if (execution.ok && (toolCall.name === "writeLocalFile" || toolCall.name === "editLocalFile")) {
|
|
217911
217957
|
const out = typeof execution.output === "object" && execution.output !== null ? execution.output : null;
|
|
217912
217958
|
const filePath = typeof out?.relativePath === "string" ? out.relativePath : null;
|
|
@@ -217965,7 +218011,7 @@ async function runModelToolLoop(input) {
|
|
|
217965
218011
|
});
|
|
217966
218012
|
}
|
|
217967
218013
|
const toolResultText = execution.outputSummary?.trim() ? execution.outputSummary : execution.result.slice(0, 1500);
|
|
217968
|
-
if (toolResultText.trim() && !isTerminalTool) {
|
|
218014
|
+
if (toolResultText.trim() && !isTerminalTool && !isToolBudgetSuppressed) {
|
|
217969
218015
|
onEvent({
|
|
217970
218016
|
type: "tool_output_delta",
|
|
217971
218017
|
toolName: toolCall.name,
|
|
@@ -217982,7 +218028,7 @@ async function runModelToolLoop(input) {
|
|
|
217982
218028
|
});
|
|
217983
218029
|
}
|
|
217984
218030
|
const persistOption = autoRouterActive && lastAutoHopModelId ? getFounderModelOption(lastAutoHopModelId) ?? option : option;
|
|
217985
|
-
if (persistToolCall && !isSyntheticReadSuppressionExecution(execution)) {
|
|
218031
|
+
if (persistToolCall && !isSyntheticReadSuppressionExecution(execution) && !isToolBudgetSuppressed) {
|
|
217986
218032
|
await persistToolCall({
|
|
217987
218033
|
toolName: execution.toolName,
|
|
217988
218034
|
toolCallId: execution.toolCallId,
|
|
@@ -219469,7 +219515,7 @@ ${contextBlock}
|
|
|
219469
219515
|
return;
|
|
219470
219516
|
}
|
|
219471
219517
|
if (event.type === "diagnostic") {
|
|
219472
|
-
ctx.onEvent(event);
|
|
219518
|
+
ctx.onEvent({ ...event, workerId: args.workerId });
|
|
219473
219519
|
return;
|
|
219474
219520
|
}
|
|
219475
219521
|
if (event.type === "model_call_failed") {
|
|
@@ -220309,7 +220355,7 @@ function flockCaps() {
|
|
|
220309
220355
|
function resolveFlockWriteWorkerCap(input) {
|
|
220310
220356
|
return input.permissionMode === "take_the_wheel" && input.planJustifiesMoreWriters ? FLOCK_MAX_WRITE_WORKERS_TAKE_THE_WHEEL : FLOCK_MAX_WRITE_WORKERS;
|
|
220311
220357
|
}
|
|
220312
|
-
var FLOCK_MIN_WORKERS, FLOCK_MAX_WORKERS, FLOCK_PREFERRED_WORKERS, FLOCK_DEFAULT_WALL_MS, FLOCK_MAX_WALL_MS, FLOCK_MAX_TOTAL_TOOL_CALLS, FLOCK_MAX_WORKER_ITERATIONS, FLOCK_MAX_WRITE_WORKERS, FLOCK_MAX_WRITE_WORKERS_TAKE_THE_WHEEL, FLOCK_MIN_TASK_CHARS, FLOCK_MIN_TASK_WORDS, FLOCK_MAX_TASK_CHARS;
|
|
220358
|
+
var FLOCK_MIN_WORKERS, FLOCK_MAX_WORKERS, FLOCK_PREFERRED_WORKERS, FLOCK_DEFAULT_WALL_MS, FLOCK_MAX_WALL_MS, FLOCK_MAX_TOTAL_TOOL_CALLS, FLOCK_MIN_WORKER_TOOL_CALLS, FLOCK_MAX_WORKER_TOOL_CALLS, FLOCK_MAX_WORKER_ITERATIONS, FLOCK_MAX_WRITE_WORKERS, FLOCK_MAX_WRITE_WORKERS_TAKE_THE_WHEEL, FLOCK_MIN_TASK_CHARS, FLOCK_MIN_TASK_WORDS, FLOCK_MAX_TASK_CHARS;
|
|
220313
220359
|
var init_flockLimits = __esm({
|
|
220314
220360
|
"features/perchTerminal/runtime/flock/flockLimits.ts"() {
|
|
220315
220361
|
"use strict";
|
|
@@ -220319,7 +220365,9 @@ var init_flockLimits = __esm({
|
|
|
220319
220365
|
FLOCK_PREFERRED_WORKERS = { min: 3, max: 6 };
|
|
220320
220366
|
FLOCK_DEFAULT_WALL_MS = 8 * 6e4;
|
|
220321
220367
|
FLOCK_MAX_WALL_MS = 10 * 6e4;
|
|
220322
|
-
FLOCK_MAX_TOTAL_TOOL_CALLS =
|
|
220368
|
+
FLOCK_MAX_TOTAL_TOOL_CALLS = 160;
|
|
220369
|
+
FLOCK_MIN_WORKER_TOOL_CALLS = 12;
|
|
220370
|
+
FLOCK_MAX_WORKER_TOOL_CALLS = 40;
|
|
220323
220371
|
FLOCK_MAX_WORKER_ITERATIONS = Math.min(10, MAX_WORKER_ITERATIONS);
|
|
220324
220372
|
FLOCK_MAX_WRITE_WORKERS = 2;
|
|
220325
220373
|
FLOCK_MAX_WRITE_WORKERS_TAKE_THE_WHEEL = 4;
|
|
@@ -220329,6 +220377,118 @@ var init_flockLimits = __esm({
|
|
|
220329
220377
|
}
|
|
220330
220378
|
});
|
|
220331
220379
|
|
|
220380
|
+
// features/perchTerminal/runtime/flock/flockModelHints.ts
|
|
220381
|
+
function parseFlockModelHints(task) {
|
|
220382
|
+
const hints = [];
|
|
220383
|
+
const seen = /* @__PURE__ */ new Set();
|
|
220384
|
+
const push2 = (modelText, roleText, explicit) => {
|
|
220385
|
+
const model = modelText.trim().replace(/\s+/g, " ");
|
|
220386
|
+
const role = roleText.trim().toLowerCase();
|
|
220387
|
+
if (!model || !(role in ROLE_WORD_MAP)) return;
|
|
220388
|
+
const key = `${model.toLowerCase()}\u2192${role}`;
|
|
220389
|
+
if (seen.has(key)) return;
|
|
220390
|
+
seen.add(key);
|
|
220391
|
+
hints.push({ modelText: model, roleText: role, explicit });
|
|
220392
|
+
};
|
|
220393
|
+
const usePattern = new RegExp(
|
|
220394
|
+
`\\buse\\s+([A-Za-z0-9][A-Za-z0-9 ._/-]{0,30}?)\\s+(?:as|for)\\s+(?:an?\\s+|the\\s+)?(${ROLE_WORDS})\\b`,
|
|
220395
|
+
"gi"
|
|
220396
|
+
);
|
|
220397
|
+
for (const match of task.matchAll(usePattern)) {
|
|
220398
|
+
push2(match[1], match[2], true);
|
|
220399
|
+
}
|
|
220400
|
+
const pairPattern = new RegExp(
|
|
220401
|
+
`\\b([A-Z][A-Za-z0-9._-]{1,24})\\s+(${ROLE_WORDS})\\b`,
|
|
220402
|
+
"g"
|
|
220403
|
+
);
|
|
220404
|
+
for (const match of task.matchAll(pairPattern)) {
|
|
220405
|
+
if (/^(use|using|the|a|an|as|and|with|for)$/i.test(match[1])) continue;
|
|
220406
|
+
push2(match[1], match[2], false);
|
|
220407
|
+
}
|
|
220408
|
+
return hints;
|
|
220409
|
+
}
|
|
220410
|
+
function resolveFlockModelOption(modelText) {
|
|
220411
|
+
const needle = modelText.trim().toLowerCase();
|
|
220412
|
+
if (needle.length < 2) return null;
|
|
220413
|
+
const usable = FOUNDER_MODEL_OPTIONS.filter(
|
|
220414
|
+
(option) => isOptionAllowedForLane(option, "fast_worker") || isOptionAllowedForLane(option, "code_worker") || isOptionAllowedForLane(option, "verifier")
|
|
220415
|
+
);
|
|
220416
|
+
const haystacks = (option) => [option.label, option.vendor ?? "", option.family ?? "", option.modelId, option.id].join(" ").toLowerCase();
|
|
220417
|
+
const ranked = [...usable].sort(
|
|
220418
|
+
(a, b2) => Number(b2.userFacing === true) - Number(a.userFacing === true)
|
|
220419
|
+
);
|
|
220420
|
+
return ranked.find((option) => haystacks(option).includes(needle)) ?? null;
|
|
220421
|
+
}
|
|
220422
|
+
function applyFlockModelOverrides(plan, task) {
|
|
220423
|
+
const report = { applied: [], unavailable: [] };
|
|
220424
|
+
const hints = parseFlockModelHints(task);
|
|
220425
|
+
for (const hint of hints) {
|
|
220426
|
+
const option = resolveFlockModelOption(hint.modelText);
|
|
220427
|
+
if (!option) {
|
|
220428
|
+
if (hint.explicit) report.unavailable.push(hint);
|
|
220429
|
+
continue;
|
|
220430
|
+
}
|
|
220431
|
+
const roles = new Set(ROLE_WORD_MAP[hint.roleText] ?? []);
|
|
220432
|
+
const targets = plan.workers.filter(
|
|
220433
|
+
(worker) => roles.has(worker.role) || worker.displayName.toLowerCase().includes(hint.roleText)
|
|
220434
|
+
);
|
|
220435
|
+
if (targets.length === 0) continue;
|
|
220436
|
+
for (const worker of targets) {
|
|
220437
|
+
if (worker.modelOverride) continue;
|
|
220438
|
+
worker.modelOverride = { optionId: option.id, label: option.label };
|
|
220439
|
+
report.applied.push({
|
|
220440
|
+
flockWorkerId: worker.flockWorkerId,
|
|
220441
|
+
displayName: worker.displayName,
|
|
220442
|
+
modelText: hint.modelText,
|
|
220443
|
+
label: option.label
|
|
220444
|
+
});
|
|
220445
|
+
}
|
|
220446
|
+
}
|
|
220447
|
+
return report;
|
|
220448
|
+
}
|
|
220449
|
+
function founderSelectionWithModelOverride(base, optionId) {
|
|
220450
|
+
const selection = base ?? DEFAULT_FOUNDER_MODEL_SELECTION;
|
|
220451
|
+
return {
|
|
220452
|
+
...selection,
|
|
220453
|
+
chatModelId: optionId,
|
|
220454
|
+
supervisorModelId: optionId,
|
|
220455
|
+
fastWorkerModelId: optionId,
|
|
220456
|
+
codeWorkerModelId: optionId,
|
|
220457
|
+
dataWorkerModelId: optionId,
|
|
220458
|
+
writerModelId: optionId,
|
|
220459
|
+
verifierModelId: optionId
|
|
220460
|
+
};
|
|
220461
|
+
}
|
|
220462
|
+
var ROLE_WORD_MAP, ROLE_WORDS;
|
|
220463
|
+
var init_flockModelHints = __esm({
|
|
220464
|
+
"features/perchTerminal/runtime/flock/flockModelHints.ts"() {
|
|
220465
|
+
"use strict";
|
|
220466
|
+
init_modelRegistry();
|
|
220467
|
+
ROLE_WORD_MAP = {
|
|
220468
|
+
scout: ["scout"],
|
|
220469
|
+
scouts: ["scout"],
|
|
220470
|
+
explorer: ["scout"],
|
|
220471
|
+
worker: ["worker"],
|
|
220472
|
+
workers: ["worker"],
|
|
220473
|
+
coder: ["worker"],
|
|
220474
|
+
patcher: ["worker"],
|
|
220475
|
+
builder: ["worker"],
|
|
220476
|
+
reviewer: ["verifier", "reducer"],
|
|
220477
|
+
reviewers: ["verifier", "reducer"],
|
|
220478
|
+
verifier: ["verifier"],
|
|
220479
|
+
verifiers: ["verifier"],
|
|
220480
|
+
tester: ["verifier"],
|
|
220481
|
+
checker: ["verifier"],
|
|
220482
|
+
reducer: ["reducer"],
|
|
220483
|
+
synthesizer: ["reducer"],
|
|
220484
|
+
synthesiser: ["reducer"],
|
|
220485
|
+
synth: ["reducer"],
|
|
220486
|
+
summarizer: ["reducer"]
|
|
220487
|
+
};
|
|
220488
|
+
ROLE_WORDS = Object.keys(ROLE_WORD_MAP).join("|");
|
|
220489
|
+
}
|
|
220490
|
+
});
|
|
220491
|
+
|
|
220332
220492
|
// features/perchTerminal/runtime/flock/flockNicknames.ts
|
|
220333
220493
|
function seedFromFlockId(flockId) {
|
|
220334
220494
|
let hash = 2166136261;
|
|
@@ -220622,6 +220782,7 @@ function buildPlanWorker(spec, index, flockId, task, writeScope, caps) {
|
|
|
220622
220782
|
writeScope: spec.writesWorkspace ? writeScope : null,
|
|
220623
220783
|
outputContract: spec.outputContract,
|
|
220624
220784
|
dependsOn: [],
|
|
220785
|
+
modelOverride: null,
|
|
220625
220786
|
dynamicManifest: null
|
|
220626
220787
|
};
|
|
220627
220788
|
}
|
|
@@ -220685,7 +220846,7 @@ function buildFlockPlannerPrompts(ctx) {
|
|
|
220685
220846
|
"- allowedTools: choose ONLY from availableTools in the input. Never request orchestration or delegation tools.",
|
|
220686
220847
|
`- At most ${writeCap} workers with writesWorkspace=true; each writer needs a disjoint, clearly scoped responsibility, and writeJustification is required for more than ${resolveFlockWriteWorkerCap({ permissionMode: null, planJustifiesMoreWriters: false })}.`,
|
|
220687
220848
|
`- maxIterations: 1-${caps.maxIterationsPerWorker}.`,
|
|
220688
|
-
"- dependsOn: ids of earlier workers whose output this worker needs. No cycles.",
|
|
220849
|
+
"- dependsOn: ids of earlier workers whose output this worker needs. No cycles. You decide the graph: parallel where independent, chained where not. Final verifier/reducer workers usually benefit from depending on the workers they check \u2014 guidance, not a requirement.",
|
|
220689
220850
|
"- baseWorkerId: set ONLY to reuse an id from existingWorkers; otherwise null.",
|
|
220690
220851
|
"- Decline (accepted=false, with reason) tasks that are trivial, conversational, or need no fanout."
|
|
220691
220852
|
].join("\n");
|
|
@@ -220814,6 +220975,8 @@ function validateLlmFlockPlan(record, ctx) {
|
|
|
220814
220975
|
outputContract,
|
|
220815
220976
|
dependsOn: [],
|
|
220816
220977
|
// resolved below once all ids are known
|
|
220978
|
+
modelOverride: null,
|
|
220979
|
+
// applied later from explicit task hints only
|
|
220817
220980
|
dynamicManifest: reusedManifest ? null : buildDynamicManifest({
|
|
220818
220981
|
workerId: dynamicWorkerId,
|
|
220819
220982
|
displayName,
|
|
@@ -221024,6 +221187,13 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
221024
221187
|
flockId
|
|
221025
221188
|
});
|
|
221026
221189
|
}
|
|
221190
|
+
if (plan.accepted && typeof options.totalToolCallBudget === "number") {
|
|
221191
|
+
plan.caps.maxTotalToolCalls = Math.max(
|
|
221192
|
+
1,
|
|
221193
|
+
Math.min(Math.floor(options.totalToolCallBudget), plan.caps.maxTotalToolCalls)
|
|
221194
|
+
);
|
|
221195
|
+
}
|
|
221196
|
+
const modelOverrides = plan.accepted ? applyFlockModelOverrides(plan, task) : { applied: [], unavailable: [] };
|
|
221027
221197
|
emitPlanEvent(emit, plan);
|
|
221028
221198
|
if (!plan.accepted) {
|
|
221029
221199
|
emit({
|
|
@@ -221070,9 +221240,12 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
221070
221240
|
const sharedContext = { task };
|
|
221071
221241
|
let toolCallsUsed = 0;
|
|
221072
221242
|
let toolCallsReserved = 0;
|
|
221243
|
+
let workersAwaitingLaunch = plan.workers.length;
|
|
221073
221244
|
try {
|
|
221074
221245
|
const phases = [...new Set(plan.workers.map((worker) => FLOCK_ROLE_ORDER[worker.role]))].sort((a, b2) => a - b2);
|
|
221075
221246
|
const runReadyWorker = async (worker) => {
|
|
221247
|
+
const workersLeftIncludingThis = Math.max(1, workersAwaitingLaunch);
|
|
221248
|
+
workersAwaitingLaunch = Math.max(0, workersAwaitingLaunch - 1);
|
|
221076
221249
|
if (flockRun.controller.signal.aborted) {
|
|
221077
221250
|
const outcome = {
|
|
221078
221251
|
worker,
|
|
@@ -221094,9 +221267,13 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
221094
221267
|
emitWorkerUpdate(emit, plan, worker, "skipped", outcome.detail);
|
|
221095
221268
|
return outcome;
|
|
221096
221269
|
}
|
|
221097
|
-
const
|
|
221098
|
-
|
|
221099
|
-
|
|
221270
|
+
const fairShare = Math.floor(remainingToolCalls / workersLeftIncludingThis);
|
|
221271
|
+
const reservedToolCalls = Math.min(
|
|
221272
|
+
remainingToolCalls,
|
|
221273
|
+
Math.max(
|
|
221274
|
+
FLOCK_MIN_WORKER_TOOL_CALLS,
|
|
221275
|
+
Math.min(FLOCK_MAX_WORKER_TOOL_CALLS, fairShare)
|
|
221276
|
+
)
|
|
221100
221277
|
);
|
|
221101
221278
|
toolCallsReserved += reservedToolCalls;
|
|
221102
221279
|
emitWorkerUpdate(emit, plan, worker, "running");
|
|
@@ -221109,7 +221286,7 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
221109
221286
|
maxIterations: worker.maxIterations,
|
|
221110
221287
|
maxToolCalls: reservedToolCalls
|
|
221111
221288
|
},
|
|
221112
|
-
buildSpawnContext(input, plan.flockId, flockRun.controller.signal, emit)
|
|
221289
|
+
buildSpawnContext(input, plan.flockId, flockRun.controller.signal, emit, worker)
|
|
221113
221290
|
);
|
|
221114
221291
|
toolCallsReserved -= reservedToolCalls;
|
|
221115
221292
|
toolCallsUsed += Math.min(result2.toolCalls ?? 0, reservedToolCalls);
|
|
@@ -221183,7 +221360,15 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
221183
221360
|
const workersDone = outcomes.filter((outcome) => outcome.status === "done").length;
|
|
221184
221361
|
const workersFailed = outcomes.filter((outcome) => outcome.status === "failed").length;
|
|
221185
221362
|
const flockStatus = userCancelled || wallTimeHit && workersDone === 0 ? "cancelled" : workersFailed === 0 && workersDone === plan.workers.length ? "completed" : workersDone > 0 ? "partial" : "failed";
|
|
221186
|
-
const assistantText =
|
|
221363
|
+
const assistantText = [
|
|
221364
|
+
buildFlockSummary(plan, outcomes, flockStatus, toolCallsUsed, wallTimeHit),
|
|
221365
|
+
...modelOverrides.applied.map(
|
|
221366
|
+
(override) => `Model override: ${override.displayName} ran on ${override.label}.`
|
|
221367
|
+
),
|
|
221368
|
+
...modelOverrides.unavailable.map(
|
|
221369
|
+
(override) => `Requested model "${override.modelText}"${override.roleText ? ` for ${override.roleText}` : ""} is not available \u2014 that worker stayed on the default model path.`
|
|
221370
|
+
)
|
|
221371
|
+
].join("\n");
|
|
221187
221372
|
emit({
|
|
221188
221373
|
type: "flock_run_completed",
|
|
221189
221374
|
flockId: plan.flockId,
|
|
@@ -221234,7 +221419,8 @@ function emitPlanEvent(emit, plan) {
|
|
|
221234
221419
|
maxIterations: worker.maxIterations,
|
|
221235
221420
|
allowedTools: worker.allowedTools,
|
|
221236
221421
|
writeScope: worker.writeScope,
|
|
221237
|
-
outputContract: worker.outputContract
|
|
221422
|
+
outputContract: worker.outputContract,
|
|
221423
|
+
modelOverride: worker.modelOverride?.label ?? null
|
|
221238
221424
|
})) : [],
|
|
221239
221425
|
caps: plan.accepted ? plan.caps : { maxWorkers: 0, maxWallMs: 0, maxTotalToolCalls: 0, maxIterationsPerWorker: 0 },
|
|
221240
221426
|
ts: now6()
|
|
@@ -221264,7 +221450,7 @@ function buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan)
|
|
|
221264
221450
|
}
|
|
221265
221451
|
return { task: sharedContext.task, dependencies };
|
|
221266
221452
|
}
|
|
221267
|
-
function buildSpawnContext(input, flockId, signal, emit) {
|
|
221453
|
+
function buildSpawnContext(input, flockId, signal, emit, worker) {
|
|
221268
221454
|
return {
|
|
221269
221455
|
workspaceRoot: input.activeRootPath ?? "",
|
|
221270
221456
|
desktopConnected: input.desktopConnected,
|
|
@@ -221277,7 +221463,12 @@ function buildSpawnContext(input, flockId, signal, emit) {
|
|
|
221277
221463
|
supabaseConfigured: input.supabaseConfigured,
|
|
221278
221464
|
supabase: input.supabase ?? null,
|
|
221279
221465
|
runId: flockId,
|
|
221280
|
-
|
|
221466
|
+
// Default: the currently selected Perch model path. Only an explicit
|
|
221467
|
+
// "use <model> as <role>" request in the task pins this worker elsewhere.
|
|
221468
|
+
founderModelSelection: worker.modelOverride ? founderSelectionWithModelOverride(
|
|
221469
|
+
input.founderModelSelection ?? null,
|
|
221470
|
+
worker.modelOverride.optionId
|
|
221471
|
+
) : input.founderModelSelection ?? null,
|
|
221281
221472
|
onEvent: emit,
|
|
221282
221473
|
signal,
|
|
221283
221474
|
mcpTools: input.mcpTools ?? []
|
|
@@ -221312,6 +221503,7 @@ var init_runFlockTurn = __esm({
|
|
|
221312
221503
|
init_registry();
|
|
221313
221504
|
init_flockCommand();
|
|
221314
221505
|
init_flockLimits();
|
|
221506
|
+
init_flockModelHints();
|
|
221315
221507
|
init_flockLlmPlanner();
|
|
221316
221508
|
init_flockPlanner();
|
|
221317
221509
|
init_flockRoles();
|
|
@@ -285013,10 +285205,12 @@ var init_build2 = __esm({
|
|
|
285013
285205
|
// scripts/perch-cli.ts
|
|
285014
285206
|
var perch_cli_exports = {};
|
|
285015
285207
|
__export(perch_cli_exports, {
|
|
285208
|
+
FLOCK_NICKNAME_ACCENTS: () => FLOCK_NICKNAME_ACCENTS,
|
|
285016
285209
|
HELP_TEXT: () => HELP_TEXT,
|
|
285017
285210
|
INTERACTIVE_HELP_TEXT: () => INTERACTIVE_HELP_TEXT,
|
|
285018
285211
|
PERCH_SPLASH_COMMANDS: () => PERCH_SPLASH_COMMANDS,
|
|
285019
285212
|
flockEventToCliRow: () => flockEventToCliRow,
|
|
285213
|
+
flockNicknameAccentColor: () => flockNicknameAccentColor,
|
|
285020
285214
|
parseInteractiveSlashCommand: () => parseInteractiveSlashCommand,
|
|
285021
285215
|
parsePerchCli: () => parsePerchCli,
|
|
285022
285216
|
runPerchCli: () => runPerchCli
|
|
@@ -285648,6 +285842,8 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
285648
285842
|
setLiveText("");
|
|
285649
285843
|
liveTextRef.current = "";
|
|
285650
285844
|
const toolNamesById = /* @__PURE__ */ new Map();
|
|
285845
|
+
const flockWorkerNames = /* @__PURE__ */ new Map();
|
|
285846
|
+
const flockLimitMeta = /* @__PURE__ */ new Map();
|
|
285651
285847
|
const clientRunId = createCliRunId();
|
|
285652
285848
|
const externalController = new AbortController();
|
|
285653
285849
|
const runtimeRun = registerRuntimeRun({
|
|
@@ -285939,10 +286135,40 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
285939
286135
|
case "executor_waiting_for_user":
|
|
285940
286136
|
addItem({ label: "wait", text: event.prompt, tone: "muted" });
|
|
285941
286137
|
break;
|
|
286138
|
+
case "diagnostic": {
|
|
286139
|
+
if (event.code !== "tool_call_budget_exhausted") break;
|
|
286140
|
+
const limitKey = `limit-${event.workerId ?? "turn"}`;
|
|
286141
|
+
const meta2 = flockLimitMeta.get(limitKey) ?? { skipped: 0, tools: /* @__PURE__ */ new Set() };
|
|
286142
|
+
meta2.skipped += event.skippedCount ?? 0;
|
|
286143
|
+
for (const toolName of event.toolNames ?? []) meta2.tools.add(toolName);
|
|
286144
|
+
flockLimitMeta.set(limitKey, meta2);
|
|
286145
|
+
const who = event.workerId ? flockWorkerNames.get(event.workerId) ?? "A worker" : "The model";
|
|
286146
|
+
updateToolItem(limitKey, {
|
|
286147
|
+
label: "flock",
|
|
286148
|
+
text: `${who} hit its tool limit and is finishing from gathered evidence.`,
|
|
286149
|
+
tone: "muted",
|
|
286150
|
+
detailLines: [
|
|
286151
|
+
{ tone: "meta", text: `cap tool-call budget \xB7 ${event.maxToolCalls ?? "?"} real calls allowed` },
|
|
286152
|
+
{
|
|
286153
|
+
tone: "meta",
|
|
286154
|
+
text: `skipped ${meta2.skipped} call(s)${meta2.tools.size ? `: ${[...meta2.tools].join(", ")}` : ""}`
|
|
286155
|
+
}
|
|
286156
|
+
],
|
|
286157
|
+
expanded: false
|
|
286158
|
+
});
|
|
286159
|
+
break;
|
|
286160
|
+
}
|
|
285942
286161
|
case "flock_run_started":
|
|
285943
286162
|
case "flock_plan_ready":
|
|
285944
286163
|
case "flock_worker_update":
|
|
285945
286164
|
case "flock_run_completed": {
|
|
286165
|
+
if (event.type === "flock_plan_ready" && event.accepted) {
|
|
286166
|
+
for (const worker of event.workers) {
|
|
286167
|
+
flockWorkerNames.set(worker.workerId, worker.displayName);
|
|
286168
|
+
}
|
|
286169
|
+
} else if (event.type === "flock_worker_update") {
|
|
286170
|
+
flockWorkerNames.set(event.workerId, event.displayName);
|
|
286171
|
+
}
|
|
285946
286172
|
const row = flockEventToCliRow(event);
|
|
285947
286173
|
if (!row) break;
|
|
285948
286174
|
if (row.id) {
|
|
@@ -286102,6 +286328,43 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
286102
286328
|
renderInkDetailContent(React11, Ink2, line)
|
|
286103
286329
|
)
|
|
286104
286330
|
);
|
|
286331
|
+
const renderFlockRow = (key, line, tone, showLabel) => {
|
|
286332
|
+
const dotParts = line.split(" \xB7 ");
|
|
286333
|
+
const limitMatch = dotParts.length < 2 ? line.match(/^(.+?) (hit its tool limit .*)$/) : null;
|
|
286334
|
+
if (dotParts.length < 2 && !limitMatch) {
|
|
286335
|
+
return renderTranscriptRow(key, "flock", line, tone, showLabel);
|
|
286336
|
+
}
|
|
286337
|
+
if (limitMatch) {
|
|
286338
|
+
return renderTranscriptRow(key, "flock", line, tone, showLabel);
|
|
286339
|
+
}
|
|
286340
|
+
const name = dotParts[0];
|
|
286341
|
+
const nickname = dotParts[1] ?? "";
|
|
286342
|
+
const restText = dotParts.length > 2 ? ` \xB7 ${dotParts.slice(2).join(" \xB7 ")}` : "";
|
|
286343
|
+
return React11.createElement(
|
|
286344
|
+
Ink2.Box,
|
|
286345
|
+
{ key },
|
|
286346
|
+
React11.createElement(
|
|
286347
|
+
Ink2.Box,
|
|
286348
|
+
{ width: INK_LABEL_WIDTH, flexShrink: 0 },
|
|
286349
|
+
React11.createElement(
|
|
286350
|
+
Ink2.Text,
|
|
286351
|
+
{ color: colorForInkTone(tone) },
|
|
286352
|
+
showLabel ? renderInkSpeakerLabel("flock") : ""
|
|
286353
|
+
)
|
|
286354
|
+
),
|
|
286355
|
+
React11.createElement(
|
|
286356
|
+
Ink2.Box,
|
|
286357
|
+
{ flexGrow: 1 },
|
|
286358
|
+
React11.createElement(
|
|
286359
|
+
Ink2.Text,
|
|
286360
|
+
null,
|
|
286361
|
+
React11.createElement(Ink2.Text, { color: bodyColorForInkTone(tone) }, `${name} \xB7 `),
|
|
286362
|
+
React11.createElement(Ink2.Text, { color: flockNicknameAccentColor(nickname), bold: true }, nickname),
|
|
286363
|
+
React11.createElement(Ink2.Text, { color: bodyColorForInkTone(tone) }, restText)
|
|
286364
|
+
)
|
|
286365
|
+
)
|
|
286366
|
+
);
|
|
286367
|
+
};
|
|
286105
286368
|
const renderTranscriptItem = (item, index) => {
|
|
286106
286369
|
const lines = item.text.split(/\r?\n/);
|
|
286107
286370
|
const previous = items[index - 1];
|
|
@@ -286115,7 +286378,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
286115
286378
|
React11.createElement(Ink2.Box, { width: INK_LABEL_WIDTH, flexShrink: 0 }),
|
|
286116
286379
|
React11.createElement(Ink2.Text, { color: CLI_BRAND.divider }, INK_DIVIDER)
|
|
286117
286380
|
) : null,
|
|
286118
|
-
lines.map((line, lineIndex) => renderTranscriptRow(
|
|
286381
|
+
lines.map((line, lineIndex) => item.label === "flock" ? renderFlockRow(`${item.id}-${lineIndex}`, line, item.tone, lineIndex === 0) : renderTranscriptRow(
|
|
286119
286382
|
`${item.id}-${lineIndex}`,
|
|
286120
286383
|
item.label,
|
|
286121
286384
|
line,
|
|
@@ -286851,6 +287114,13 @@ function colorForInkTone(tone) {
|
|
|
286851
287114
|
return CLI_BRAND.patina;
|
|
286852
287115
|
}
|
|
286853
287116
|
}
|
|
287117
|
+
function flockNicknameAccentColor(name) {
|
|
287118
|
+
let hash = 0;
|
|
287119
|
+
for (let i = 0; i < name.length; i++) {
|
|
287120
|
+
hash = hash * 31 + name.charCodeAt(i) >>> 0;
|
|
287121
|
+
}
|
|
287122
|
+
return FLOCK_NICKNAME_ACCENTS[hash % FLOCK_NICKNAME_ACCENTS.length];
|
|
287123
|
+
}
|
|
286854
287124
|
function bodyColorForInkTone(tone) {
|
|
286855
287125
|
switch (tone) {
|
|
286856
287126
|
case "danger":
|
|
@@ -287533,7 +287803,7 @@ function defaultWriter() {
|
|
|
287533
287803
|
stderr: (text) => process.stderr.write(text)
|
|
287534
287804
|
};
|
|
287535
287805
|
}
|
|
287536
|
-
var execFileAsync3, DEFAULT_CLI_LOGIN_APP_URL, CLI_PACKAGE_VERSION, CLI_BRAND, ANSI2, HELP_TEXT, INTERACTIVE_HELP_TEXT, FLOCK_STATUS_LABELS, INK_LABEL_WIDTH, INK_ROW_LIMIT, INK_DETAIL_LINE_LIMIT, INK_DIVIDER, PERCH_MOTION_FRAMES, PERCH_SPLASH_WIDTH, PERCH_SPLASH_SCENE, PERCH_SPLASH_COMMANDS;
|
|
287806
|
+
var execFileAsync3, DEFAULT_CLI_LOGIN_APP_URL, CLI_PACKAGE_VERSION, CLI_BRAND, ANSI2, HELP_TEXT, INTERACTIVE_HELP_TEXT, FLOCK_STATUS_LABELS, INK_LABEL_WIDTH, INK_ROW_LIMIT, INK_DETAIL_LINE_LIMIT, INK_DIVIDER, PERCH_MOTION_FRAMES, PERCH_SPLASH_WIDTH, PERCH_SPLASH_SCENE, PERCH_SPLASH_COMMANDS, FLOCK_NICKNAME_ACCENTS;
|
|
287537
287807
|
var init_perch_cli = __esm({
|
|
287538
287808
|
"scripts/perch-cli.ts"() {
|
|
287539
287809
|
"use strict";
|
|
@@ -287669,6 +287939,11 @@ Commands:
|
|
|
287669
287939
|
["/permission", "change autonomy for the next turns"],
|
|
287670
287940
|
["/login", "connect your Perch account"]
|
|
287671
287941
|
];
|
|
287942
|
+
FLOCK_NICKNAME_ACCENTS = [
|
|
287943
|
+
CLI_BRAND.patinaActive,
|
|
287944
|
+
CLI_BRAND.bronze,
|
|
287945
|
+
CLI_BRAND.bronzeGlint
|
|
287946
|
+
];
|
|
287672
287947
|
if (!process.env.PERCH_CLI_BUNDLE_ENTRY && process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
287673
287948
|
runPerchCli(process.argv.slice(2)).then((code) => {
|
|
287674
287949
|
process.exitCode = code;
|