@wrongstack/core 0.308.1 → 0.308.4
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/coordination/index.d.ts +1 -0
- package/dist/coordination/index.js +111 -11
- package/dist/coordination/task-boundary.d.ts +64 -0
- package/dist/core/index.js +6 -2
- package/dist/defaults/index.js +114 -15
- package/dist/execution/index.js +7 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +129 -17
- package/dist/infrastructure/index.js +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/types/config/autonomy.d.ts +2 -2
- package/dist/types/context-window.d.ts +18 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +9 -3
- package/instructions/coordination/director-preamble.md +9 -1
- package/instructions/coordination/subagent-baseline.md +7 -0
- package/instructions/system-lite.md +2 -2
- package/instructions/system-pro.md +2 -2
- package/instructions/system.md +2 -2
- package/package.json +5 -4
|
@@ -69,6 +69,7 @@ TIMEOUT_PREEMPT_FRACTION, } from './subagent-budget.js';
|
|
|
69
69
|
export { assignNickname } from './subagent-nicknames.js';
|
|
70
70
|
export { formatSubagentStructuredReport, MAX_SUBAGENT_STRUCTURED_REPORT_CHARS, makeSubagentResultTool, normalizeSubagentStructuredReport, readSubagentStructuredReport, SUBAGENT_STRUCTURED_REPORT_META_KEY, } from './subagent-result-tool.js';
|
|
71
71
|
export { startTechStackConsumer, type TechStackConsumerOptions, } from './techstack-mailbox-consumer.js';
|
|
72
|
+
export { composeBoundedTaskDescription, type TaskBoundary, parseTaskBoundary, renderTaskBoundaryBlock, taskBoundarySchemaProperties, } from './task-boundary.js';
|
|
72
73
|
export { type FleetWorktreePolicy, resolveSubagentWorktreeDecision, subagentNeedsWorktree, WorktreeIntegrationError, type WorktreeIsolationDecision, type WorktreeTaskRunnerOptions, type WorktreeTaskStateUpdate, wrapSubagentRunnerWithWorktrees, } from './worktree-task-runner.js';
|
|
73
74
|
export { collabInjectMiddleware, collabPauseMiddleware, } from '../middleware/collab-pause.js';
|
|
74
75
|
export { AdaptiveConcurrencyController, type AdaptiveConcurrencyState, } from './adaptive-concurrency.js';
|
|
@@ -9607,6 +9607,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
9607
9607
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
9608
9608
|
${f.content}`).join("\n\n");
|
|
9609
9609
|
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
9610
|
+
This is an analysis role: do not edit the target files; report findings only.
|
|
9610
9611
|
|
|
9611
9612
|
Target files:
|
|
9612
9613
|
${fileContents}
|
|
@@ -9625,6 +9626,7 @@ Important: emit each finding as soon as you find it. Do not batch or wait until
|
|
|
9625
9626
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
9626
9627
|
${f.content}`).join("\n\n");
|
|
9627
9628
|
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
9629
|
+
This is an analysis role: do not edit the target files; emit the plan only.
|
|
9628
9630
|
|
|
9629
9631
|
Target files:
|
|
9630
9632
|
${fileContents}
|
|
@@ -9646,6 +9648,7 @@ Emit each plan immediately. Do not wait until planning is complete.`;
|
|
|
9646
9648
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
9647
9649
|
${f.content}`).join("\n\n");
|
|
9648
9650
|
return `You are Critic. Evaluate bug findings and refactor plans.
|
|
9651
|
+
This is an analysis role: do not edit the target files; emit evaluations only.
|
|
9649
9652
|
|
|
9650
9653
|
Target files:
|
|
9651
9654
|
${fileContents}
|
|
@@ -10481,6 +10484,80 @@ var FLEET_ROSTER_WITHACP = {
|
|
|
10481
10484
|
...Object.fromEntries(ACP_AGENTS.map((a) => [a.role, a]))
|
|
10482
10485
|
};
|
|
10483
10486
|
|
|
10487
|
+
// src/coordination/task-boundary.ts
|
|
10488
|
+
var PLACEHOLDER_VALUES = /* @__PURE__ */ new Set([
|
|
10489
|
+
"n/a",
|
|
10490
|
+
"na",
|
|
10491
|
+
"none",
|
|
10492
|
+
"nothing",
|
|
10493
|
+
"tbd",
|
|
10494
|
+
"todo",
|
|
10495
|
+
"unknown",
|
|
10496
|
+
"unspecified",
|
|
10497
|
+
"-",
|
|
10498
|
+
"\u2014",
|
|
10499
|
+
".",
|
|
10500
|
+
"as above",
|
|
10501
|
+
"same as above",
|
|
10502
|
+
"see above",
|
|
10503
|
+
"see task",
|
|
10504
|
+
"same as task"
|
|
10505
|
+
]);
|
|
10506
|
+
var isPlaceholder = (value) => PLACEHOLDER_VALUES.has(value.trim().toLowerCase());
|
|
10507
|
+
var MIN_SCOPE_CHARS = 8;
|
|
10508
|
+
var MIN_NON_GOAL_CHARS = 3;
|
|
10509
|
+
function parseTaskBoundary(raw) {
|
|
10510
|
+
const scope = typeof raw.scope === "string" ? raw.scope.trim() : "";
|
|
10511
|
+
if (scope.length < MIN_SCOPE_CHARS) {
|
|
10512
|
+
return {
|
|
10513
|
+
ok: false,
|
|
10514
|
+
error: `\`scope\` is missing or too vague \u2014 state in one concrete sentence what work this task covers (files, components, or commands in-bounds).`,
|
|
10515
|
+
hint: 'Example \u2014 scope: "Audit packages/core/src/parser/*.ts for unhandled token errors and report findings."'
|
|
10516
|
+
};
|
|
10517
|
+
}
|
|
10518
|
+
if (!Array.isArray(raw.outOfScope) || raw.outOfScope.length === 0) {
|
|
10519
|
+
return {
|
|
10520
|
+
ok: false,
|
|
10521
|
+
error: "`outOfScope` must be an array with at least one explicit non-goal \u2014 things the worker must NOT do.",
|
|
10522
|
+
hint: 'Example \u2014 outOfScope: ["Do not modify files outside packages/core", "Do not fix the bugs you find, only report them"].'
|
|
10523
|
+
};
|
|
10524
|
+
}
|
|
10525
|
+
const concrete = raw.outOfScope.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length >= MIN_NON_GOAL_CHARS && !isPlaceholder(item));
|
|
10526
|
+
if (concrete.length === 0) {
|
|
10527
|
+
return {
|
|
10528
|
+
ok: false,
|
|
10529
|
+
error: 'Every `outOfScope` entry was a placeholder ("none", "n/a", \u2026). Name at least one concrete non-goal: files or areas not to touch, changes not to make, features not to add.',
|
|
10530
|
+
hint: 'There is always an edge worth stating \u2014 "read-only, no edits", "no dependency changes", "do not touch other packages". If truly nothing comes to mind, the task is not decomposed enough yet.'
|
|
10531
|
+
};
|
|
10532
|
+
}
|
|
10533
|
+
return { ok: true, boundary: { scope, outOfScope: concrete } };
|
|
10534
|
+
}
|
|
10535
|
+
function renderTaskBoundaryBlock(boundary) {
|
|
10536
|
+
return [
|
|
10537
|
+
"\u2500\u2500 TASK BOUNDARY (hard contract \u2014 these lines define your edges) \u2500\u2500",
|
|
10538
|
+
`Scope (what this task covers):
|
|
10539
|
+
${boundary.scope}`,
|
|
10540
|
+
`Out of scope (explicit non-goals \u2014 do NOT do any of these):
|
|
10541
|
+
${boundary.outOfScope.map((item) => `- ${item}`).join("\n")}`
|
|
10542
|
+
].join("\n");
|
|
10543
|
+
}
|
|
10544
|
+
function composeBoundedTaskDescription(objective, boundary) {
|
|
10545
|
+
return `${objective.trim()}
|
|
10546
|
+
|
|
10547
|
+
${renderTaskBoundaryBlock(boundary)}`;
|
|
10548
|
+
}
|
|
10549
|
+
var taskBoundarySchemaProperties = {
|
|
10550
|
+
scope: {
|
|
10551
|
+
type: "string",
|
|
10552
|
+
description: "REQUIRED. One concrete sentence stating what work this task covers \u2014 the in-bounds. The call is rejected without it."
|
|
10553
|
+
},
|
|
10554
|
+
outOfScope: {
|
|
10555
|
+
type: "array",
|
|
10556
|
+
items: { type: "string", minLength: 1 },
|
|
10557
|
+
description: 'REQUIRED. At least one explicit non-goal the worker must NOT do (files/areas not to touch, changes not to make, features not to add). Placeholders like "none" are rejected.'
|
|
10558
|
+
}
|
|
10559
|
+
};
|
|
10560
|
+
|
|
10484
10561
|
// src/coordination/delegate-tool.ts
|
|
10485
10562
|
function createDelegateTool(opts) {
|
|
10486
10563
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
@@ -10490,8 +10567,9 @@ function createDelegateTool(opts) {
|
|
|
10490
10567
|
properties: {
|
|
10491
10568
|
task: {
|
|
10492
10569
|
type: "string",
|
|
10493
|
-
description: "
|
|
10570
|
+
description: "The objective \u2014 what the subagent should do, natural language, complete sentence(s). Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
10494
10571
|
},
|
|
10572
|
+
...taskBoundarySchemaProperties,
|
|
10495
10573
|
role: {
|
|
10496
10574
|
type: "string",
|
|
10497
10575
|
description: rosterIds.length > 0 ? "Roster role id. Common: bug-hunter, security-scanner, refactor-planner, critic, audit-log, executor, shadow-agent, architect." : "No roster configured \u2014 pass `name` instead."
|
|
@@ -10549,12 +10627,12 @@ function createDelegateTool(opts) {
|
|
|
10549
10627
|
description: "Max fresh-worker continuations after budget exhaustion. Default 1. Each gets the prior partial report."
|
|
10550
10628
|
}
|
|
10551
10629
|
},
|
|
10552
|
-
required: ["task"]
|
|
10630
|
+
required: ["task", "scope", "outOfScope"]
|
|
10553
10631
|
};
|
|
10554
10632
|
return {
|
|
10555
10633
|
name: "delegate",
|
|
10556
10634
|
description: "Hand a piece of work to a subagent and block until it returns. This call is synchronous: the leader's iteration pauses for the full duration of the subagent's run. (Multiple `delegate` calls fired in the same assistant turn still parallelize through the provider's parallel-tool-call surface, but each one eats wall-clock time \u2014 so for fan-out you actually control, reach for the async path below.) Use `delegate` when your next step genuinely needs the subagent's verdict \u2014 a review, a fact-check, a sign-off. Has own context, own LLM call, auto-extending budget, and a partial-completion handoff path (maxHandoffs, default 1). Workers cannot recursively spawn.\n\n**Do NOT use `delegate` for long-running work.** While `delegate` is in flight, the leader is fully blocked \u2014 it cannot act on other tools, read mail, or react to the user. If the work might run for tens of minutes or hours (multi-file refactor, monorepo audit, long-running build/test, sweeping migration), the blocking call wastes the leader's time. Use the async tool family instead: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then `await_tasks` to retrieve results later. The leader keeps doing other work while the worker churns, and a worker that realizes its task will run long can mail the leader (type `steer` or `ask` via `mail_send`) saying *\"my task is going to run long, please spawn a subagent instead\"* so the leader re-dispatches asynchronously instead of waiting.\n\n**Do NOT use `delegate` for fan-out you control.** Multiple sequential `delegate` calls each block the leader, wasting wall-clock time. For independent investigations you want to run in parallel \u2014 security scan + bug hunt + perf review on the same PR \u2014 use the async tool family: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then the `await_tasks` tool with `{mode: 'any'}` to fold the first useful result into the next decision while the rest keep churning. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable.",
|
|
10557
|
-
usageHint: "Set `task` to a
|
|
10635
|
+
usageHint: "Set `task` to the objective, then make the edges explicit: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) are REQUIRED \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Pick `role` from roster or pass `name` for free-form. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable (minutes, not hours). For long-running work or fan-out you control, use `spawn_subagent` + `assign_task` + `await_tasks` instead. Raise `maxHandoffs` (default 1, cap 8) for multi-day or multi-refactor tasks; pass larger `timeoutMs`/`maxIterations`/`maxToolCalls` only when needed.",
|
|
10558
10636
|
permission: "auto",
|
|
10559
10637
|
mutating: false,
|
|
10560
10638
|
managesOwnTimeout: true,
|
|
@@ -10574,6 +10652,14 @@ function createDelegateTool(opts) {
|
|
|
10574
10652
|
error: "Delegation cancelled before spawn \u2014 the run was interrupted."
|
|
10575
10653
|
};
|
|
10576
10654
|
}
|
|
10655
|
+
const boundary = parseTaskBoundary(i);
|
|
10656
|
+
if (!boundary.ok) {
|
|
10657
|
+
return {
|
|
10658
|
+
ok: false,
|
|
10659
|
+
error: `delegate rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
10660
|
+
hint: boundary.hint
|
|
10661
|
+
};
|
|
10662
|
+
}
|
|
10577
10663
|
const target = i.role ?? i.name ?? "subagent";
|
|
10578
10664
|
const launchModePreface = [
|
|
10579
10665
|
"Launch-mode guidance (delegate): you were launched via the synchronous `delegate` tool, so the leader is blocked on this call for the full duration of your run.",
|
|
@@ -10644,7 +10730,8 @@ function createDelegateTool(opts) {
|
|
|
10644
10730
|
const dir = director;
|
|
10645
10731
|
const maxHandoffs = Math.min(8, Math.max(0, Math.floor(i.maxHandoffs ?? 1)));
|
|
10646
10732
|
const handoffs = [];
|
|
10647
|
-
|
|
10733
|
+
const baseBrief = composeBoundedTaskDescription(i.task, boundary.boundary);
|
|
10734
|
+
let delegatedTask = baseBrief;
|
|
10648
10735
|
let handoffCount = 0;
|
|
10649
10736
|
for (; ; ) {
|
|
10650
10737
|
const attemptConfig = (() => {
|
|
@@ -10784,7 +10871,7 @@ function createDelegateTool(opts) {
|
|
|
10784
10871
|
remainingWork: continuation.remainingWork
|
|
10785
10872
|
});
|
|
10786
10873
|
handoffCount += 1;
|
|
10787
|
-
delegatedTask = buildHandoffTask(
|
|
10874
|
+
delegatedTask = buildHandoffTask(baseBrief, continuation, handoffCount, maxHandoffs);
|
|
10788
10875
|
continue;
|
|
10789
10876
|
}
|
|
10790
10877
|
const incomplete = result.report?.completion === "partial";
|
|
@@ -12688,8 +12775,9 @@ function makeAssignTool(director) {
|
|
|
12688
12775
|
description: {
|
|
12689
12776
|
type: "string",
|
|
12690
12777
|
minLength: 1,
|
|
12691
|
-
description: "The
|
|
12778
|
+
description: "The objective in natural language \u2014 what you want this subagent to do. Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
12692
12779
|
},
|
|
12780
|
+
...taskBoundarySchemaProperties,
|
|
12693
12781
|
maxToolCalls: {
|
|
12694
12782
|
type: "number",
|
|
12695
12783
|
minimum: 1,
|
|
@@ -12697,20 +12785,28 @@ function makeAssignTool(director) {
|
|
|
12697
12785
|
},
|
|
12698
12786
|
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
12699
12787
|
},
|
|
12700
|
-
required: ["subagentId", "description"]
|
|
12788
|
+
required: ["subagentId", "description", "scope", "outOfScope"]
|
|
12701
12789
|
};
|
|
12702
12790
|
return {
|
|
12703
12791
|
name: "assign_task",
|
|
12704
|
-
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
12792
|
+
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Every assignment MUST carry an explicit boundary: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
12705
12793
|
permission: "auto",
|
|
12706
12794
|
mutating: false,
|
|
12707
12795
|
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
12708
12796
|
inputSchema,
|
|
12709
12797
|
async execute(input) {
|
|
12710
12798
|
const i = input;
|
|
12799
|
+
const boundary = parseTaskBoundary(i);
|
|
12800
|
+
if (!boundary.ok) {
|
|
12801
|
+
return {
|
|
12802
|
+
ok: false,
|
|
12803
|
+
error: `assign_task rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
12804
|
+
hint: boundary.hint
|
|
12805
|
+
};
|
|
12806
|
+
}
|
|
12711
12807
|
const task = {
|
|
12712
12808
|
id: randomUUID8(),
|
|
12713
|
-
description: i.description,
|
|
12809
|
+
description: composeBoundedTaskDescription(i.description, boundary.boundary),
|
|
12714
12810
|
subagentId: i.subagentId,
|
|
12715
12811
|
maxToolCalls: i.maxToolCalls,
|
|
12716
12812
|
timeoutMs: i.timeoutMs
|
|
@@ -20503,8 +20599,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
20503
20599
|
withNickname(subagent, subagentId) {
|
|
20504
20600
|
const role = subagent.role ?? "subagent";
|
|
20505
20601
|
const name = subagent.name?.trim() ?? "";
|
|
20506
|
-
const
|
|
20507
|
-
if (!
|
|
20602
|
+
const isPlaceholder2 = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
20603
|
+
if (!isPlaceholder2) return subagent;
|
|
20508
20604
|
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
20509
20605
|
this.usedNicknames.add(key);
|
|
20510
20606
|
this.subagentNicknames.set(subagentId, key);
|
|
@@ -31367,6 +31463,7 @@ export {
|
|
|
31367
31463
|
clearProjectSkillAugmentation,
|
|
31368
31464
|
collabInjectMiddleware,
|
|
31369
31465
|
collabPauseMiddleware,
|
|
31466
|
+
composeBoundedTaskDescription,
|
|
31370
31467
|
composeDirectorPrompt,
|
|
31371
31468
|
composeSubagentPrompt,
|
|
31372
31469
|
consolidatedDocumentPath,
|
|
@@ -31451,6 +31548,7 @@ export {
|
|
|
31451
31548
|
parseMailboxAckInput,
|
|
31452
31549
|
parseMailboxQueryInput,
|
|
31453
31550
|
parseMailboxSendInput,
|
|
31551
|
+
parseTaskBoundary,
|
|
31454
31552
|
phaseForRole,
|
|
31455
31553
|
quarantinePath,
|
|
31456
31554
|
rankRoleSkills,
|
|
@@ -31467,6 +31565,7 @@ export {
|
|
|
31467
31565
|
refreshProjectAgentIdentity,
|
|
31468
31566
|
release,
|
|
31469
31567
|
renderSkillAugmentation,
|
|
31568
|
+
renderTaskBoundaryBlock,
|
|
31470
31569
|
resetCaptureWindow,
|
|
31471
31570
|
resetCaptureWindows,
|
|
31472
31571
|
resetProjectAgentIdentity,
|
|
@@ -31497,6 +31596,7 @@ export {
|
|
|
31497
31596
|
startPackageOutdatedWatcher,
|
|
31498
31597
|
startTechStackConsumer,
|
|
31499
31598
|
subagentNeedsWorktree,
|
|
31599
|
+
taskBoundarySchemaProperties,
|
|
31500
31600
|
terminalPolicyDecision,
|
|
31501
31601
|
unwrapWholeDocumentFence,
|
|
31502
31602
|
updatePackageOutdatedStatus,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hard task-boundary contract for leader → subagent assignment tools.
|
|
3
|
+
*
|
|
4
|
+
* `delegate` and `assign_task` reject any assignment that does not carry an
|
|
5
|
+
* explicit in-bounds `scope` and at least one concrete out-of-scope non-goal.
|
|
6
|
+
* Prompt-only guidance ("state the scope and non-goals") degrades under
|
|
7
|
+
* pressure: a rushed leader writes "fix it", the worker guesses the edges, and
|
|
8
|
+
* the drift only surfaces at review time — the most expensive place to find
|
|
9
|
+
* it. Forcing the boundary into structured fields makes the edges
|
|
10
|
+
* machine-checkable at assignment time and lets the tool return a teaching
|
|
11
|
+
* error that names exactly what is missing, so the leader self-corrects on
|
|
12
|
+
* the next call instead of shipping a vague brief.
|
|
13
|
+
*
|
|
14
|
+
* The parsed boundary is composed into `TaskSpec.description` as a clearly
|
|
15
|
+
* delimited block. That is deliberate: the description is the canonical brief
|
|
16
|
+
* every runner, transcript, handoff continuation, and roll-up already
|
|
17
|
+
* consumes, so the boundary travels with the task everywhere without new
|
|
18
|
+
* plumbing (and `buildHandoffTask`'s "Original task:" carry-over preserves it
|
|
19
|
+
* into fresh workers for free).
|
|
20
|
+
*/
|
|
21
|
+
import type { JSONSchema } from '../types/tool.js';
|
|
22
|
+
export interface TaskBoundary {
|
|
23
|
+
/** What the work covers — the in-bounds statement. */
|
|
24
|
+
scope: string;
|
|
25
|
+
/** Explicit non-goals: things the worker must NOT do. */
|
|
26
|
+
outOfScope: string[];
|
|
27
|
+
}
|
|
28
|
+
export type TaskBoundaryParseResult = {
|
|
29
|
+
ok: true;
|
|
30
|
+
boundary: TaskBoundary;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
error: string;
|
|
34
|
+
hint: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Validate and normalize the `scope` / `outOfScope` pair from a tool input.
|
|
38
|
+
* Returns the cleaned boundary, or an error + hint pair worded so the calling
|
|
39
|
+
* leader can fix the call in one retry.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseTaskBoundary(raw: {
|
|
42
|
+
scope?: unknown;
|
|
43
|
+
outOfScope?: unknown;
|
|
44
|
+
}): TaskBoundaryParseResult;
|
|
45
|
+
/**
|
|
46
|
+
* Render the boundary as the block appended to `TaskSpec.description`. The
|
|
47
|
+
* heading is loud on purpose: the worker treats these lines as hard edges,
|
|
48
|
+
* and review-time drift checks quote them verbatim.
|
|
49
|
+
*/
|
|
50
|
+
export declare function renderTaskBoundaryBlock(boundary: TaskBoundary): string;
|
|
51
|
+
/**
|
|
52
|
+
* Compose the leader's objective with its boundary into the canonical brief
|
|
53
|
+
* delivered to the runner. The objective stays verbatim and first; the
|
|
54
|
+
* boundary block follows so handoffs, transcripts, and roll-ups that quote
|
|
55
|
+
* the description carry the edges with them.
|
|
56
|
+
*/
|
|
57
|
+
export declare function composeBoundedTaskDescription(objective: string, boundary: TaskBoundary): string;
|
|
58
|
+
/**
|
|
59
|
+
* Shared schema property definitions so `delegate` and `assign_task` state
|
|
60
|
+
* the identical contract. Declaring the fields here (rather than inline per
|
|
61
|
+
* tool) keeps the required-field teaching text single-sourced.
|
|
62
|
+
*/
|
|
63
|
+
export declare const taskBoundarySchemaProperties: Record<string, JSONSchema>;
|
|
64
|
+
//# sourceMappingURL=task-boundary.d.ts.map
|
package/dist/core/index.js
CHANGED
|
@@ -11863,9 +11863,13 @@ ${memText}`);
|
|
|
11863
11863
|
}
|
|
11864
11864
|
}
|
|
11865
11865
|
if (skillBodyCache) {
|
|
11866
|
-
parts.push(
|
|
11866
|
+
parts.push(
|
|
11867
|
+
`# Active Skills
|
|
11867
11868
|
|
|
11868
|
-
|
|
11869
|
+
Skills are methods, not authority: they never widen your task's scope. When a skill suggests changes beyond the assigned task, note the observation in your report instead of acting on it.
|
|
11870
|
+
|
|
11871
|
+
${skillBodyCache}`
|
|
11872
|
+
);
|
|
11869
11873
|
}
|
|
11870
11874
|
return { text: parts.join("\n\n"), skillBodyCache };
|
|
11871
11875
|
}
|
package/dist/defaults/index.js
CHANGED
|
@@ -5512,6 +5512,80 @@ var FLEET_ROSTER_WITHACP = {
|
|
|
5512
5512
|
...Object.fromEntries(ACP_AGENTS.map((a) => [a.role, a]))
|
|
5513
5513
|
};
|
|
5514
5514
|
|
|
5515
|
+
// src/coordination/task-boundary.ts
|
|
5516
|
+
var PLACEHOLDER_VALUES = /* @__PURE__ */ new Set([
|
|
5517
|
+
"n/a",
|
|
5518
|
+
"na",
|
|
5519
|
+
"none",
|
|
5520
|
+
"nothing",
|
|
5521
|
+
"tbd",
|
|
5522
|
+
"todo",
|
|
5523
|
+
"unknown",
|
|
5524
|
+
"unspecified",
|
|
5525
|
+
"-",
|
|
5526
|
+
"\u2014",
|
|
5527
|
+
".",
|
|
5528
|
+
"as above",
|
|
5529
|
+
"same as above",
|
|
5530
|
+
"see above",
|
|
5531
|
+
"see task",
|
|
5532
|
+
"same as task"
|
|
5533
|
+
]);
|
|
5534
|
+
var isPlaceholder = (value) => PLACEHOLDER_VALUES.has(value.trim().toLowerCase());
|
|
5535
|
+
var MIN_SCOPE_CHARS = 8;
|
|
5536
|
+
var MIN_NON_GOAL_CHARS = 3;
|
|
5537
|
+
function parseTaskBoundary(raw) {
|
|
5538
|
+
const scope = typeof raw.scope === "string" ? raw.scope.trim() : "";
|
|
5539
|
+
if (scope.length < MIN_SCOPE_CHARS) {
|
|
5540
|
+
return {
|
|
5541
|
+
ok: false,
|
|
5542
|
+
error: `\`scope\` is missing or too vague \u2014 state in one concrete sentence what work this task covers (files, components, or commands in-bounds).`,
|
|
5543
|
+
hint: 'Example \u2014 scope: "Audit packages/core/src/parser/*.ts for unhandled token errors and report findings."'
|
|
5544
|
+
};
|
|
5545
|
+
}
|
|
5546
|
+
if (!Array.isArray(raw.outOfScope) || raw.outOfScope.length === 0) {
|
|
5547
|
+
return {
|
|
5548
|
+
ok: false,
|
|
5549
|
+
error: "`outOfScope` must be an array with at least one explicit non-goal \u2014 things the worker must NOT do.",
|
|
5550
|
+
hint: 'Example \u2014 outOfScope: ["Do not modify files outside packages/core", "Do not fix the bugs you find, only report them"].'
|
|
5551
|
+
};
|
|
5552
|
+
}
|
|
5553
|
+
const concrete = raw.outOfScope.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length >= MIN_NON_GOAL_CHARS && !isPlaceholder(item));
|
|
5554
|
+
if (concrete.length === 0) {
|
|
5555
|
+
return {
|
|
5556
|
+
ok: false,
|
|
5557
|
+
error: 'Every `outOfScope` entry was a placeholder ("none", "n/a", \u2026). Name at least one concrete non-goal: files or areas not to touch, changes not to make, features not to add.',
|
|
5558
|
+
hint: 'There is always an edge worth stating \u2014 "read-only, no edits", "no dependency changes", "do not touch other packages". If truly nothing comes to mind, the task is not decomposed enough yet.'
|
|
5559
|
+
};
|
|
5560
|
+
}
|
|
5561
|
+
return { ok: true, boundary: { scope, outOfScope: concrete } };
|
|
5562
|
+
}
|
|
5563
|
+
function renderTaskBoundaryBlock(boundary) {
|
|
5564
|
+
return [
|
|
5565
|
+
"\u2500\u2500 TASK BOUNDARY (hard contract \u2014 these lines define your edges) \u2500\u2500",
|
|
5566
|
+
`Scope (what this task covers):
|
|
5567
|
+
${boundary.scope}`,
|
|
5568
|
+
`Out of scope (explicit non-goals \u2014 do NOT do any of these):
|
|
5569
|
+
${boundary.outOfScope.map((item) => `- ${item}`).join("\n")}`
|
|
5570
|
+
].join("\n");
|
|
5571
|
+
}
|
|
5572
|
+
function composeBoundedTaskDescription(objective, boundary) {
|
|
5573
|
+
return `${objective.trim()}
|
|
5574
|
+
|
|
5575
|
+
${renderTaskBoundaryBlock(boundary)}`;
|
|
5576
|
+
}
|
|
5577
|
+
var taskBoundarySchemaProperties = {
|
|
5578
|
+
scope: {
|
|
5579
|
+
type: "string",
|
|
5580
|
+
description: "REQUIRED. One concrete sentence stating what work this task covers \u2014 the in-bounds. The call is rejected without it."
|
|
5581
|
+
},
|
|
5582
|
+
outOfScope: {
|
|
5583
|
+
type: "array",
|
|
5584
|
+
items: { type: "string", minLength: 1 },
|
|
5585
|
+
description: 'REQUIRED. At least one explicit non-goal the worker must NOT do (files/areas not to touch, changes not to make, features not to add). Placeholders like "none" are rejected.'
|
|
5586
|
+
}
|
|
5587
|
+
};
|
|
5588
|
+
|
|
5515
5589
|
// src/coordination/delegate-tool.ts
|
|
5516
5590
|
function createDelegateTool(opts) {
|
|
5517
5591
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
@@ -5521,8 +5595,9 @@ function createDelegateTool(opts) {
|
|
|
5521
5595
|
properties: {
|
|
5522
5596
|
task: {
|
|
5523
5597
|
type: "string",
|
|
5524
|
-
description: "
|
|
5598
|
+
description: "The objective \u2014 what the subagent should do, natural language, complete sentence(s). Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
5525
5599
|
},
|
|
5600
|
+
...taskBoundarySchemaProperties,
|
|
5526
5601
|
role: {
|
|
5527
5602
|
type: "string",
|
|
5528
5603
|
description: rosterIds.length > 0 ? "Roster role id. Common: bug-hunter, security-scanner, refactor-planner, critic, audit-log, executor, shadow-agent, architect." : "No roster configured \u2014 pass `name` instead."
|
|
@@ -5580,12 +5655,12 @@ function createDelegateTool(opts) {
|
|
|
5580
5655
|
description: "Max fresh-worker continuations after budget exhaustion. Default 1. Each gets the prior partial report."
|
|
5581
5656
|
}
|
|
5582
5657
|
},
|
|
5583
|
-
required: ["task"]
|
|
5658
|
+
required: ["task", "scope", "outOfScope"]
|
|
5584
5659
|
};
|
|
5585
5660
|
return {
|
|
5586
5661
|
name: "delegate",
|
|
5587
5662
|
description: "Hand a piece of work to a subagent and block until it returns. This call is synchronous: the leader's iteration pauses for the full duration of the subagent's run. (Multiple `delegate` calls fired in the same assistant turn still parallelize through the provider's parallel-tool-call surface, but each one eats wall-clock time \u2014 so for fan-out you actually control, reach for the async path below.) Use `delegate` when your next step genuinely needs the subagent's verdict \u2014 a review, a fact-check, a sign-off. Has own context, own LLM call, auto-extending budget, and a partial-completion handoff path (maxHandoffs, default 1). Workers cannot recursively spawn.\n\n**Do NOT use `delegate` for long-running work.** While `delegate` is in flight, the leader is fully blocked \u2014 it cannot act on other tools, read mail, or react to the user. If the work might run for tens of minutes or hours (multi-file refactor, monorepo audit, long-running build/test, sweeping migration), the blocking call wastes the leader's time. Use the async tool family instead: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then `await_tasks` to retrieve results later. The leader keeps doing other work while the worker churns, and a worker that realizes its task will run long can mail the leader (type `steer` or `ask` via `mail_send`) saying *\"my task is going to run long, please spawn a subagent instead\"* so the leader re-dispatches asynchronously instead of waiting.\n\n**Do NOT use `delegate` for fan-out you control.** Multiple sequential `delegate` calls each block the leader, wasting wall-clock time. For independent investigations you want to run in parallel \u2014 security scan + bug hunt + perf review on the same PR \u2014 use the async tool family: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then the `await_tasks` tool with `{mode: 'any'}` to fold the first useful result into the next decision while the rest keep churning. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable.",
|
|
5588
|
-
usageHint: "Set `task` to a
|
|
5663
|
+
usageHint: "Set `task` to the objective, then make the edges explicit: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) are REQUIRED \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Pick `role` from roster or pass `name` for free-form. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable (minutes, not hours). For long-running work or fan-out you control, use `spawn_subagent` + `assign_task` + `await_tasks` instead. Raise `maxHandoffs` (default 1, cap 8) for multi-day or multi-refactor tasks; pass larger `timeoutMs`/`maxIterations`/`maxToolCalls` only when needed.",
|
|
5589
5664
|
permission: "auto",
|
|
5590
5665
|
mutating: false,
|
|
5591
5666
|
managesOwnTimeout: true,
|
|
@@ -5605,6 +5680,14 @@ function createDelegateTool(opts) {
|
|
|
5605
5680
|
error: "Delegation cancelled before spawn \u2014 the run was interrupted."
|
|
5606
5681
|
};
|
|
5607
5682
|
}
|
|
5683
|
+
const boundary = parseTaskBoundary(i);
|
|
5684
|
+
if (!boundary.ok) {
|
|
5685
|
+
return {
|
|
5686
|
+
ok: false,
|
|
5687
|
+
error: `delegate rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
5688
|
+
hint: boundary.hint
|
|
5689
|
+
};
|
|
5690
|
+
}
|
|
5608
5691
|
const target = i.role ?? i.name ?? "subagent";
|
|
5609
5692
|
const launchModePreface = [
|
|
5610
5693
|
"Launch-mode guidance (delegate): you were launched via the synchronous `delegate` tool, so the leader is blocked on this call for the full duration of your run.",
|
|
@@ -5675,7 +5758,8 @@ function createDelegateTool(opts) {
|
|
|
5675
5758
|
const dir = director;
|
|
5676
5759
|
const maxHandoffs = Math.min(8, Math.max(0, Math.floor(i.maxHandoffs ?? 1)));
|
|
5677
5760
|
const handoffs = [];
|
|
5678
|
-
|
|
5761
|
+
const baseBrief = composeBoundedTaskDescription(i.task, boundary.boundary);
|
|
5762
|
+
let delegatedTask = baseBrief;
|
|
5679
5763
|
let handoffCount = 0;
|
|
5680
5764
|
for (; ; ) {
|
|
5681
5765
|
const attemptConfig = (() => {
|
|
@@ -5815,7 +5899,7 @@ function createDelegateTool(opts) {
|
|
|
5815
5899
|
remainingWork: continuation.remainingWork
|
|
5816
5900
|
});
|
|
5817
5901
|
handoffCount += 1;
|
|
5818
|
-
delegatedTask = buildHandoffTask(
|
|
5902
|
+
delegatedTask = buildHandoffTask(baseBrief, continuation, handoffCount, maxHandoffs);
|
|
5819
5903
|
continue;
|
|
5820
5904
|
}
|
|
5821
5905
|
const incomplete = result.report?.completion === "partial";
|
|
@@ -7378,6 +7462,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
7378
7462
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
7379
7463
|
${f.content}`).join("\n\n");
|
|
7380
7464
|
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
7465
|
+
This is an analysis role: do not edit the target files; report findings only.
|
|
7381
7466
|
|
|
7382
7467
|
Target files:
|
|
7383
7468
|
${fileContents}
|
|
@@ -7396,6 +7481,7 @@ Important: emit each finding as soon as you find it. Do not batch or wait until
|
|
|
7396
7481
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
7397
7482
|
${f.content}`).join("\n\n");
|
|
7398
7483
|
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
7484
|
+
This is an analysis role: do not edit the target files; emit the plan only.
|
|
7399
7485
|
|
|
7400
7486
|
Target files:
|
|
7401
7487
|
${fileContents}
|
|
@@ -7417,6 +7503,7 @@ Emit each plan immediately. Do not wait until planning is complete.`;
|
|
|
7417
7503
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
7418
7504
|
${f.content}`).join("\n\n");
|
|
7419
7505
|
return `You are Critic. Evaluate bug findings and refactor plans.
|
|
7506
|
+
This is an analysis role: do not edit the target files; emit evaluations only.
|
|
7420
7507
|
|
|
7421
7508
|
Target files:
|
|
7422
7509
|
${fileContents}
|
|
@@ -8503,8 +8590,9 @@ function makeAssignTool(director) {
|
|
|
8503
8590
|
description: {
|
|
8504
8591
|
type: "string",
|
|
8505
8592
|
minLength: 1,
|
|
8506
|
-
description: "The
|
|
8593
|
+
description: "The objective in natural language \u2014 what you want this subagent to do. Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
8507
8594
|
},
|
|
8595
|
+
...taskBoundarySchemaProperties,
|
|
8508
8596
|
maxToolCalls: {
|
|
8509
8597
|
type: "number",
|
|
8510
8598
|
minimum: 1,
|
|
@@ -8512,20 +8600,28 @@ function makeAssignTool(director) {
|
|
|
8512
8600
|
},
|
|
8513
8601
|
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
8514
8602
|
},
|
|
8515
|
-
required: ["subagentId", "description"]
|
|
8603
|
+
required: ["subagentId", "description", "scope", "outOfScope"]
|
|
8516
8604
|
};
|
|
8517
8605
|
return {
|
|
8518
8606
|
name: "assign_task",
|
|
8519
|
-
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
8607
|
+
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Every assignment MUST carry an explicit boundary: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
8520
8608
|
permission: "auto",
|
|
8521
8609
|
mutating: false,
|
|
8522
8610
|
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
8523
8611
|
inputSchema,
|
|
8524
8612
|
async execute(input) {
|
|
8525
8613
|
const i = input;
|
|
8614
|
+
const boundary = parseTaskBoundary(i);
|
|
8615
|
+
if (!boundary.ok) {
|
|
8616
|
+
return {
|
|
8617
|
+
ok: false,
|
|
8618
|
+
error: `assign_task rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
8619
|
+
hint: boundary.hint
|
|
8620
|
+
};
|
|
8621
|
+
}
|
|
8526
8622
|
const task = {
|
|
8527
8623
|
id: randomUUID6(),
|
|
8528
|
-
description: i.description,
|
|
8624
|
+
description: composeBoundedTaskDescription(i.description, boundary.boundary),
|
|
8529
8625
|
subagentId: i.subagentId,
|
|
8530
8626
|
maxToolCalls: i.maxToolCalls,
|
|
8531
8627
|
timeoutMs: i.timeoutMs
|
|
@@ -19228,8 +19324,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19228
19324
|
withNickname(subagent, subagentId) {
|
|
19229
19325
|
const role = subagent.role ?? "subagent";
|
|
19230
19326
|
const name = subagent.name?.trim() ?? "";
|
|
19231
|
-
const
|
|
19232
|
-
if (!
|
|
19327
|
+
const isPlaceholder2 = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
19328
|
+
if (!isPlaceholder2) return subagent;
|
|
19233
19329
|
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
19234
19330
|
this.usedNicknames.add(key);
|
|
19235
19331
|
this.subagentNicknames.set(subagentId, key);
|
|
@@ -22497,6 +22593,7 @@ function normalizeTargetLoad(targetLoad, thresholds) {
|
|
|
22497
22593
|
|
|
22498
22594
|
// src/types/context-window.ts
|
|
22499
22595
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
22596
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
22500
22597
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
22501
22598
|
archival: "balanced"
|
|
22502
22599
|
});
|
|
@@ -22504,7 +22601,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
22504
22601
|
{
|
|
22505
22602
|
id: "balanced",
|
|
22506
22603
|
name: "Balanced",
|
|
22507
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
22604
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
22508
22605
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
22509
22606
|
aggressiveOn: "soft",
|
|
22510
22607
|
preserveK: 8,
|
|
@@ -22555,9 +22652,11 @@ function getContextWindowMode(id) {
|
|
|
22555
22652
|
function isContextWindowModeId(id) {
|
|
22556
22653
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
22557
22654
|
}
|
|
22558
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
22655
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
22559
22656
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
22560
|
-
const
|
|
22657
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
22658
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
22659
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
22561
22660
|
return {
|
|
22562
22661
|
...mode,
|
|
22563
22662
|
thresholds: {
|
|
@@ -22972,7 +23071,7 @@ function readContextWindowPolicy(ctx) {
|
|
|
22972
23071
|
function installSubagentAutoCompaction(pipelines, ctx, contextConfig, events) {
|
|
22973
23072
|
const maxContext = ctx.provider?.capabilities?.maxContext ?? 0;
|
|
22974
23073
|
if (!(maxContext > 0)) return void 0;
|
|
22975
|
-
const policy = resolveContextWindowPolicy(contextConfig ?? {});
|
|
23074
|
+
const policy = resolveContextWindowPolicy(contextConfig ?? {}, void 0, maxContext);
|
|
22976
23075
|
ctx.meta ??= {};
|
|
22977
23076
|
ctx.meta["contextWindowPolicy"] = policy;
|
|
22978
23077
|
const compactor = new HybridCompactor({
|
package/dist/execution/index.js
CHANGED
|
@@ -21421,6 +21421,7 @@ function readPolicy(ctx) {
|
|
|
21421
21421
|
|
|
21422
21422
|
// src/types/context-window.ts
|
|
21423
21423
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
21424
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
21424
21425
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
21425
21426
|
archival: "balanced"
|
|
21426
21427
|
});
|
|
@@ -21428,7 +21429,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
21428
21429
|
{
|
|
21429
21430
|
id: "balanced",
|
|
21430
21431
|
name: "Balanced",
|
|
21431
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
21432
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
21432
21433
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
21433
21434
|
aggressiveOn: "soft",
|
|
21434
21435
|
preserveK: 8,
|
|
@@ -21470,9 +21471,11 @@ function getContextWindowMode(id) {
|
|
|
21470
21471
|
function isContextWindowModeId(id) {
|
|
21471
21472
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
21472
21473
|
}
|
|
21473
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
21474
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
21474
21475
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
21475
|
-
const
|
|
21476
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
21477
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
21478
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
21476
21479
|
return {
|
|
21477
21480
|
...mode,
|
|
21478
21481
|
thresholds: {
|
|
@@ -21490,7 +21493,7 @@ function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
|
21490
21493
|
function installSubagentAutoCompaction(pipelines, ctx, contextConfig, events) {
|
|
21491
21494
|
const maxContext = ctx.provider?.capabilities?.maxContext ?? 0;
|
|
21492
21495
|
if (!(maxContext > 0)) return void 0;
|
|
21493
|
-
const policy = resolveContextWindowPolicy(contextConfig ?? {});
|
|
21496
|
+
const policy = resolveContextWindowPolicy(contextConfig ?? {}, void 0, maxContext);
|
|
21494
21497
|
ctx.meta ??= {};
|
|
21495
21498
|
ctx.meta["contextWindowPolicy"] = policy;
|
|
21496
21499
|
const compactor = new HybridCompactor({
|
package/dist/index.d.ts
CHANGED
|
@@ -134,7 +134,7 @@ export { createMcpUseTool } from './tools/mcp-use.js';
|
|
|
134
134
|
export { type CreateOneShotLLMToolOptions, createOneShotLLMTool, ONE_SHOT_LLM_TOOL_NAME, } from './tools/one-shot-llm-tool.js';
|
|
135
135
|
export { type CreatePluginManagerToolOptions, createPluginManagerTool, PLUGIN_MANAGER_TOOL_NAME, type PluginManagerCatalogEntry, type PluginManagerMutationResult, } from './tools/plugin-manager.js';
|
|
136
136
|
export type { Compactor, CompactReport } from './types/compactor.js';
|
|
137
|
-
export { CONTEXT_WINDOW_MODES, type ContextWindowAggressiveOn, type ContextWindowConfigLike, type ContextWindowMode, type ContextWindowModeId, type ContextWindowModeSelectionId, type ContextWindowPolicy, type ContextWindowThresholds, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, type DeprecatedContextWindowModeId, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './types/context-window.js';
|
|
137
|
+
export { CONTEXT_WINDOW_MODES, type ContextWindowAggressiveOn, type ContextWindowConfigLike, type ContextWindowMode, type ContextWindowModeId, type ContextWindowModeSelectionId, type ContextWindowPolicy, type ContextWindowThresholds, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, type DeprecatedContextWindowModeId, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, LARGE_WINDOW_DEEP_MODE_THRESHOLD, CONTEXT_WINDOW_MODE_PINNED_META_KEY, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './types/context-window.js';
|
|
138
138
|
export { DEFAULT_SESSION_PRUNE_DAYS } from './types/default-config.js';
|
|
139
139
|
export type { FileEventRecord } from './types/file-event-record.js';
|
|
140
140
|
export * from './types/index.js';
|
package/dist/index.js
CHANGED
|
@@ -3042,6 +3042,8 @@ function expectDefined(value, label) {
|
|
|
3042
3042
|
|
|
3043
3043
|
// src/types/context-window.ts
|
|
3044
3044
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
3045
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
3046
|
+
var CONTEXT_WINDOW_MODE_PINNED_META_KEY = "contextWindowModePinned";
|
|
3045
3047
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
3046
3048
|
archival: "balanced"
|
|
3047
3049
|
});
|
|
@@ -3049,7 +3051,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
3049
3051
|
{
|
|
3050
3052
|
id: "balanced",
|
|
3051
3053
|
name: "Balanced",
|
|
3052
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
3054
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
3053
3055
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
3054
3056
|
aggressiveOn: "soft",
|
|
3055
3057
|
preserveK: 8,
|
|
@@ -3100,9 +3102,11 @@ function getContextWindowMode(id) {
|
|
|
3100
3102
|
function isContextWindowModeId(id) {
|
|
3101
3103
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
3102
3104
|
}
|
|
3103
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
3105
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
3104
3106
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
3105
|
-
const
|
|
3107
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
3108
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
3109
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
3106
3110
|
return {
|
|
3107
3111
|
...mode,
|
|
3108
3112
|
thresholds: {
|
|
@@ -11980,9 +11984,13 @@ ${memText}`);
|
|
|
11980
11984
|
}
|
|
11981
11985
|
}
|
|
11982
11986
|
if (skillBodyCache) {
|
|
11983
|
-
parts.push(
|
|
11987
|
+
parts.push(
|
|
11988
|
+
`# Active Skills
|
|
11989
|
+
|
|
11990
|
+
Skills are methods, not authority: they never widen your task's scope. When a skill suggests changes beyond the assigned task, note the observation in your report instead of acting on it.
|
|
11984
11991
|
|
|
11985
|
-
${skillBodyCache}`
|
|
11992
|
+
${skillBodyCache}`
|
|
11993
|
+
);
|
|
11986
11994
|
}
|
|
11987
11995
|
return { text: parts.join("\n\n"), skillBodyCache };
|
|
11988
11996
|
}
|
|
@@ -30142,6 +30150,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
30142
30150
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
30143
30151
|
${f.content}`).join("\n\n");
|
|
30144
30152
|
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
30153
|
+
This is an analysis role: do not edit the target files; report findings only.
|
|
30145
30154
|
|
|
30146
30155
|
Target files:
|
|
30147
30156
|
${fileContents}
|
|
@@ -30160,6 +30169,7 @@ Important: emit each finding as soon as you find it. Do not batch or wait until
|
|
|
30160
30169
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
30161
30170
|
${f.content}`).join("\n\n");
|
|
30162
30171
|
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
30172
|
+
This is an analysis role: do not edit the target files; emit the plan only.
|
|
30163
30173
|
|
|
30164
30174
|
Target files:
|
|
30165
30175
|
${fileContents}
|
|
@@ -30181,6 +30191,7 @@ Emit each plan immediately. Do not wait until planning is complete.`;
|
|
|
30181
30191
|
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
30182
30192
|
${f.content}`).join("\n\n");
|
|
30183
30193
|
return `You are Critic. Evaluate bug findings and refactor plans.
|
|
30194
|
+
This is an analysis role: do not edit the target files; emit evaluations only.
|
|
30184
30195
|
|
|
30185
30196
|
Target files:
|
|
30186
30197
|
${fileContents}
|
|
@@ -31216,6 +31227,82 @@ function makeLLMClassifier(complete2) {
|
|
|
31216
31227
|
// src/coordination/director-basic-tools.ts
|
|
31217
31228
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
31218
31229
|
init_error();
|
|
31230
|
+
|
|
31231
|
+
// src/coordination/task-boundary.ts
|
|
31232
|
+
var PLACEHOLDER_VALUES = /* @__PURE__ */ new Set([
|
|
31233
|
+
"n/a",
|
|
31234
|
+
"na",
|
|
31235
|
+
"none",
|
|
31236
|
+
"nothing",
|
|
31237
|
+
"tbd",
|
|
31238
|
+
"todo",
|
|
31239
|
+
"unknown",
|
|
31240
|
+
"unspecified",
|
|
31241
|
+
"-",
|
|
31242
|
+
"\u2014",
|
|
31243
|
+
".",
|
|
31244
|
+
"as above",
|
|
31245
|
+
"same as above",
|
|
31246
|
+
"see above",
|
|
31247
|
+
"see task",
|
|
31248
|
+
"same as task"
|
|
31249
|
+
]);
|
|
31250
|
+
var isPlaceholder = (value) => PLACEHOLDER_VALUES.has(value.trim().toLowerCase());
|
|
31251
|
+
var MIN_SCOPE_CHARS = 8;
|
|
31252
|
+
var MIN_NON_GOAL_CHARS = 3;
|
|
31253
|
+
function parseTaskBoundary(raw) {
|
|
31254
|
+
const scope = typeof raw.scope === "string" ? raw.scope.trim() : "";
|
|
31255
|
+
if (scope.length < MIN_SCOPE_CHARS) {
|
|
31256
|
+
return {
|
|
31257
|
+
ok: false,
|
|
31258
|
+
error: `\`scope\` is missing or too vague \u2014 state in one concrete sentence what work this task covers (files, components, or commands in-bounds).`,
|
|
31259
|
+
hint: 'Example \u2014 scope: "Audit packages/core/src/parser/*.ts for unhandled token errors and report findings."'
|
|
31260
|
+
};
|
|
31261
|
+
}
|
|
31262
|
+
if (!Array.isArray(raw.outOfScope) || raw.outOfScope.length === 0) {
|
|
31263
|
+
return {
|
|
31264
|
+
ok: false,
|
|
31265
|
+
error: "`outOfScope` must be an array with at least one explicit non-goal \u2014 things the worker must NOT do.",
|
|
31266
|
+
hint: 'Example \u2014 outOfScope: ["Do not modify files outside packages/core", "Do not fix the bugs you find, only report them"].'
|
|
31267
|
+
};
|
|
31268
|
+
}
|
|
31269
|
+
const concrete = raw.outOfScope.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length >= MIN_NON_GOAL_CHARS && !isPlaceholder(item));
|
|
31270
|
+
if (concrete.length === 0) {
|
|
31271
|
+
return {
|
|
31272
|
+
ok: false,
|
|
31273
|
+
error: 'Every `outOfScope` entry was a placeholder ("none", "n/a", \u2026). Name at least one concrete non-goal: files or areas not to touch, changes not to make, features not to add.',
|
|
31274
|
+
hint: 'There is always an edge worth stating \u2014 "read-only, no edits", "no dependency changes", "do not touch other packages". If truly nothing comes to mind, the task is not decomposed enough yet.'
|
|
31275
|
+
};
|
|
31276
|
+
}
|
|
31277
|
+
return { ok: true, boundary: { scope, outOfScope: concrete } };
|
|
31278
|
+
}
|
|
31279
|
+
function renderTaskBoundaryBlock(boundary) {
|
|
31280
|
+
return [
|
|
31281
|
+
"\u2500\u2500 TASK BOUNDARY (hard contract \u2014 these lines define your edges) \u2500\u2500",
|
|
31282
|
+
`Scope (what this task covers):
|
|
31283
|
+
${boundary.scope}`,
|
|
31284
|
+
`Out of scope (explicit non-goals \u2014 do NOT do any of these):
|
|
31285
|
+
${boundary.outOfScope.map((item) => `- ${item}`).join("\n")}`
|
|
31286
|
+
].join("\n");
|
|
31287
|
+
}
|
|
31288
|
+
function composeBoundedTaskDescription(objective, boundary) {
|
|
31289
|
+
return `${objective.trim()}
|
|
31290
|
+
|
|
31291
|
+
${renderTaskBoundaryBlock(boundary)}`;
|
|
31292
|
+
}
|
|
31293
|
+
var taskBoundarySchemaProperties = {
|
|
31294
|
+
scope: {
|
|
31295
|
+
type: "string",
|
|
31296
|
+
description: "REQUIRED. One concrete sentence stating what work this task covers \u2014 the in-bounds. The call is rejected without it."
|
|
31297
|
+
},
|
|
31298
|
+
outOfScope: {
|
|
31299
|
+
type: "array",
|
|
31300
|
+
items: { type: "string", minLength: 1 },
|
|
31301
|
+
description: 'REQUIRED. At least one explicit non-goal the worker must NOT do (files/areas not to touch, changes not to make, features not to add). Placeholders like "none" are rejected.'
|
|
31302
|
+
}
|
|
31303
|
+
};
|
|
31304
|
+
|
|
31305
|
+
// src/coordination/director-basic-tools.ts
|
|
31219
31306
|
function makeAssignTool(director) {
|
|
31220
31307
|
const inputSchema = {
|
|
31221
31308
|
type: "object",
|
|
@@ -31224,8 +31311,9 @@ function makeAssignTool(director) {
|
|
|
31224
31311
|
description: {
|
|
31225
31312
|
type: "string",
|
|
31226
31313
|
minLength: 1,
|
|
31227
|
-
description: "The
|
|
31314
|
+
description: "The objective in natural language \u2014 what you want this subagent to do. Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
31228
31315
|
},
|
|
31316
|
+
...taskBoundarySchemaProperties,
|
|
31229
31317
|
maxToolCalls: {
|
|
31230
31318
|
type: "number",
|
|
31231
31319
|
minimum: 1,
|
|
@@ -31233,20 +31321,28 @@ function makeAssignTool(director) {
|
|
|
31233
31321
|
},
|
|
31234
31322
|
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
31235
31323
|
},
|
|
31236
|
-
required: ["subagentId", "description"]
|
|
31324
|
+
required: ["subagentId", "description", "scope", "outOfScope"]
|
|
31237
31325
|
};
|
|
31238
31326
|
return {
|
|
31239
31327
|
name: "assign_task",
|
|
31240
|
-
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
31328
|
+
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Every assignment MUST carry an explicit boundary: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
31241
31329
|
permission: "auto",
|
|
31242
31330
|
mutating: false,
|
|
31243
31331
|
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
31244
31332
|
inputSchema,
|
|
31245
31333
|
async execute(input) {
|
|
31246
31334
|
const i = input;
|
|
31335
|
+
const boundary = parseTaskBoundary(i);
|
|
31336
|
+
if (!boundary.ok) {
|
|
31337
|
+
return {
|
|
31338
|
+
ok: false,
|
|
31339
|
+
error: `assign_task rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
31340
|
+
hint: boundary.hint
|
|
31341
|
+
};
|
|
31342
|
+
}
|
|
31247
31343
|
const task = {
|
|
31248
31344
|
id: randomUUID12(),
|
|
31249
|
-
description: i.description,
|
|
31345
|
+
description: composeBoundedTaskDescription(i.description, boundary.boundary),
|
|
31250
31346
|
subagentId: i.subagentId,
|
|
31251
31347
|
maxToolCalls: i.maxToolCalls,
|
|
31252
31348
|
timeoutMs: i.timeoutMs
|
|
@@ -39083,8 +39179,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
39083
39179
|
withNickname(subagent, subagentId) {
|
|
39084
39180
|
const role = subagent.role ?? "subagent";
|
|
39085
39181
|
const name = subagent.name?.trim() ?? "";
|
|
39086
|
-
const
|
|
39087
|
-
if (!
|
|
39182
|
+
const isPlaceholder2 = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
39183
|
+
if (!isPlaceholder2) return subagent;
|
|
39088
39184
|
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
39089
39185
|
this.usedNicknames.add(key);
|
|
39090
39186
|
this.subagentNicknames.set(subagentId, key);
|
|
@@ -43320,8 +43416,9 @@ function createDelegateTool(opts) {
|
|
|
43320
43416
|
properties: {
|
|
43321
43417
|
task: {
|
|
43322
43418
|
type: "string",
|
|
43323
|
-
description: "
|
|
43419
|
+
description: "The objective \u2014 what the subagent should do, natural language, complete sentence(s). Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
43324
43420
|
},
|
|
43421
|
+
...taskBoundarySchemaProperties,
|
|
43325
43422
|
role: {
|
|
43326
43423
|
type: "string",
|
|
43327
43424
|
description: rosterIds.length > 0 ? "Roster role id. Common: bug-hunter, security-scanner, refactor-planner, critic, audit-log, executor, shadow-agent, architect." : "No roster configured \u2014 pass `name` instead."
|
|
@@ -43379,12 +43476,12 @@ function createDelegateTool(opts) {
|
|
|
43379
43476
|
description: "Max fresh-worker continuations after budget exhaustion. Default 1. Each gets the prior partial report."
|
|
43380
43477
|
}
|
|
43381
43478
|
},
|
|
43382
|
-
required: ["task"]
|
|
43479
|
+
required: ["task", "scope", "outOfScope"]
|
|
43383
43480
|
};
|
|
43384
43481
|
return {
|
|
43385
43482
|
name: "delegate",
|
|
43386
43483
|
description: "Hand a piece of work to a subagent and block until it returns. This call is synchronous: the leader's iteration pauses for the full duration of the subagent's run. (Multiple `delegate` calls fired in the same assistant turn still parallelize through the provider's parallel-tool-call surface, but each one eats wall-clock time \u2014 so for fan-out you actually control, reach for the async path below.) Use `delegate` when your next step genuinely needs the subagent's verdict \u2014 a review, a fact-check, a sign-off. Has own context, own LLM call, auto-extending budget, and a partial-completion handoff path (maxHandoffs, default 1). Workers cannot recursively spawn.\n\n**Do NOT use `delegate` for long-running work.** While `delegate` is in flight, the leader is fully blocked \u2014 it cannot act on other tools, read mail, or react to the user. If the work might run for tens of minutes or hours (multi-file refactor, monorepo audit, long-running build/test, sweeping migration), the blocking call wastes the leader's time. Use the async tool family instead: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then `await_tasks` to retrieve results later. The leader keeps doing other work while the worker churns, and a worker that realizes its task will run long can mail the leader (type `steer` or `ask` via `mail_send`) saying *\"my task is going to run long, please spawn a subagent instead\"* so the leader re-dispatches asynchronously instead of waiting.\n\n**Do NOT use `delegate` for fan-out you control.** Multiple sequential `delegate` calls each block the leader, wasting wall-clock time. For independent investigations you want to run in parallel \u2014 security scan + bug hunt + perf review on the same PR \u2014 use the async tool family: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then the `await_tasks` tool with `{mode: 'any'}` to fold the first useful result into the next decision while the rest keep churning. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable.",
|
|
43387
|
-
usageHint: "Set `task` to a
|
|
43484
|
+
usageHint: "Set `task` to the objective, then make the edges explicit: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) are REQUIRED \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Pick `role` from roster or pass `name` for free-form. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable (minutes, not hours). For long-running work or fan-out you control, use `spawn_subagent` + `assign_task` + `await_tasks` instead. Raise `maxHandoffs` (default 1, cap 8) for multi-day or multi-refactor tasks; pass larger `timeoutMs`/`maxIterations`/`maxToolCalls` only when needed.",
|
|
43388
43485
|
permission: "auto",
|
|
43389
43486
|
mutating: false,
|
|
43390
43487
|
managesOwnTimeout: true,
|
|
@@ -43404,6 +43501,14 @@ function createDelegateTool(opts) {
|
|
|
43404
43501
|
error: "Delegation cancelled before spawn \u2014 the run was interrupted."
|
|
43405
43502
|
};
|
|
43406
43503
|
}
|
|
43504
|
+
const boundary = parseTaskBoundary(i);
|
|
43505
|
+
if (!boundary.ok) {
|
|
43506
|
+
return {
|
|
43507
|
+
ok: false,
|
|
43508
|
+
error: `delegate rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
43509
|
+
hint: boundary.hint
|
|
43510
|
+
};
|
|
43511
|
+
}
|
|
43407
43512
|
const target = i.role ?? i.name ?? "subagent";
|
|
43408
43513
|
const launchModePreface = [
|
|
43409
43514
|
"Launch-mode guidance (delegate): you were launched via the synchronous `delegate` tool, so the leader is blocked on this call for the full duration of your run.",
|
|
@@ -43474,7 +43579,8 @@ function createDelegateTool(opts) {
|
|
|
43474
43579
|
const dir = director;
|
|
43475
43580
|
const maxHandoffs = Math.min(8, Math.max(0, Math.floor(i.maxHandoffs ?? 1)));
|
|
43476
43581
|
const handoffs = [];
|
|
43477
|
-
|
|
43582
|
+
const baseBrief = composeBoundedTaskDescription(i.task, boundary.boundary);
|
|
43583
|
+
let delegatedTask = baseBrief;
|
|
43478
43584
|
let handoffCount = 0;
|
|
43479
43585
|
for (; ; ) {
|
|
43480
43586
|
const attemptConfig = (() => {
|
|
@@ -43614,7 +43720,7 @@ function createDelegateTool(opts) {
|
|
|
43614
43720
|
remainingWork: continuation.remainingWork
|
|
43615
43721
|
});
|
|
43616
43722
|
handoffCount += 1;
|
|
43617
|
-
delegatedTask = buildHandoffTask(
|
|
43723
|
+
delegatedTask = buildHandoffTask(baseBrief, continuation, handoffCount, maxHandoffs);
|
|
43618
43724
|
continue;
|
|
43619
43725
|
}
|
|
43620
43726
|
const incomplete = result.report?.completion === "partial";
|
|
@@ -58285,7 +58391,7 @@ function readContextWindowPolicy(ctx) {
|
|
|
58285
58391
|
function installSubagentAutoCompaction(pipelines, ctx, contextConfig, events) {
|
|
58286
58392
|
const maxContext = ctx.provider?.capabilities?.maxContext ?? 0;
|
|
58287
58393
|
if (!(maxContext > 0)) return void 0;
|
|
58288
|
-
const policy = resolveContextWindowPolicy(contextConfig ?? {});
|
|
58394
|
+
const policy = resolveContextWindowPolicy(contextConfig ?? {}, void 0, maxContext);
|
|
58289
58395
|
ctx.meta ??= {};
|
|
58290
58396
|
ctx.meta["contextWindowPolicy"] = policy;
|
|
58291
58397
|
const compactor = new HybridCompactor({
|
|
@@ -97079,6 +97185,7 @@ export {
|
|
|
97079
97185
|
COMPLETED_WORK_LEDGER_MARKER,
|
|
97080
97186
|
CONFIG_BEHAVIOR_DEFAULTS,
|
|
97081
97187
|
CONTEXT_WINDOW_MODES,
|
|
97188
|
+
CONTEXT_WINDOW_MODE_PINNED_META_KEY,
|
|
97082
97189
|
CORE_RECONSTRUCT_EVENTS,
|
|
97083
97190
|
COUNCIL_JUDGE_PROMPT_PATH,
|
|
97084
97191
|
COUNCIL_REFUSAL_OPTION_ID,
|
|
@@ -97270,6 +97377,7 @@ export {
|
|
|
97270
97377
|
KNOWN_TOKEN_GROUPS,
|
|
97271
97378
|
KNOWN_TOKEN_NAMES,
|
|
97272
97379
|
KnowledgeGraph,
|
|
97380
|
+
LARGE_WINDOW_DEEP_MODE_THRESHOLD,
|
|
97273
97381
|
LAYER_1_IDENTITY,
|
|
97274
97382
|
LEADER_MODEL_SET_TOOL_NAME,
|
|
97275
97383
|
LEARNED_HARD_LIMIT,
|
|
@@ -97546,6 +97654,7 @@ export {
|
|
|
97546
97654
|
compileUserRegex,
|
|
97547
97655
|
completeBrainLlm,
|
|
97548
97656
|
completePartialObject,
|
|
97657
|
+
composeBoundedTaskDescription,
|
|
97549
97658
|
composeDirectorPrompt,
|
|
97550
97659
|
composeSubagentPrompt,
|
|
97551
97660
|
computeMessageTokens,
|
|
@@ -97963,6 +98072,7 @@ export {
|
|
|
97963
98072
|
parseReviewSeverity,
|
|
97964
98073
|
parseSkillFrontmatter,
|
|
97965
98074
|
parseSkillRef,
|
|
98075
|
+
parseTaskBoundary,
|
|
97966
98076
|
peekQueuedMessages,
|
|
97967
98077
|
pendingBtwCount,
|
|
97968
98078
|
persistReviewReport,
|
|
@@ -98021,6 +98131,7 @@ export {
|
|
|
98021
98131
|
renderPrometheus,
|
|
98022
98132
|
renderPrompt,
|
|
98023
98133
|
renderSkillAugmentation,
|
|
98134
|
+
renderTaskBoundaryBlock,
|
|
98024
98135
|
repairConfigDefaults,
|
|
98025
98136
|
repairToolUseAdjacency,
|
|
98026
98137
|
repeatedReadPressure,
|
|
@@ -98185,6 +98296,7 @@ export {
|
|
|
98185
98296
|
syncReportCompletion,
|
|
98186
98297
|
syncReportReopen,
|
|
98187
98298
|
takeHeapSample,
|
|
98299
|
+
taskBoundarySchemaProperties,
|
|
98188
98300
|
terminalPolicyDecision,
|
|
98189
98301
|
tightenHqRedactionPolicy,
|
|
98190
98302
|
toAlertMessage,
|
|
@@ -2895,7 +2895,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
2895
2895
|
{
|
|
2896
2896
|
id: "balanced",
|
|
2897
2897
|
name: "Balanced",
|
|
2898
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
2898
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
2899
2899
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
2900
2900
|
aggressiveOn: "soft",
|
|
2901
2901
|
preserveK: 8,
|
package/dist/storage/index.js
CHANGED
|
@@ -4656,7 +4656,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
4656
4656
|
{
|
|
4657
4657
|
id: "balanced",
|
|
4658
4658
|
name: "Balanced",
|
|
4659
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
4659
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
4660
4660
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
4661
4661
|
aggressiveOn: "soft",
|
|
4662
4662
|
preserveK: 8,
|
|
@@ -153,7 +153,7 @@ export interface LaunchConfig {
|
|
|
153
153
|
*
|
|
154
154
|
* Stored so the menu can offer a one-line "Continue with last
|
|
155
155
|
* settings? [Y/n/q]" summary on the next boot instead of re-asking
|
|
156
|
-
* the same 1-of-
|
|
156
|
+
* the same 1-of-5 question. Distinct from `mode` (tui/repl) — that
|
|
157
157
|
* field is set by the inner pre-launch prompts that run AFTER the
|
|
158
158
|
* user has chosen "TUI/REPL" here.
|
|
159
159
|
*
|
|
@@ -171,7 +171,7 @@ export interface LaunchConfig {
|
|
|
171
171
|
*/
|
|
172
172
|
export interface LaunchMenuChoice {
|
|
173
173
|
/** Which top-level surface the user picked from the menu. */
|
|
174
|
-
mode: 'tui-repl' | 'webui' | 'simpleui' | 'hq';
|
|
174
|
+
mode: 'tui-repl' | 'webui' | 'simpleui' | 'hq' | 'desktop';
|
|
175
175
|
/** Port override the user typed (defaults to the surface's default). */
|
|
176
176
|
port?: number | undefined;
|
|
177
177
|
/** Host override the user typed (defaults to 127.0.0.1). */
|
|
@@ -45,6 +45,23 @@ export interface ContextWindowConfigLike {
|
|
|
45
45
|
targetLoad?: number | undefined;
|
|
46
46
|
}
|
|
47
47
|
export declare const DEFAULT_CONTEXT_WINDOW_MODE_ID: ContextWindowModeId;
|
|
48
|
+
/**
|
|
49
|
+
* Windows at or above this size default to the `deep` policy instead of
|
|
50
|
+
* `balanced`: 1M-class windows exist to be filled, and balanced's hard line
|
|
51
|
+
* (0.85) would compact at ~890K of a 1.05M window, stranding the tail. Deep
|
|
52
|
+
* holds compaction until 0.96 and keeps a wider verbatim tail. An explicit
|
|
53
|
+
* `frugal`/`deep` choice, custom modes, and per-field threshold overrides are
|
|
54
|
+
* always respected as-is; only the balanced default is swapped.
|
|
55
|
+
*/
|
|
56
|
+
export declare const LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1000000;
|
|
57
|
+
/**
|
|
58
|
+
* Meta key the mode-switch surfaces (`/context mode`, WebUI `context.mode.switch`,
|
|
59
|
+
* `/context thresholds`) set after the user deliberately picks a policy for the
|
|
60
|
+
* session. Window-change flows re-resolve the default policy against the new
|
|
61
|
+
* window (so a 1M↔200K model switch keeps the policy scaled to the window) but
|
|
62
|
+
* must leave a user-pinned choice alone.
|
|
63
|
+
*/
|
|
64
|
+
export declare const CONTEXT_WINDOW_MODE_PINNED_META_KEY = "contextWindowModePinned";
|
|
48
65
|
export declare const DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES: Readonly<Record<DeprecatedContextWindowModeId, ContextWindowModeId>>;
|
|
49
66
|
export declare const CONTEXT_WINDOW_MODES: readonly ContextWindowMode[];
|
|
50
67
|
export declare function listContextWindowModes(): ContextWindowMode[];
|
|
@@ -53,6 +70,6 @@ export declare function isDeprecatedContextWindowModeId(id: string): id is Depre
|
|
|
53
70
|
export declare function isContextWindowModeSelectionId(id: string): id is ContextWindowModeSelectionId;
|
|
54
71
|
export declare function getContextWindowMode(id: string | null | undefined): ContextWindowMode | null;
|
|
55
72
|
export declare function isContextWindowModeId(id: string): id is ContextWindowModeId;
|
|
56
|
-
export declare function resolveContextWindowPolicy(config?: ContextWindowConfigLike, overrideMode?: string | null | undefined): ContextWindowPolicy;
|
|
73
|
+
export declare function resolveContextWindowPolicy(config?: ContextWindowConfigLike, overrideMode?: string | null | undefined, maxContext?: number | undefined): ContextWindowPolicy;
|
|
57
74
|
export declare function formatContextWindowModeList(activeId?: string | null): string;
|
|
58
75
|
//# sourceMappingURL=context-window.d.ts.map
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type { AdaptiveConcurrencyConfig, AgentLearningConfig, AutonomyConfig, Br
|
|
|
7
7
|
export { DEFAULT_TUI_THINKING_WORD, FLEET_CHAT_VERBOSITY_VALUES, MAX_TUI_THINKING_WORD_LENGTH, normalizeTokenSavingTier, normalizeTuiThinkingWord, resolveFleetChatVerbosity, resolveTokenSavingTier, THEME_PRESET_IDS, } from './config.js';
|
|
8
8
|
export type { CompletedWorkEvidence, CompletedWorkSource, ContextEvidenceState, ContextFileEvidence, ContextIntentEvidence, ContextRepeatedReadEvidence, ToolEvidenceStatus, ToolOutputMetadata, } from './context-evidence.js';
|
|
9
9
|
export type { ContextSnapshot, ContextWindowAggressiveOn, ContextWindowConfigLike, ContextWindowMode, ContextWindowModeId, ContextWindowModeSelectionId, ContextWindowPolicy, ContextWindowThresholds, DeprecatedContextWindowModeId, } from './context-window.js';
|
|
10
|
-
export { CONTEXT_WINDOW_MODES, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './context-window.js';
|
|
10
|
+
export { CONTEXT_WINDOW_MODES, CONTEXT_WINDOW_MODE_PINNED_META_KEY, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, LARGE_WINDOW_DEEP_MODE_THRESHOLD, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './context-window.js';
|
|
11
11
|
export type { CouncilDistinctness, CouncilLLMCaller, CouncilModelTarget, CouncilOption, CouncilPersona, CouncilProfileConfig, CouncilQuestion, CouncilResolutionMethod, CouncilResult, CouncilSeatConfig, CouncilUsage, CouncilVoteResult, CouncilVoteStatus, ResolvedCouncilProfile, ResolvedCouncilSeat, } from './council.js';
|
|
12
12
|
export { DEFAULT_AUTONOMY_CONFIG, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONTEXT_CONFIG, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_TOOLS_CONFIG, } from './default-config.js';
|
|
13
13
|
export type { DesignKitEntry, DesignKitLoader, DesignKitManifest, DesignKitTokens, DesignStack, DesignStudioState, DesignTokenSet, TokenValueKind, } from './design-kit.js';
|
package/dist/types/index.js
CHANGED
|
@@ -111,6 +111,8 @@ function expectDefined(value, label) {
|
|
|
111
111
|
|
|
112
112
|
// src/types/context-window.ts
|
|
113
113
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
114
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
115
|
+
var CONTEXT_WINDOW_MODE_PINNED_META_KEY = "contextWindowModePinned";
|
|
114
116
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
115
117
|
archival: "balanced"
|
|
116
118
|
});
|
|
@@ -118,7 +120,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
118
120
|
{
|
|
119
121
|
id: "balanced",
|
|
120
122
|
name: "Balanced",
|
|
121
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
123
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
122
124
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
123
125
|
aggressiveOn: "soft",
|
|
124
126
|
preserveK: 8,
|
|
@@ -169,9 +171,11 @@ function getContextWindowMode(id) {
|
|
|
169
171
|
function isContextWindowModeId(id) {
|
|
170
172
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
171
173
|
}
|
|
172
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
174
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
173
175
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
174
|
-
const
|
|
176
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
177
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
178
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
175
179
|
return {
|
|
176
180
|
...mode,
|
|
177
181
|
thresholds: {
|
|
@@ -1313,6 +1317,7 @@ export {
|
|
|
1313
1317
|
BUILTIN_PROMPT_CATEGORIES,
|
|
1314
1318
|
CHAT_MARKER_SOURCES,
|
|
1315
1319
|
CONTEXT_WINDOW_MODES,
|
|
1320
|
+
CONTEXT_WINDOW_MODE_PINNED_META_KEY,
|
|
1316
1321
|
ConfigError,
|
|
1317
1322
|
DEFAULT_AUTONOMY_CONFIG,
|
|
1318
1323
|
DEFAULT_CIRCUIT_BREAKER_CONFIG,
|
|
@@ -1333,6 +1338,7 @@ export {
|
|
|
1333
1338
|
GOVERNED_TOOL_EXECUTOR_META_KEY,
|
|
1334
1339
|
KNOWN_TOKEN_GROUPS,
|
|
1335
1340
|
KNOWN_TOKEN_NAMES,
|
|
1341
|
+
LARGE_WINDOW_DEEP_MODE_THRESHOLD,
|
|
1336
1342
|
MALFORMED_ARG_MARKERS,
|
|
1337
1343
|
MAX_TUI_THINKING_WORD_LENGTH,
|
|
1338
1344
|
MEMORY_TYPE_LABELS,
|
|
@@ -48,7 +48,7 @@ For controlled fan-out, use `spawn_subagent` → `assign_task` →
|
|
|
48
48
|
|
|
49
49
|
## Dispatch contract
|
|
50
50
|
|
|
51
|
-
Every assigned task
|
|
51
|
+
Every assigned task must state:
|
|
52
52
|
|
|
53
53
|
- the objective and why it matters;
|
|
54
54
|
- exact scope and non-goals;
|
|
@@ -58,6 +58,14 @@ Every assigned task should state:
|
|
|
58
58
|
- the narrowest required verification;
|
|
59
59
|
- known dependencies, risks, and assumptions.
|
|
60
60
|
|
|
61
|
+
The assignment tools enforce the boundary: `delegate` and `assign_task` reject
|
|
62
|
+
any call without an explicit `scope` (what the work covers) and at least one
|
|
63
|
+
concrete `outOfScope` non-goal (what the worker must not do). Treat a
|
|
64
|
+
rejection as a design checkpoint, not paperwork — if you cannot name what is
|
|
65
|
+
out of scope, the task is not decomposed enough yet. The boundary is rendered
|
|
66
|
+
into the worker's brief as a hard contract and survives into handoff
|
|
67
|
+
continuations, so write it once, precisely.
|
|
68
|
+
|
|
61
69
|
Match role and model to the work: use economical workers for bounded discovery
|
|
62
70
|
and capable workers for ambiguous implementation or synthesis. Provider
|
|
63
71
|
diversity is useful for independent review, not an end in itself.
|
|
@@ -7,6 +7,10 @@ self-contained handoff; do not take over fleet orchestration.
|
|
|
7
7
|
- Treat the assigned objective, scope, write authority, non-goals, and
|
|
8
8
|
completion criteria as your boundary. Later role, task, and per-spawn
|
|
9
9
|
instructions may narrow this baseline.
|
|
10
|
+
- Your brief carries an explicit "TASK BOUNDARY" block (scope plus
|
|
11
|
+
out-of-scope non-goals). It is a hard contract, not a suggestion: stay
|
|
12
|
+
inside it even when an out-of-scope change looks quick or obviously right —
|
|
13
|
+
report it back instead of doing it.
|
|
10
14
|
- Inspect before editing. Resolve discoverable context yourself and use the
|
|
11
15
|
project's existing conventions, tests, and tooling.
|
|
12
16
|
- Make only task-relevant changes. Preserve unrelated work and avoid broad
|
|
@@ -192,6 +196,9 @@ Your final output is an integration artifact for the Director. It must state:
|
|
|
192
196
|
- files materially examined or changed;
|
|
193
197
|
- verification commands and observed results;
|
|
194
198
|
- atomic findings, decisions, or behavior changes;
|
|
199
|
+
- out-of-scope observations — issues you noticed but did not touch, each
|
|
200
|
+
with a file/symbol anchor — so the Director can surface them to the user
|
|
201
|
+
instead of a worker silently fixing them;
|
|
195
202
|
- uncertainty flags, blockers, and exact remaining work.
|
|
196
203
|
|
|
197
204
|
Never end with a bare “done.” Distinguish direct evidence from inference and
|
|
@@ -9,10 +9,10 @@ The user is an experienced developer; accelerate them and stay focused.
|
|
|
9
9
|
|
|
10
10
|
1. Understand the real request before acting.
|
|
11
11
|
2. Ask one concrete question only when ambiguity changes the approach.
|
|
12
|
-
3. For clear requests, proceed with the smallest safe change.
|
|
12
|
+
3. For clear requests, proceed with the smallest safe change; before non-trivial work, state in one short line what is in scope and what is not.
|
|
13
13
|
4. Read relevant files before editing them.
|
|
14
14
|
5. Prefer surgical edits over rewrites.
|
|
15
|
-
6. Do not change unrelated code.
|
|
15
|
+
6. Do not change unrelated code; if you notice an unrelated problem, report it in your summary instead of fixing it.
|
|
16
16
|
7. Match the file's existing conventions; add a dependency only when the task requires it.
|
|
17
17
|
8. The cost ladder — before writing new code, stop at the first rung that answers: can it be deleted instead; does it need to exist; does this repo already do it; does the language, runtime, or platform do it; does an installed dependency do it; is it one line? Only then write the minimum that works.
|
|
18
18
|
9. The ladder trims code you invented, never the user's request. Reuse claims need a named file, symbol, or package — not recollection. Do not narrate rung numbers.
|
|
@@ -106,13 +106,13 @@ Reasoning depth is a dial, not a constant. Match it to the blast radius of what
|
|
|
106
106
|
<!--ws:else-->
|
|
107
107
|
2. **Honor the live tool boundary.** If this request is read-only, report findings without proposing unavailable calls.
|
|
108
108
|
<!--ws:end-->
|
|
109
|
-
3. **Announce, then act.** Before a non-trivial change, one
|
|
109
|
+
3. **Announce the edges, then act.** Before a non-trivial change, one short statement of what you're about to do and what is explicitly out of scope for this task — not a wall of text. Afterwards, summarize the outcome, not the mechanics, and surface any out-of-scope issues you noticed but did not touch.
|
|
110
110
|
4. **Be honest about limits, precisely.** If you don't know, say so. Never fabricate file contents, command output, or test results. Never call work "production-ready" or "fully tested" — the user makes that call. State what you ran and what it returned; do not imply verification you did not perform.
|
|
111
111
|
5. **Separate verified from assumed.** Use plain markers in reports: *verified* (you ran it / read it), *assumed* (reasonable inference, unchecked), *unknown* (needs the user or a tool you lack). One glance should tell the user how much to trust each claim.
|
|
112
112
|
6. **Be concise and scannable.** No marketing language, no filler. If a one-liner answers, a one-liner is the answer. Code blocks for code, backticks for paths, bold for key terms; paragraphs max 3 sentences. (Active modes may override verbosity.)
|
|
113
113
|
7. **Match the user's language.** Reply in the language the user writes in; if they mix, follow the dominant one.
|
|
114
114
|
8. **Ask when blocked, proceed when not.** If ambiguity meaningfully changes the approach (unclear file, conflicting requirements), ask. Otherwise pick a reasonable default, state the assumption, and proceed.
|
|
115
|
-
9. **Stay focused, stay native.** Fix only what was asked — no refactoring or reformatting of neighboring code. Match the surrounding code's conventions (naming, imports, error handling) instead of imposing your own, and add a new dependency only when the task requires it and you say so. Comment only to explain *why*, not *what*. Don't lecture about engineering principles unless asked.
|
|
115
|
+
9. **Stay focused, stay native.** Fix only what was asked — no refactoring or reformatting of neighboring code. When you notice an unrelated problem while working (another bug five lines above the one you were asked to fix, a neighboring broken test, a suspicious call site), do not fix it — name it in your final summary as an observation and leave the decision to the user. Match the surrounding code's conventions (naming, imports, error handling) instead of imposing your own, and add a new dependency only when the task requires it and you say so. Comment only to explain *why*, not *what*. Don't lecture about engineering principles unless asked.
|
|
116
116
|
10. **The working tree is shared.** Never commit, push, amend, or discard changes unless the user asked for it. Treat destructive commands (recursive delete, hard reset, force push, history rewrites) as requiring an explicit request — never run them as convenience cleanup.
|
|
117
117
|
11. **Leave the knowledge behind, not just the diff.** A task that taught you something durable about this codebase isn't finished until that knowledge is in memory (see Memory management).
|
|
118
118
|
12. **Keep helper scripts temporary and contained.** This rule applies to every agent, regardless of role (leader, coordinator, or subagent). Create all ad hoc helper scripts and their temporary inputs/outputs only under `<project-root>/.temp_files/` — never in the repository root or source directories. Write each helper script so its paths, imports, and generated artifacts work from that location. Delete the helper script and any temporary artifacts it created as soon as they are no longer needed, and always before reporting the task complete. Only remove files created for the current task; never delete pre-existing or user-owned contents of `.temp_files/`. This rule does not apply to permanent project scripts explicitly requested by the user.
|
package/instructions/system.md
CHANGED
|
@@ -49,12 +49,12 @@ This parse is **internal reasoning**, not something you output. It keeps you anc
|
|
|
49
49
|
<!--ws:else-->
|
|
50
50
|
2. **Honor the live tool boundary.** If this request is read-only, report findings without proposing unavailable calls.
|
|
51
51
|
<!--ws:end-->
|
|
52
|
-
3. **Announce, then act.** Before a non-trivial change, one
|
|
52
|
+
3. **Announce the edges, then act.** Before a non-trivial change, one short statement of what you're about to do and what is explicitly out of scope for this task — not a wall of text. Afterwards, summarize the outcome, not the mechanics, and surface any out-of-scope issues you noticed but did not touch.
|
|
53
53
|
4. **Be honest about limits.** If you don't know, say so. Never fabricate file contents, command output, or test results. Never call work "production-ready" or "fully tested" — the user makes that call.
|
|
54
54
|
5. **Be concise and scannable.** No marketing language, no filler. If a one-liner answers, a one-liner is the answer. Code blocks for code, backticks for paths, bold for key terms; paragraphs max 3 sentences. (Active modes may override verbosity.)
|
|
55
55
|
6. **Match the user's language.** Reply in the language the user writes in; if they mix, follow the dominant one.
|
|
56
56
|
7. **Ask when blocked, proceed when not.** If ambiguity meaningfully changes the approach (unclear file, conflicting requirements), ask. Otherwise pick a reasonable default, state the assumption, and proceed.
|
|
57
|
-
8. **Stay focused, stay native.** Fix only what was asked — no refactoring or reformatting of neighboring code. Match the surrounding code's conventions (naming, imports, error handling) instead of imposing your own, and add a new dependency only when the task requires it and you say so. Comment only to explain *why*, not *what*. Don't lecture about engineering principles unless asked.
|
|
57
|
+
8. **Stay focused, stay native.** Fix only what was asked — no refactoring or reformatting of neighboring code. When you notice an unrelated problem while working (another bug five lines above the one you were asked to fix, a neighboring broken test, a suspicious call site), do not fix it — name it in your final summary as an observation and leave the decision to the user. Match the surrounding code's conventions (naming, imports, error handling) instead of imposing your own, and add a new dependency only when the task requires it and you say so. Comment only to explain *why*, not *what*. Don't lecture about engineering principles unless asked.
|
|
58
58
|
9. **The working tree is shared.** Never commit, push, amend, or discard changes unless the user asked for it. Treat destructive commands (recursive delete, hard reset, force push, history rewrites) as requiring an explicit request — never run them as convenience cleanup.
|
|
59
59
|
10. **Keep helper scripts temporary and contained.** This rule applies to every agent, regardless of role (leader, coordinator, or subagent). Create all ad hoc helper scripts and their temporary inputs/outputs only under `<project-root>/.temp_files/` — never in the repository root or source directories. Write each helper script so its paths, imports, and generated artifacts work from that location. Delete the helper script and any temporary artifacts it created as soon as they are no longer needed, and always before reporting the task complete. Only remove files created for the current task; never delete pre-existing or user-owned contents of `.temp_files/`. This rule does not apply to permanent project scripts explicitly requested by the user.
|
|
60
60
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/core",
|
|
3
|
-
"version": "0.308.
|
|
3
|
+
"version": "0.308.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack core: kernel, types, defaults, and shared utilities for the WrongStack CLI agent.",
|
|
6
6
|
"repository": {
|
|
@@ -94,7 +94,8 @@
|
|
|
94
94
|
},
|
|
95
95
|
"./storage": {
|
|
96
96
|
"types": "./dist/storage/index.d.ts",
|
|
97
|
-
"import": "./dist/storage/index.js"
|
|
97
|
+
"import": "./dist/storage/index.js",
|
|
98
|
+
"require": "./dist/storage/index.js"
|
|
98
99
|
},
|
|
99
100
|
"./session-catalog": {
|
|
100
101
|
"types": "./dist/session-catalog/index.d.ts",
|
|
@@ -181,8 +182,8 @@
|
|
|
181
182
|
"wrongstackApiVersion": "0.1.10",
|
|
182
183
|
"dependencies": {
|
|
183
184
|
"zod": "4.4.3",
|
|
184
|
-
"@wrongstack/
|
|
185
|
-
"@wrongstack/
|
|
185
|
+
"@wrongstack/persistence": "0.308.4",
|
|
186
|
+
"@wrongstack/kanban": "0.308.4"
|
|
186
187
|
},
|
|
187
188
|
"devDependencies": {
|
|
188
189
|
"@types/node": "^26.2.0",
|