@wrongstack/core 0.308.7 → 0.309.1
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 +16 -0
- package/dist/coordination/explore-companion.d.ts +9 -6
- package/dist/coordination/fleet.d.ts +12 -0
- package/dist/coordination/index.d.ts +1 -1
- package/dist/coordination/index.js +990 -61
- 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 +76 -0
- package/dist/coordination/subagent-budget.d.ts +54 -0
- package/dist/coordination/subagent-finish.d.ts +78 -0
- package/dist/core/index.js +58 -13
- package/dist/defaults/index.js +1132 -106
- package/dist/execution/index.js +260 -20
- package/dist/hq/index.js +45 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1456 -263
- package/dist/infrastructure/index.js +22 -3
- package/dist/kernel/events/agent-events.d.ts +31 -2
- package/dist/models/index.js +11 -1
- package/dist/observability/index.js +1 -1
- package/dist/plugin/index.js +113 -12
- package/dist/prompts/index.js +360 -3
- package/dist/security/auto-approve-policy.d.ts +2 -2
- package/dist/security/index.js +177 -50
- package/dist/security/permission-helpers.d.ts +11 -0
- package/dist/security/permission-policy.d.ts +10 -1
- package/dist/security/yolo-risk.d.ts +17 -0
- package/dist/session-catalog/index.js +32 -2
- package/dist/session-catalog/project-server.js +32 -2
- package/dist/skills/index.js +39 -6
- package/dist/storage/index.js +44 -3
- 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/dist/types/tool.d.ts +15 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +54 -4
- package/dist/utils/terminal-sanitize.d.ts +41 -0
- package/dist/utils/tool-subject.d.ts +1 -1
- package/instructions/agents/chaos-monkey.md +61 -0
- package/package.json +4 -4
|
@@ -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,76 @@
|
|
|
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, string literals,
|
|
49
|
+
* and template literals (single- or multi-line, interpolation contents
|
|
50
|
+
* excepted) are filtered out by the cross-line scanner `computeLineMasks`
|
|
51
|
+
* below.
|
|
52
|
+
*/
|
|
53
|
+
export declare function planMutations(file: string, source: string, opts?: PlanMutationsOptions): MutationPlanItem[];
|
|
54
|
+
/**
|
|
55
|
+
* Apply one planned mutation to source. Pure: same input → same output.
|
|
56
|
+
* Returns the original source when the site no longer matches (the file has
|
|
57
|
+
* drifted since planning) so callers can treat that as a skipped mutant.
|
|
58
|
+
*/
|
|
59
|
+
export declare function applyMutation(source: string, mutation: Pick<MutationPlanItem, 'kind' | 'line' | 'column' | 'original' | 'replacement'>): string;
|
|
60
|
+
/**
|
|
61
|
+
* Parse a structured mutation report emitted by the chaos-monkey subagent
|
|
62
|
+
* (either via `submit_result` or its final text). Tolerant of surrounding
|
|
63
|
+
* prose: the first JSON object containing a `mutants` array wins.
|
|
64
|
+
*/
|
|
65
|
+
export declare function parseMutationReport(text: string): {
|
|
66
|
+
mutants: Array<{
|
|
67
|
+
id: string;
|
|
68
|
+
file: string;
|
|
69
|
+
line: number;
|
|
70
|
+
kind: string;
|
|
71
|
+
status: 'killed' | 'survived' | 'skipped' | 'killed-by-hang';
|
|
72
|
+
evidence?: string | undefined;
|
|
73
|
+
}>;
|
|
74
|
+
summary?: string | undefined;
|
|
75
|
+
} | undefined;
|
|
76
|
+
//# 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
|
@@ -1751,6 +1751,14 @@ var PATTERNS = [
|
|
|
1751
1751
|
anchor: "sk-ant-"
|
|
1752
1752
|
},
|
|
1753
1753
|
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g, anchor: "sk-" },
|
|
1754
|
+
{
|
|
1755
|
+
// `xai` is a first-class provider in this codebase, but its key shape was
|
|
1756
|
+
// absent here — so the one credential format WrongStack itself hands users
|
|
1757
|
+
// was the one the scrubber could not recognize (audit 2026-08-20).
|
|
1758
|
+
type: "xai_key",
|
|
1759
|
+
regex: /(?<![A-Za-z0-9])xai-[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
|
|
1760
|
+
anchor: "xai-"
|
|
1761
|
+
},
|
|
1754
1762
|
{ type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g, anchor: "ghp_" },
|
|
1755
1763
|
{ type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g, anchor: "github_pat_" },
|
|
1756
1764
|
{ type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g, anchor: "AKIA" },
|
|
@@ -1841,8 +1849,8 @@ var PATTERNS = [
|
|
|
1841
1849
|
// replacement so the separator between adjacent secrets is preserved
|
|
1842
1850
|
// rather than collapsed. Capture groups are therefore: 1=leading
|
|
1843
1851
|
// delimiter, 2=key name, 3=value.
|
|
1844
|
-
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
|
|
1845
|
-
anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD"]
|
|
1852
|
+
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD|PASSPHRASE))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
|
|
1853
|
+
anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD", "PASSPHRASE"]
|
|
1846
1854
|
},
|
|
1847
1855
|
{
|
|
1848
1856
|
type: "json_credential_key",
|
|
@@ -1959,6 +1967,27 @@ var JSON_CREDENTIAL_REGEX = PATTERNS.find((p) => p.type === "json_credential_key
|
|
|
1959
1967
|
var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
|
|
1960
1968
|
var SCRUB_CHUNK_BYTES = 64 * 1024;
|
|
1961
1969
|
var SCRUB_OVERLAP_BYTES = 1024;
|
|
1970
|
+
var PEM_PRIVATE_KEY_BEGIN_RE = /-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----/;
|
|
1971
|
+
var PEM_END_MARKER = "-----END";
|
|
1972
|
+
var MAX_PEM_BLOCK_BYTES = 64 * 1024;
|
|
1973
|
+
var PEM_END_LINE_TOLERANCE = 64;
|
|
1974
|
+
function extendChunkBoundaryPastPem(text, chunkStart, proposedEnd) {
|
|
1975
|
+
const head = text.slice(chunkStart, proposedEnd);
|
|
1976
|
+
const lastBegin = head.lastIndexOf("-----BEGIN ");
|
|
1977
|
+
if (lastBegin === -1) return proposedEnd;
|
|
1978
|
+
const fromBegin = text.slice(chunkStart + lastBegin);
|
|
1979
|
+
const marker = PEM_PRIVATE_KEY_BEGIN_RE.exec(fromBegin);
|
|
1980
|
+
if (!marker || marker.index !== 0) return proposedEnd;
|
|
1981
|
+
const bodyStart = marker[0].length;
|
|
1982
|
+
const cap = Math.min(text.length, chunkStart + lastBegin + MAX_PEM_BLOCK_BYTES);
|
|
1983
|
+
const closeIdx = fromBegin.indexOf(PEM_END_MARKER, bodyStart);
|
|
1984
|
+
if (closeIdx === -1 || chunkStart + lastBegin + closeIdx >= cap + PEM_END_LINE_TOLERANCE) {
|
|
1985
|
+
return proposedEnd;
|
|
1986
|
+
}
|
|
1987
|
+
const lineEnd = fromBegin.indexOf("\n", closeIdx);
|
|
1988
|
+
const end = lineEnd === -1 ? text.length : chunkStart + lastBegin + lineEnd + 1;
|
|
1989
|
+
return Math.max(proposedEnd, end);
|
|
1990
|
+
}
|
|
1962
1991
|
var PATTERN_ANCHORS = [
|
|
1963
1992
|
...new Set(
|
|
1964
1993
|
PATTERNS.flatMap(
|
|
@@ -1995,6 +2024,7 @@ var DefaultSecretScrubber = class {
|
|
|
1995
2024
|
}
|
|
1996
2025
|
}
|
|
1997
2026
|
end = safe === -1 ? end : safe + 1;
|
|
2027
|
+
end = extendChunkBoundaryPastPem(text, i, end);
|
|
1998
2028
|
}
|
|
1999
2029
|
out.push(this.scrubOne(text.slice(i, end)));
|
|
2000
2030
|
i = end;
|
|
@@ -3649,9 +3679,17 @@ var TASK_SNIPPET_CHARS = 60;
|
|
|
3649
3679
|
function fleetPulseSignature(statuses) {
|
|
3650
3680
|
return statuses.map((s) => `${s.agentId}|${s.status}|${s.currentTask ?? ""}`).sort().join("\n");
|
|
3651
3681
|
}
|
|
3652
|
-
function
|
|
3682
|
+
function visibleLineKey(s) {
|
|
3683
|
+
const role = s.role && s.role !== s.name ? s.role : "";
|
|
3684
|
+
const task = s.currentTask && s.currentTask.length > TASK_SNIPPET_CHARS ? `${s.currentTask.slice(0, TASK_SNIPPET_CHARS)}\u2026` : s.currentTask ?? "";
|
|
3685
|
+
const tool = s.currentTool || "";
|
|
3686
|
+
const toolCalls = s.toolCalls > 0 ? String(s.toolCalls) : "";
|
|
3687
|
+
return [s.name, role, s.status, task, tool, toolCalls].join("\0");
|
|
3688
|
+
}
|
|
3689
|
+
function peerLine(s, count = 1) {
|
|
3653
3690
|
const role = s.role && s.role !== s.name ? ` (${s.role})` : "";
|
|
3654
|
-
const
|
|
3691
|
+
const grouped = count > 1 ? ` \xD7${count}` : "";
|
|
3692
|
+
const parts = [`\u2022 ${s.name}${role}${grouped} \u2014 ${s.status}`];
|
|
3655
3693
|
if (s.currentTask) {
|
|
3656
3694
|
const task = s.currentTask.length > TASK_SNIPPET_CHARS ? `${s.currentTask.slice(0, TASK_SNIPPET_CHARS)}\u2026` : s.currentTask;
|
|
3657
3695
|
parts.push(`"${task}"`);
|
|
@@ -3667,13 +3705,20 @@ function buildFleetPulseBlock(statuses, opts) {
|
|
|
3667
3705
|
if (peers.length === 0) return null;
|
|
3668
3706
|
const order = { running: 0, streaming: 0, waiting_user: 1, idle: 2, error: 3, offline: 4 };
|
|
3669
3707
|
const sorted = [...peers].sort(
|
|
3670
|
-
(x, y) => (order[x.status] ?? 5) - (order[y.status] ?? 5) || x.
|
|
3708
|
+
(x, y) => (order[x.status] ?? 5) - (order[y.status] ?? 5) || visibleLineKey(x).localeCompare(visibleLineKey(y))
|
|
3671
3709
|
);
|
|
3672
3710
|
const shown = sorted.slice(0, maxAgents);
|
|
3673
3711
|
const hidden = sorted.length - shown.length;
|
|
3674
3712
|
const parts = [];
|
|
3675
3713
|
parts.push(`[FLEET PULSE] ${peers.length} peer${peers.length === 1 ? "" : "s"} online:`);
|
|
3676
|
-
for (
|
|
3714
|
+
for (let i = 0; i < shown.length; ) {
|
|
3715
|
+
let run = 1;
|
|
3716
|
+
while (i + run < shown.length && visibleLineKey(shown[i]) === visibleLineKey(shown[i + run])) {
|
|
3717
|
+
run++;
|
|
3718
|
+
}
|
|
3719
|
+
parts.push(peerLine(shown[i], run));
|
|
3720
|
+
i += run;
|
|
3721
|
+
}
|
|
3677
3722
|
if (hidden > 0) parts.push(`\u2026 +${hidden} more`);
|
|
3678
3723
|
parts.push(
|
|
3679
3724
|
"[END FLEET PULSE] (FYI \u2014 coordinate via mail_send; avoid duplicating peers' work)"
|
|
@@ -6225,6 +6270,7 @@ function maybeAppendPendingNextSteps(ctx, res) {
|
|
|
6225
6270
|
// src/prompts/prompt-journal.ts
|
|
6226
6271
|
import * as fs5 from "node:fs/promises";
|
|
6227
6272
|
import * as path11 from "node:path";
|
|
6273
|
+
var defaultScrubber2 = new DefaultSecretScrubber();
|
|
6228
6274
|
async function ensureGitignore(projectRoot) {
|
|
6229
6275
|
const gitignorePath = path11.join(projectRoot, ".gitignore");
|
|
6230
6276
|
try {
|
|
@@ -6252,7 +6298,9 @@ async function recordPromptJournalEntry(opts) {
|
|
|
6252
6298
|
const monthStr = dateStr.slice(0, 7);
|
|
6253
6299
|
const sessionId = opts.sessionId && opts.sessionId.trim() ? opts.sessionId.trim() : "general";
|
|
6254
6300
|
const id = `pmt_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
6255
|
-
const content = opts.content ?? "";
|
|
6301
|
+
const content = defaultScrubber2.scrub(opts.content ?? "");
|
|
6302
|
+
const rawContent = typeof opts.rawContent === "string" && opts.rawContent.length > 0 ? defaultScrubber2.scrub(opts.rawContent) : opts.rawContent;
|
|
6303
|
+
const decisionReason = typeof opts.decisionReason === "string" && opts.decisionReason.length > 0 ? defaultScrubber2.scrub(opts.decisionReason) : opts.decisionReason;
|
|
6256
6304
|
const lines = content.split("\n");
|
|
6257
6305
|
const characterCount = content.length;
|
|
6258
6306
|
const lineCount = lines.length;
|
|
@@ -6265,7 +6313,7 @@ async function recordPromptJournalEntry(opts) {
|
|
|
6265
6313
|
role: opts.role ?? (opts.category === "system_prompt" ? "system" : "user"),
|
|
6266
6314
|
category: opts.category,
|
|
6267
6315
|
content,
|
|
6268
|
-
rawContent
|
|
6316
|
+
rawContent,
|
|
6269
6317
|
metadata: {
|
|
6270
6318
|
model: opts.model,
|
|
6271
6319
|
provider: opts.provider,
|
|
@@ -6276,7 +6324,7 @@ async function recordPromptJournalEntry(opts) {
|
|
|
6276
6324
|
activeTools: opts.activeTools,
|
|
6277
6325
|
contextFiles: opts.contextFiles,
|
|
6278
6326
|
durationMs: opts.durationMs,
|
|
6279
|
-
decisionReason
|
|
6327
|
+
decisionReason,
|
|
6280
6328
|
tags: opts.tags
|
|
6281
6329
|
}
|
|
6282
6330
|
};
|
|
@@ -8876,10 +8924,7 @@ function createAgentToolHandler(a) {
|
|
|
8876
8924
|
} catch {
|
|
8877
8925
|
}
|
|
8878
8926
|
}
|
|
8879
|
-
if (decision === "
|
|
8880
|
-
const p = a.permission;
|
|
8881
|
-
p.allowOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
8882
|
-
} else if (decision === "no") {
|
|
8927
|
+
if (decision === "no") {
|
|
8883
8928
|
const p = a.permission;
|
|
8884
8929
|
p.denyOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
8885
8930
|
}
|