Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions src/subagent/agent-fleet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* spawn_agent / wait_agents (CL-6942): the non-blocking half of fleet
* dispatch, split out of `task()`'s fused spawn+wait.
* spawn_agent / wait_agents: the non-blocking half of fleet dispatch,
* split out of `task()`'s fused spawn+wait.
*
* `task()` (task-tool.ts) remains the fused, blocking primitive and is
* unchanged. These two verbs let an orchestrator start several workers in
Expand Down Expand Up @@ -395,9 +395,9 @@ export function createSpawnAgentTool(deps: AgentFleetDeps): AgentTool {
description,
agentId: resolved.directorId,
brief,
// CL-6943: a spawn_agent worker's session survives a clean
// completion instead of being torn down — close_agent (or
// resume_agent, transitively) governs it from here on.
// A spawn_agent worker's session survives a clean completion instead
// of being torn down — close_agent (or resume_agent, transitively)
// governs it from here on.
retained: true,
});
deps.fleetRecords.register(session.id);
Expand Down Expand Up @@ -446,8 +446,8 @@ export function createSpawnAgentTool(deps: AgentFleetDeps): AgentTool {
...(resolved.capabilities !== undefined ? { capabilities: resolved.capabilities } : {}),
systemPromptRole: resolved.systemPromptRole,
directorId: resolved.directorId,
// CL-6943: keep the session open after a clean completion, and hand
// the store a bounded close for close_agent to call later.
// Keep the session open after a clean completion, and hand the
// store a bounded close for close_agent to call later.
persist: true,
onAgentReady: ({ close, interrupt, followup }) => {
deps.sessions.registerClose(session.id, close);
Expand All @@ -458,27 +458,27 @@ export function createSpawnAgentTool(deps: AgentFleetDeps): AgentTool {
};

// Fire and forget: this handler must return before the worker finishes.
// fleetRecords (never capped) is the durable source of truth wait_agents
// reads from; deps.sessions.complete/fail is still called for the TUI's
// benefit, but only after fleetRecords already has the result, and
// fleetRecords is written before it so the synchronous subscribe
// notification fired by complete()/fail() always sees the up-to-date
// fleetRecords is the durable source of truth wait_agents reads from
// (see the module doc for its own cap/eviction policy);
// deps.sessions.complete/fail is still called for the TUI's benefit,
// but only after fleetRecords already has the result, so the
// synchronous subscribe notification always sees the up-to-date
// record.
deps
.run(params)
.then((result) => {
if (childCtl.signal.aborted) return;
// CL-6997: interrupt_agent already flipped this session to
// "interrupted" synchronously (session-store.interruptOne) — do
// not let the settling promise's normal bookkeeping overwrite
// that with a "completed" status.
// interrupt_agent already flipped this session to "interrupted"
// synchronously (session-store.interruptOne) — do not let the
// settling promise's normal bookkeeping overwrite that with a
// "completed" status.
if (result.interrupted === true) return;
deps.fleetRecords.resolve(session.id, result.report);
// CL-7001: result.agentRetained is only true on run.ts's clean-
// completion path when persist actually skipped teardown — a
// deadline/cancel salvage resolves through the same promise but
// always disposed its agent first, so the store must not treat it
// as resumable just because retained:true was requested at spawn.
// result.agentRetained is only true on run.ts's clean-completion
// path when persist actually skipped teardown — a deadline/cancel
// salvage resolves through the same promise but always disposed
// its agent first, so the store must not treat it as resumable
// just because retained:true was requested at spawn.
deps.sessions.complete(session.id, result.report, {
agentRetained: result.agentRetained === true,
});
Expand Down
4 changes: 2 additions & 2 deletions src/subagent/authority.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ describe("assertTierMayMountFleetVerb", () => {
expect(() => assertTierMayMountFleetVerb("leaf", "task")).toThrow(FleetAuthorityError);
expect(() => assertTierMayMountFleetVerb("leaf", "search_agents")).toThrow(FleetAuthorityError);
expect(() => assertTierMayMountFleetVerb("leaf", "spawn_agent")).toThrow(FleetAuthorityError);
// CL-6943: the reusable-session verbs are gated the same way.
// The reusable-session verbs are gated the same way.
expect(() => assertTierMayMountFleetVerb("leaf", "close_agent")).toThrow(FleetAuthorityError);
expect(() => assertTierMayMountFleetVerb("leaf", "resume_agent")).toThrow(FleetAuthorityError);
// CL-6997: interrupt_agent / followup_task are gated the same way.
// Interrupt_agent / followup_task are gated the same way.
expect(() => assertTierMayMountFleetVerb("leaf", "interrupt_agent")).toThrow(
FleetAuthorityError,
);
Expand Down
7 changes: 3 additions & 4 deletions src/subagent/authority.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Fleet authority (CL-6941): the runtime boundary between the three tiers.
* Fleet authority: the runtime boundary between the three tiers.
*
* Tier enforcement lives here and at the tool-mount point in run.ts — never
* in a prompt. This module owns two checks:
Expand Down Expand Up @@ -93,9 +93,8 @@ function isDescendant(
* No verb in this codebase currently lets one live agent target another
* (`task` only spawns; it never addresses an existing session), so the
* subtree rule below is exercised only by authority.test.ts — it is not
* enforced at runtime yet. It exists now so CL-6942 (split spawn from wait)
* and CL-6944 (send_input steering) — the first two verbs that make one
* agent addressable by another — can call it from day one instead of
* enforced at runtime yet. It exists now so future verbs that make one
* agent addressable by another can call it from day one instead of
* inventing their own check. Until one of those wires a call site here, do
* not describe this rule as enforced; only assertTierMayMountFleetVerb is.
*
Expand Down
2 changes: 1 addition & 1 deletion src/subagent/brief-dispatch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Parent-side re-dispatch bookkeeping for task briefs (CL-4343 + CL-5203).
* Parent-side re-dispatch bookkeeping for task briefs.
*
* This module tracks how often the *parent* re-spawns the same brief so
* salvage outcomes can be classified per-fingerprint (successful completes
Expand Down
2 changes: 1 addition & 1 deletion src/subagent/dispose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function isSubAgentCancelError(err: unknown, signal?: AbortSignal): boole
export const SUBAGENT_SPAWN_DRAIN_MS = 2_000;

/**
* Bounded cleanup deadline for close_agent (CL-6943): a wedged descendant's
* Bounded cleanup deadline for close_agent: a wedged descendant's
* teardown is abandoned (not awaited further), not a reason to hang the
* caller.
*/
Expand Down
6 changes: 3 additions & 3 deletions src/subagent/fleet-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* cannot keep: a lane failed or went quiet, and the single moment the fleet runs
* dry. Per-lane "done — summary" walls are intentionally never printed — they
* restate the strip and the parent and turn the transcript into a second
* status log (CL-5846).
* status log.
*
* Pure and stateless per call — the caller keeps the returned watch and hands
* it back on the next observation. No painting, no store access.
Expand Down Expand Up @@ -175,7 +175,7 @@ export function observeFleet(

// A lane going quiet is no longer emitted to the transcript: the single
// agents-panel rollup row carries the quiet count instead, so a stalled
// fleet stops producing "went quiet" walls (CL-5846). stallReported is
// fleet stops producing "went quiet" walls. stallReported is
// still tracked internally so the strip does not flap.
}

Expand All @@ -184,7 +184,7 @@ export function observeFleet(

// Board owns live lanes. Parent prose owns success narratives. Transcript
// only: fail/stall while work is still running, or one dry-fleet tally.
// Never per-lane "done — summary" walls (CL-5846).
// Never per-lane "done — summary" walls.
if (wentDry) {
return {
watch,
Expand Down
4 changes: 2 additions & 2 deletions src/subagent/followup-live-agent.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* CL-6997 regression guard: lifecycle-tools.test.ts proves interrupt_agent /
* Regression guard: lifecycle-tools.test.ts proves interrupt_agent /
* followup_task behave correctly against *fake registered closures* at the
* tool/store layer — it never exercises run.ts's real wiring, where
* `followup` calls `agent!.send()` on the same live agent object created by
Expand Down Expand Up @@ -82,7 +82,7 @@ function createStubAgent() {
};
}

describe("interrupt_agent / followup_task reuse the same live agent (CL-6997)", () => {
describe("interrupt_agent / followup_task reuse the same live agent", () => {
test("followup after interrupt sends into the SAME agent instance — not a rebuilt one", async () => {
const cwd = await tmpCwd();
let constructions = 0;
Expand Down
4 changes: 2 additions & 2 deletions src/subagent/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ describe("sub-agent stop helpers", () => {
).toBeNull();
});

test("re-read pressure no longer stops a worker (CL-6936)", () => {
test("re-read pressure no longer stops a worker", () => {
let thrash = EMPTY_THRASH_STATE;
thrash = nextThrashState(thrash, [
{ type: "tool_call", name: "edit_file", arguments: { path: "a.ts" } },
Expand Down Expand Up @@ -608,7 +608,7 @@ describe("thrash edge cases", () => {
expect(stop(s)).toBeNull();
});

test("re-reading the same chunk repeatedly is not a stop (CL-6936)", () => {
test("re-reading the same chunk repeatedly is not a stop", () => {
let s = EMPTY_THRASH_STATE;
s = nextThrashState(s, [edit("big.ts")]);
for (let i = 0; i < 8; i++) {
Expand Down
2 changes: 1 addition & 1 deletion src/subagent/intervention-log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async function flush(): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, 10));
}

describe("intervention log (CL-6938)", () => {
describe("intervention log", () => {
test("records carry the shared context, the measurement, and the run state", async () => {
const dir = await mkdtemp(join(tmpdir(), "intervention-log-"));
const sink = createInterventionLog(
Expand Down
2 changes: 1 addition & 1 deletion src/subagent/intervention-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* There was no way to tell how often a stop or nudge trigger was wrong. Every
* threshold in the tree was set by judgment, and the tuning history is a
* record of that not working — a grok 6/10 pair reverted as miscalibrated,
* and a grok stall timeout reverted (CL-6938).
* and a grok stall timeout reverted.
*
* The point of this file is that a threshold change can cite data. Each record
* carries the trigger's *measured value beside its threshold*, the identity of
Expand Down
4 changes: 2 additions & 2 deletions src/subagent/lifecycle-tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* close_agent / resume_agent (CL-6943): the session-lifecycle half of
* reusable worker sessions. spawn_agent/wait_agents (CL-6942) start and
* close_agent / resume_agent: the session-lifecycle half of
* reusable worker sessions. spawn_agent/wait_agents start and
* collect workers; these two verbs let an orchestrator tear one down on
* purpose (close_agent) or bring a retained one back for further input
* (resume_agent), instead of every session dying the instant its turn ends.
Expand Down
8 changes: 4 additions & 4 deletions src/subagent/nudge-director.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ export class SubAgentDirector extends DefaultDirector {
private lastAssistantText = "";
// Every stop and nudge is recorded with its measured value beside its
// threshold, so a later threshold change can cite data instead of judgment
// (CL-6938). Defaults to a no-op: logging is diagnostic, never required.
//. Defaults to a no-op: logging is diagnostic, never required.
private interventions: InterventionSink = NOOP_INTERVENTION_SINK;
// Structured stop-reason side channel (CL-6946 part 2): fired synchronously
// whenever this director force-stops, so the caller learns the reason as a
// typed value rather than re-parsing the forcedStopReport prose it returns.
// Structured stop-reason side channel: fired synchronously whenever this
// director force-stops, so the caller learns the reason as a typed value
// rather than re-parsing the forcedStopReport prose it returns.
private onForcedStop: (reason: ForcedStopReason) => void = () => {};

/** Route this leaf's stop/nudge decisions to an intervention log. */
Expand Down
2 changes: 1 addition & 1 deletion src/subagent/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface DispatchBrief {
successCriteria?: readonly string[];
doNot?: readonly string[];
reportFocus?: string;
/** Turn token (CL-6946) a leaf must echo back to `submit_result`. Leaf-tier dispatches only. */
/** Turn token a leaf must echo back to `submit_result`. Leaf-tier dispatches only. */
turnToken?: string;
}

Expand Down
10 changes: 5 additions & 5 deletions src/subagent/run-authority.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* Gate-level proof for CL-6941: authority.test.ts proves the assert
* functions throw when called directly, which is necessary but not
* sufficient — it does not prove runSubAgent itself cannot be talked into
* mounting a fleet verb for a caller whose tier cannot be established. These
* tests drive runSubAgent (the real mount point) end to end.
* authority.test.ts proves the assert functions throw when called directly,
* which is necessary but not sufficient — it does not prove runSubAgent
* itself cannot be talked into mounting a fleet verb for a caller whose tier
* cannot be established. These tests drive runSubAgent (the real mount
* point) end to end.
*/

import { describe, expect, test } from "bun:test";
Expand Down
Loading
Loading