@wrongstack/core 0.308.7 → 0.309.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/coordination/director/director-toolset.d.ts +2 -2
- package/dist/coordination/director-mutation-test-tool.d.ts +29 -0
- package/dist/coordination/director-tools.d.ts +2 -0
- package/dist/coordination/director.d.ts +9 -0
- package/dist/coordination/fleet.d.ts +12 -0
- package/dist/coordination/index.d.ts +1 -1
- package/dist/coordination/index.js +705 -48
- package/dist/coordination/mail-tools.d.ts +10 -6
- package/dist/coordination/mailbox-codecs.d.ts +31 -0
- package/dist/coordination/multi-agent-coordinator.d.ts +14 -0
- package/dist/coordination/multi-agent-timeout.d.ts +11 -1
- package/dist/coordination/mutation-engine.d.ts +74 -0
- package/dist/coordination/subagent-budget.d.ts +54 -0
- package/dist/coordination/subagent-finish.d.ts +78 -0
- package/dist/core/index.js +19 -4
- package/dist/defaults/index.js +699 -51
- package/dist/execution/index.js +238 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +862 -178
- package/dist/kernel/events/agent-events.d.ts +31 -2
- package/dist/models/index.js +11 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +14 -0
- package/dist/types/multi-agent.d.ts +15 -0
- package/dist/types/provider.d.ts +29 -1
- package/instructions/agents/chaos-monkey.md +57 -0
- package/package.json +3 -3
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { EventBus } from '../kernel/events.js';
|
|
22
22
|
import type { Context } from '../core/context.js';
|
|
23
|
-
import type {
|
|
23
|
+
import type { MailboxMessageType } from './mailbox-types.js';
|
|
24
24
|
import { type MailboxResolver } from './mailbox-tool.js';
|
|
25
25
|
export interface MailToolsOptions {
|
|
26
26
|
/** How to obtain a Mailbox given the execution Context (tests). */
|
|
@@ -75,12 +75,16 @@ export declare function makeMailSendTool(opts?: MailToolsOptions): {
|
|
|
75
75
|
required: string[];
|
|
76
76
|
};
|
|
77
77
|
execute(input: unknown, ctx: Context): Promise<{
|
|
78
|
-
messageId?: never;
|
|
79
|
-
to?: never;
|
|
80
|
-
summary?: never;
|
|
81
78
|
ok: boolean;
|
|
82
79
|
error: string;
|
|
83
|
-
|
|
80
|
+
} | {
|
|
81
|
+
error?: never;
|
|
82
|
+
ok: boolean;
|
|
83
|
+
messageId: string;
|
|
84
|
+
from: string;
|
|
85
|
+
to: string;
|
|
86
|
+
strippedFields: string[];
|
|
87
|
+
summary: string;
|
|
84
88
|
} | {
|
|
85
89
|
error?: never;
|
|
86
90
|
ok: boolean;
|
|
@@ -128,7 +132,7 @@ export declare function makeMailInboxTool(opts?: MailToolsOptions): {
|
|
|
128
132
|
from: string;
|
|
129
133
|
to: string;
|
|
130
134
|
type: MailboxMessageType;
|
|
131
|
-
audience: MailboxAudience;
|
|
135
|
+
audience: import("./mailbox-message-types.js").MailboxAudience;
|
|
132
136
|
subject: string;
|
|
133
137
|
body: string;
|
|
134
138
|
timestamp: string;
|
|
@@ -45,6 +45,37 @@ export declare class MailboxValidationError extends Error {
|
|
|
45
45
|
readonly field: string;
|
|
46
46
|
constructor(code: string, field: string, message: string);
|
|
47
47
|
}
|
|
48
|
+
/** Fields allowed in a send mutation payload from untrusted callers. */
|
|
49
|
+
export declare const SEND_ALLOWED_FIELDS: ReadonlySet<string>;
|
|
50
|
+
/** Result of {@link filterMailboxSendPayload}. */
|
|
51
|
+
export interface FilteredSendPayload {
|
|
52
|
+
/** Copy of the input containing only allow-listed and trust-relevant keys. */
|
|
53
|
+
payload: Record<string, unknown>;
|
|
54
|
+
/** Keys that were removed, in input order. Empty when nothing was stripped. */
|
|
55
|
+
stripped: string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Strip fields that do not belong in a send payload before it reaches the
|
|
59
|
+
* boundary codec or the mailbox store.
|
|
60
|
+
*
|
|
61
|
+
* Senders (hosts, adapters, models) attach fields the mailbox never asked
|
|
62
|
+
* for — debug knobs, client metadata, accidental whole-context dumps. Two
|
|
63
|
+
* failure modes follow without a filter: the strict codec rejects the whole
|
|
64
|
+
* send because of one irrelevant key, or a lenient surface persists the
|
|
65
|
+
* clutter into every recipient's inbox. This function prevents both:
|
|
66
|
+
* irrelevant keys are removed from the payload and reported in `stripped`.
|
|
67
|
+
*
|
|
68
|
+
* It is pure (never mutates the input) and keyed off the same
|
|
69
|
+
* {@link SEND_ALLOWED_FIELDS} set that governs `parseMailboxSendInput`, so
|
|
70
|
+
* the filter and the validator cannot drift apart.
|
|
71
|
+
*
|
|
72
|
+
* Trust-relevant fields (`from`, `sessionAffinity`, see
|
|
73
|
+
* {@link SEND_FORBIDDEN_FIELDS}) are deliberately PASSED THROUGH, never
|
|
74
|
+
* stripped: the codec must reject them loudly as unknown fields. Dropping
|
|
75
|
+
* them here would convert a forgery attempt into a successful,
|
|
76
|
+
* differently-scoped send.
|
|
77
|
+
*/
|
|
78
|
+
export declare function filterMailboxSendPayload(input: Record<string, unknown>): FilteredSendPayload;
|
|
48
79
|
/**
|
|
49
80
|
* Result of successful send-input parsing.
|
|
50
81
|
* `type` is the resolved type after default selection and validation.
|
|
@@ -150,6 +150,20 @@ export declare class DefaultMultiAgentCoordinator extends EventEmitter implement
|
|
|
150
150
|
* calls this itself.
|
|
151
151
|
*/
|
|
152
152
|
completeTask(result: TaskResult): void;
|
|
153
|
+
/**
|
|
154
|
+
* Ask every RUNNING subagent that opted into `gracefulFinish` to finish its
|
|
155
|
+
* task in its own turn (see coordination/subagent-finish.ts). This is the
|
|
156
|
+
* leader-side entry point for "the leader agent has finished": it delivers
|
|
157
|
+
* an in-band notification between tool batches — never an interrupt, never
|
|
158
|
+
* an abort. Each notified subagent keeps its existing time budget and
|
|
159
|
+
* accelerates; the watchdog still bounds the maximum lifetime.
|
|
160
|
+
*
|
|
161
|
+
* Subagents without the policy opted in are deliberately untouched — their
|
|
162
|
+
* lifecycle remains the legacy watchdog contract.
|
|
163
|
+
*
|
|
164
|
+
* Returns the number of subagents actually notified.
|
|
165
|
+
*/
|
|
166
|
+
requestFinish(reason: string): number;
|
|
153
167
|
private tryDispatchNext;
|
|
154
168
|
private canDispatch;
|
|
155
169
|
private takeNextDispatchableTask;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SubagentRunContext, SubagentRunner, TaskSpec } from '../types/multi-agent.js';
|
|
2
2
|
import { type SubagentBudget } from './subagent-budget.js';
|
|
3
|
+
import type { GracefulFinish } from './subagent-finish.js';
|
|
3
4
|
export interface ExecuteSubagentWithTimeoutOptions {
|
|
4
5
|
runner: SubagentRunner;
|
|
5
6
|
task: TaskSpec;
|
|
@@ -8,6 +9,15 @@ export interface ExecuteSubagentWithTimeoutOptions {
|
|
|
8
9
|
preemptFraction?: number | undefined;
|
|
9
10
|
abortSubagent: (subagentId: string) => void;
|
|
10
11
|
currentSessionId: () => string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Model-driven completion policy resolved from the subagent config. When
|
|
14
|
+
* set, crossing the wall-clock deadline does NOT abort the runner: the
|
|
15
|
+
* budget emits `subagent.finish_requested` in-band (folded into the
|
|
16
|
+
* conversation between tool batches) and extends its own ceiling by the
|
|
17
|
+
* grace window. The terminal stop applies only if that window also
|
|
18
|
+
* elapses — the subagent's bounded maximum lifetime.
|
|
19
|
+
*/
|
|
20
|
+
gracefulFinish?: GracefulFinish | undefined;
|
|
11
21
|
}
|
|
12
|
-
export declare function executeSubagentWithTimeout({ runner, task, ctx, budget, preemptFraction, abortSubagent, currentSessionId, }: ExecuteSubagentWithTimeoutOptions): Promise<import("../types/multi-agent.js").SubagentRunOutcome>;
|
|
22
|
+
export declare function executeSubagentWithTimeout({ runner, task, ctx, budget, preemptFraction, abortSubagent, currentSessionId, gracefulFinish, }: ExecuteSubagentWithTimeoutOptions): Promise<import("../types/multi-agent.js").SubagentRunOutcome>;
|
|
13
23
|
//# sourceMappingURL=multi-agent-timeout.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic mutation-testing engine ("Kaos Maymunu" / Chaos Monkey).
|
|
3
|
+
*
|
|
4
|
+
* Plans and applies classical boundary-condition mutations to TypeScript
|
|
5
|
+
* source — `>` ↔ `>=`, `+` ↔ `-`, boolean negation, `return x` → `return
|
|
6
|
+
* null`. The engine is deliberately regex/token based, NOT AST based: it has
|
|
7
|
+
* zero dependencies, runs synchronously, and every mutation site is
|
|
8
|
+
* re-derivable from (file, mutation id) alone. Callers (the `mutation_test`
|
|
9
|
+
* director tool) compute the plan, hand it to a chaos-monkey subagent that
|
|
10
|
+
* applies/runs/restores in an isolated worktree, and then compare per-mutant
|
|
11
|
+
* test outcomes against this plan.
|
|
12
|
+
*
|
|
13
|
+
* Safety properties the engine guarantees:
|
|
14
|
+
* - `applyMutation` is a pure string transform: given the same source and
|
|
15
|
+
* the same mutation id it always produces the same output.
|
|
16
|
+
* - Mutations are single-site: exactly one token occurrence changes.
|
|
17
|
+
* - Ids are stable across runs (position-anchored, not hash-of-content), so
|
|
18
|
+
* a worktree-chaos agent and the director tool agree on what each id
|
|
19
|
+
* means without exchanging anything but the id list.
|
|
20
|
+
*
|
|
21
|
+
* @module coordination/mutation-engine
|
|
22
|
+
*/
|
|
23
|
+
/** Mutation families this engine can plan. */
|
|
24
|
+
export type MutationKind = 'relax-boundary' | 'tighten-boundary' | 'arith-plus-to-minus' | 'arith-minus-to-plus' | 'negate-boolean' | 'return-null';
|
|
25
|
+
export interface MutationPlanItem {
|
|
26
|
+
/** Stable id: `<kind>#<line1based>#<col1based>`. */
|
|
27
|
+
id: string;
|
|
28
|
+
kind: MutationKind;
|
|
29
|
+
/** Project-relative file path, exactly as passed to planMutations. */
|
|
30
|
+
file: string;
|
|
31
|
+
/** 1-based line. */
|
|
32
|
+
line: number;
|
|
33
|
+
/** 1-based column of the mutated token start. */
|
|
34
|
+
column: number;
|
|
35
|
+
/** Original source text at the site. */
|
|
36
|
+
original: string;
|
|
37
|
+
/** Replacement text. */
|
|
38
|
+
replacement: string;
|
|
39
|
+
}
|
|
40
|
+
export interface PlanMutationsOptions {
|
|
41
|
+
/** Hard cap on planned mutants per file. Default 25. */
|
|
42
|
+
maxPerFile?: number | undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Plan mutations for one file's source text.
|
|
46
|
+
*
|
|
47
|
+
* The scan is line-by-line with the file's own line splits preserved so ids
|
|
48
|
+
* stay (line, column) anchored. Mutations inside comments and string
|
|
49
|
+
* literals are filtered out by `isMasked` below.
|
|
50
|
+
*/
|
|
51
|
+
export declare function planMutations(file: string, source: string, opts?: PlanMutationsOptions): MutationPlanItem[];
|
|
52
|
+
/**
|
|
53
|
+
* Apply one planned mutation to source. Pure: same input → same output.
|
|
54
|
+
* Returns the original source when the site no longer matches (the file has
|
|
55
|
+
* drifted since planning) so callers can treat that as a skipped mutant.
|
|
56
|
+
*/
|
|
57
|
+
export declare function applyMutation(source: string, mutation: Pick<MutationPlanItem, 'kind' | 'line' | 'column' | 'original' | 'replacement'>): string;
|
|
58
|
+
/**
|
|
59
|
+
* Parse a structured mutation report emitted by the chaos-monkey subagent
|
|
60
|
+
* (either via `submit_result` or its final text). Tolerant of surrounding
|
|
61
|
+
* prose: the first JSON object containing a `mutants` array wins.
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseMutationReport(text: string): {
|
|
64
|
+
mutants: Array<{
|
|
65
|
+
id: string;
|
|
66
|
+
file: string;
|
|
67
|
+
line: number;
|
|
68
|
+
kind: string;
|
|
69
|
+
status: 'killed' | 'survived' | 'skipped';
|
|
70
|
+
evidence?: string | undefined;
|
|
71
|
+
}>;
|
|
72
|
+
summary?: string | undefined;
|
|
73
|
+
} | undefined;
|
|
74
|
+
//# sourceMappingURL=mutation-engine.d.ts.map
|
|
@@ -63,6 +63,19 @@ export type BudgetNegotiationMode = 'auto' | 'sync';
|
|
|
63
63
|
export type BudgetSessionIdSource = string | (() => string | undefined);
|
|
64
64
|
interface SubagentBudgetOptions {
|
|
65
65
|
sessionId?: BudgetSessionIdSource | undefined;
|
|
66
|
+
/** Owning subagent id — used to address the graceful-finish event. */
|
|
67
|
+
subagentId?: string | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Wall-clock enforcement is owned EXCLUSIVELY by the coordinator watchdog
|
|
70
|
+
* (`executeSubagentWithTimeout`). Set for `gracefulFinish` runs: their
|
|
71
|
+
* notify-then-bound lifecycle must not be raced by `checkTimeout()` calls
|
|
72
|
+
* from `tool.progress` heartbeats, which can fire in the window between a
|
|
73
|
+
* deadline crossing and the watchdog's own tick — starting legacy
|
|
74
|
+
* negotiation that either aborts before `subagent.finish_requested` is
|
|
75
|
+
* delivered or grants an extension past the grace deadline (violating the
|
|
76
|
+
* bounded maximum lifetime). Idle-timeout checks still run.
|
|
77
|
+
*/
|
|
78
|
+
wallClockWatchdogOwned?: boolean | undefined;
|
|
66
79
|
}
|
|
67
80
|
export interface BudgetUsage {
|
|
68
81
|
iterations: number;
|
|
@@ -146,6 +159,43 @@ export declare class SubagentBudget {
|
|
|
146
159
|
* This is the single write path for limit mutations so that future
|
|
147
160
|
* validation or side-effects live in one place (M1). */
|
|
148
161
|
patchLimits(ext: Partial<BudgetLimits>): void;
|
|
162
|
+
/**
|
|
163
|
+
* Graceful-finish state (see coordination/subagent-finish.ts).
|
|
164
|
+
* `_finishNotified` guards the single in-band emission; `_grace` records a
|
|
165
|
+
* granted working-time extension past the original wall-clock deadline.
|
|
166
|
+
* They are separate because the two callers want different semantics:
|
|
167
|
+
* the watchdog grants grace at the deadline crossing (notify + extend),
|
|
168
|
+
* while an explicit leader-finished request only notifies — a subagent
|
|
169
|
+
* well inside its budget keeps its full legitimate working time and simply
|
|
170
|
+
* accelerates.
|
|
171
|
+
*/
|
|
172
|
+
private _finishNotified;
|
|
173
|
+
private _grace;
|
|
174
|
+
/** True once the in-band finish notification has been emitted. */
|
|
175
|
+
get finishNotified(): boolean;
|
|
176
|
+
/** True once a grace window has been granted past the original deadline. */
|
|
177
|
+
get graceGranted(): boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Notify the subagent in-band to finish its task in its own turn:
|
|
180
|
+
* `subagent.finish_requested` is emitted on the wired EventBus and the
|
|
181
|
+
* agent loop folds the notice into the conversation between tool batches.
|
|
182
|
+
* Nothing aborts — this is a notification, never an interrupt.
|
|
183
|
+
*
|
|
184
|
+
* `opts.graceMs` additionally extends the wall-clock ceiling by that window
|
|
185
|
+
* (used by the watchdog at a deadline crossing, so the model gets working
|
|
186
|
+
* time instead of a kill). Omit it to notify without touching the budget —
|
|
187
|
+
* the subagent keeps its existing time budget and just accelerates.
|
|
188
|
+
*
|
|
189
|
+
* Returns `true` when this call did something (emitted the notification
|
|
190
|
+
* and/or granted grace); `false` when there was nothing to do (already
|
|
191
|
+
* notified, grace already granted, no EventBus wired, budget not started).
|
|
192
|
+
*/
|
|
193
|
+
notifyFinish(reason: string, opts?: {
|
|
194
|
+
graceMs?: number | undefined;
|
|
195
|
+
}, now?: () => number): boolean;
|
|
196
|
+
/** Epoch ms by which the subagent should have produced its final output,
|
|
197
|
+
* once a grace window was granted. Undefined before that. */
|
|
198
|
+
get finishDeadlineMs(): number | undefined;
|
|
149
199
|
private iterations;
|
|
150
200
|
private toolCalls;
|
|
151
201
|
private tokenInput;
|
|
@@ -161,6 +211,10 @@ export declare class SubagentBudget {
|
|
|
161
211
|
private lastActivityTime;
|
|
162
212
|
private _onThreshold;
|
|
163
213
|
private readonly _sessionId;
|
|
214
|
+
/** Owning subagent id — used to address the graceful-finish event. */
|
|
215
|
+
private readonly _subagentId;
|
|
216
|
+
/** True when only the coordinator watchdog may enforce wall-clock limits. */
|
|
217
|
+
private readonly _wallClockWatchdogOwned;
|
|
164
218
|
/**
|
|
165
219
|
* Hard cap on how long `_negotiateExtension` waits for the coordinator to
|
|
166
220
|
* respond before defaulting to 'stop'. Without this fallback an absent
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* subagent-finish — model-driven completion for background subagents.
|
|
3
|
+
*
|
|
4
|
+
* Problem this module solves: a subagent that outlives its leader (the
|
|
5
|
+
* post-session Chimera reviewer is the canonical case) previously had only
|
|
6
|
+
* two exits — finish on its own, or be killed by the wall-clock watchdog
|
|
7
|
+
* (`executeSubagentWithTimeout` aborts the runner at the deadline). The kill
|
|
8
|
+
* is an external interrupt: it discards whatever the model was mid-way
|
|
9
|
+
* through producing and violates the "agent completes its own turn" contract.
|
|
10
|
+
*
|
|
11
|
+
* The graceful-finish path replaces the kill with a notification:
|
|
12
|
+
*
|
|
13
|
+
* 1. The subagent's config opts in via `gracefulFinish`.
|
|
14
|
+
* 2. When the finish condition fires — the wall-clock deadline is crossed,
|
|
15
|
+
* or the leader explicitly calls `Director.requestFinish()` — the budget
|
|
16
|
+
* records a single finish deadline and emits `subagent.finish_requested`
|
|
17
|
+
* on the subagent's EventBus, carrying the ready-to-read notice text.
|
|
18
|
+
* 3. The agent loop folds that notice into the conversation as a `/btw`
|
|
19
|
+
* note at the TOP of its next iteration — between tool batches,
|
|
20
|
+
* in-band, no abort, no restart.
|
|
21
|
+
* 4. The model reads the notice and completes its task in its own turn.
|
|
22
|
+
* 5. If the grace window also elapses, the watchdog applies the existing
|
|
23
|
+
* terminal stop. That is the documented maximum lifetime — the bound
|
|
24
|
+
* that keeps a subagent from living forever, reached only after the
|
|
25
|
+
* model was given legitimate working time to finish.
|
|
26
|
+
*
|
|
27
|
+
* @module subagent-finish
|
|
28
|
+
*/
|
|
29
|
+
/** Structural form of `SubagentConfig.gracefulFinish` (avoids a layer cycle). */
|
|
30
|
+
export type GracefulFinishConfig = boolean | {
|
|
31
|
+
graceMs?: number | undefined;
|
|
32
|
+
} | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* EventBus name for the in-band finish request. Emitted on the subagent's own
|
|
35
|
+
* EventBus (the bus the runner wired into `budget._events`), so delivery is
|
|
36
|
+
* process-local and lands at the loop's next iteration boundary.
|
|
37
|
+
*/
|
|
38
|
+
export declare const SUBAGENT_FINISH_REQUESTED_EVENT = "subagent.finish_requested";
|
|
39
|
+
/**
|
|
40
|
+
* Default grace window granted after the finish request fires. Long enough
|
|
41
|
+
* for a model to run one or two more tool calls and write a complete final
|
|
42
|
+
* report; short enough that a session shutdown cannot stall indefinitely.
|
|
43
|
+
*/
|
|
44
|
+
export declare const DEFAULT_SUBAGENT_FINISH_GRACE_MS = 120000;
|
|
45
|
+
/** Resolved graceful-finish policy for one subagent. */
|
|
46
|
+
export interface GracefulFinish {
|
|
47
|
+
/** Milliseconds of legitimate working time granted after notification. */
|
|
48
|
+
graceMs: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the graceful-finish policy from a `SubagentConfig`.
|
|
52
|
+
*
|
|
53
|
+
* `undefined` (the default for every existing spawn) keeps the legacy
|
|
54
|
+
* behavior byte-for-byte: the watchdog preempts/negotiates/aborts exactly as
|
|
55
|
+
* it did before. Only spawns that explicitly opt in get the notify-then-bound
|
|
56
|
+
* lifecycle.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveGracefulFinish(config: {
|
|
59
|
+
gracefulFinish?: GracefulFinishConfig | undefined;
|
|
60
|
+
}): GracefulFinish | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* The in-band notice the subagent reads between tool calls. Delivered as a
|
|
63
|
+
* `/btw` note, so it arrives at the next iteration boundary — never mid-tool,
|
|
64
|
+
* never as an abort. The wording is deliberately imperative and
|
|
65
|
+
* self-contained: the model must be able to act on it without any other
|
|
66
|
+
* context about why the leader finished. Carried verbatim on the event
|
|
67
|
+
* payload so the core agent loop needs no coordination-layer import to fold
|
|
68
|
+
* it into the conversation.
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildSubagentFinishNotice(input: {
|
|
71
|
+
/** Why the finish was requested (e.g. "leader session ended"). */
|
|
72
|
+
reason: string;
|
|
73
|
+
/** Epoch milliseconds by which the final output should be complete. */
|
|
74
|
+
deadlineMs: number;
|
|
75
|
+
/** Granted working-time window in milliseconds. */
|
|
76
|
+
graceMs: number;
|
|
77
|
+
}): string;
|
|
78
|
+
//# sourceMappingURL=subagent-finish.d.ts.map
|
package/dist/core/index.js
CHANGED
|
@@ -3649,9 +3649,17 @@ var TASK_SNIPPET_CHARS = 60;
|
|
|
3649
3649
|
function fleetPulseSignature(statuses) {
|
|
3650
3650
|
return statuses.map((s) => `${s.agentId}|${s.status}|${s.currentTask ?? ""}`).sort().join("\n");
|
|
3651
3651
|
}
|
|
3652
|
-
function
|
|
3652
|
+
function visibleLineKey(s) {
|
|
3653
|
+
const role = s.role && s.role !== s.name ? s.role : "";
|
|
3654
|
+
const task = s.currentTask && s.currentTask.length > TASK_SNIPPET_CHARS ? `${s.currentTask.slice(0, TASK_SNIPPET_CHARS)}\u2026` : s.currentTask ?? "";
|
|
3655
|
+
const tool = s.currentTool || "";
|
|
3656
|
+
const toolCalls = s.toolCalls > 0 ? String(s.toolCalls) : "";
|
|
3657
|
+
return [s.name, role, s.status, task, tool, toolCalls].join("\0");
|
|
3658
|
+
}
|
|
3659
|
+
function peerLine(s, count = 1) {
|
|
3653
3660
|
const role = s.role && s.role !== s.name ? ` (${s.role})` : "";
|
|
3654
|
-
const
|
|
3661
|
+
const grouped = count > 1 ? ` \xD7${count}` : "";
|
|
3662
|
+
const parts = [`\u2022 ${s.name}${role}${grouped} \u2014 ${s.status}`];
|
|
3655
3663
|
if (s.currentTask) {
|
|
3656
3664
|
const task = s.currentTask.length > TASK_SNIPPET_CHARS ? `${s.currentTask.slice(0, TASK_SNIPPET_CHARS)}\u2026` : s.currentTask;
|
|
3657
3665
|
parts.push(`"${task}"`);
|
|
@@ -3667,13 +3675,20 @@ function buildFleetPulseBlock(statuses, opts) {
|
|
|
3667
3675
|
if (peers.length === 0) return null;
|
|
3668
3676
|
const order = { running: 0, streaming: 0, waiting_user: 1, idle: 2, error: 3, offline: 4 };
|
|
3669
3677
|
const sorted = [...peers].sort(
|
|
3670
|
-
(x, y) => (order[x.status] ?? 5) - (order[y.status] ?? 5) || x.
|
|
3678
|
+
(x, y) => (order[x.status] ?? 5) - (order[y.status] ?? 5) || visibleLineKey(x).localeCompare(visibleLineKey(y))
|
|
3671
3679
|
);
|
|
3672
3680
|
const shown = sorted.slice(0, maxAgents);
|
|
3673
3681
|
const hidden = sorted.length - shown.length;
|
|
3674
3682
|
const parts = [];
|
|
3675
3683
|
parts.push(`[FLEET PULSE] ${peers.length} peer${peers.length === 1 ? "" : "s"} online:`);
|
|
3676
|
-
for (
|
|
3684
|
+
for (let i = 0; i < shown.length; ) {
|
|
3685
|
+
let run = 1;
|
|
3686
|
+
while (i + run < shown.length && visibleLineKey(shown[i]) === visibleLineKey(shown[i + run])) {
|
|
3687
|
+
run++;
|
|
3688
|
+
}
|
|
3689
|
+
parts.push(peerLine(shown[i], run));
|
|
3690
|
+
i += run;
|
|
3691
|
+
}
|
|
3677
3692
|
if (hidden > 0) parts.push(`\u2026 +${hidden} more`);
|
|
3678
3693
|
parts.push(
|
|
3679
3694
|
"[END FLEET PULSE] (FYI \u2014 coordinate via mail_send; avoid duplicating peers' work)"
|