@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.
@@ -144,8 +144,8 @@ export interface AgentEventMap {
144
144
  /**
145
145
  * A spawn resolved fewer skills than it selected. Emitted so a skill that
146
146
  * silently failed to load — missing from the loader, gated by a capability
147
- * the subagent lacks, or cut by the prompt budget — is observable instead of
148
- * leaving the agent believing it received guidance it never got.
147
+ * the subagent lacks, or cut by the prompt budget — is observable instead
148
+ * of leaving the agent believing it received guidance it never got.
149
149
  */
150
150
  'subagent.skills.dropped': {
151
151
  sessionId?: string | undefined;
@@ -155,6 +155,35 @@ export interface AgentEventMap {
155
155
  /** skill → reason it was dropped. */
156
156
  dropped: Record<string, string>;
157
157
  };
158
+ /**
159
+ * In-band graceful-finish request for a background subagent (see
160
+ * coordination/subagent-finish.ts). Emitted on the subagent's OWN EventBus
161
+ * when its wall-clock deadline is crossed — or the leader explicitly asked
162
+ * it to finish (session shutdown) — and the subagent's config opted into
163
+ * `gracefulFinish`. The runner folds the notice into the conversation as a
164
+ * `/btw` note at the next iteration boundary; the model then completes its
165
+ * task in its own turn within the granted grace window. This event is a
166
+ * notification, never an interrupt: nothing aborts when it fires.
167
+ */
168
+ 'subagent.finish_requested': {
169
+ /** Parent/host session id. */
170
+ sessionId?: string | undefined;
171
+ /**
172
+ * Owning subagent id when the budget knows it; omitted when the budget
173
+ * was constructed without one. An empty string would look like an
174
+ * address that matches nothing — the runner treats an omitted id as
175
+ * deliverable (`if (e.subagentId && …)`), so omission is safe.
176
+ */
177
+ subagentId?: string | undefined;
178
+ /** Why the finish was requested (deadline crossed / leader finished). */
179
+ reason: string;
180
+ /** Epoch ms by which the subagent should have produced its final output. */
181
+ deadlineMs: number;
182
+ /** Granted working-time window in ms. */
183
+ graceMs: number;
184
+ /** Ready-to-read notice text; the loop folds it in as a `/btw` note. */
185
+ notice: string;
186
+ };
158
187
  /**
159
188
  * A background learning-distillation pass finished for a roster role.
160
189
  * Emitted by the fleet host's auto-optimize scheduler so surfaces can show
@@ -877,7 +877,17 @@ function normalizeModelsDevModel(model) {
877
877
  const reasoningConfig = {
878
878
  default: disableSupported ? "enabled" : "always_on",
879
879
  disableSupported,
880
- effortSupported: effortLevels.length > 0,
880
+ // Tri-state (see ReasoningConfig.effortSupported):
881
+ // options present → documented answer (true when effort values exist;
882
+ // an explicitly EMPTY array is a documented "no
883
+ // effort control", not an absent field).
884
+ // field ABSENT → the model is known to reason but its vocabulary is
885
+ // undocumented → `undefined`, so the resolver forwards
886
+ // the request and each wire adapter applies its own
887
+ // transport gating. Sending `false` here would make
888
+ // the resolver claim "does not support effort" — an
889
+ // assertion the catalog never made.
890
+ ...raw === void 0 ? {} : { effortSupported: effortLevels.length > 0 },
881
891
  effortLevels,
882
892
  preserveThinking: model.interleaved ? "always_on" : "unsupported"
883
893
  };
@@ -34,7 +34,7 @@ export { BUILTIN_PROMPT_CATEGORIES, isBuiltinCategory, PROMPT_CATEGORY_LABELS }
34
34
  export type { InstalledPromptEntry, ManifestValidation, PromptManifestData, PromptRegistryManifest, PromptRegistryRef, RegistryDiff, } from './prompt-registry.js';
35
35
  export { diffRegistry, validateRegistryManifest } from './prompt-registry.js';
36
36
  export type { CacheTtl, Capabilities, JsonSchemaSpec, Provider, ProviderContextLimit, ProviderErrorBody, ProviderErrorKind, ReasoningConfig, ReasoningEffort, ReasoningRequest, Request, RequestCacheControl, Response, ResponseFormat, SafetySetting, StopReason, StreamEvent, Usage, } from './provider.js';
37
- export { classifyProviderError, effectiveInputTokens, isContextOverflowShaped, isFallbackWorthy, isRetryableKind, ProviderError, StreamHangError, } from './provider.js';
37
+ export { classifyProviderError, effectiveInputTokens, isContextOverflowShaped, isFallbackWorthy, isReasoningEffort, isRetryableKind, ProviderError, REASONING_EFFORT_LEVELS, StreamHangError, } from './provider.js';
38
38
  export type { ProviderRunner, RunProviderOptions } from './provider-runner.js';
39
39
  export type { Renderer } from './renderer.js';
40
40
  export type { SecretScrubber } from './secret-scrubber.js';
@@ -914,6 +914,18 @@ function truncate(s, max) {
914
914
  var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
915
915
 
916
916
  // src/types/provider.ts
917
+ var REASONING_EFFORT_LEVELS = [
918
+ "none",
919
+ "minimal",
920
+ "low",
921
+ "medium",
922
+ "high",
923
+ "xhigh",
924
+ "max"
925
+ ];
926
+ function isReasoningEffort(value) {
927
+ return typeof value === "string" && REASONING_EFFORT_LEVELS.includes(value);
928
+ }
917
929
  function effectiveInputTokens(usage) {
918
930
  return usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
919
931
  }
@@ -1346,6 +1358,7 @@ export {
1346
1358
  ParseError,
1347
1359
  PluginError,
1348
1360
  ProviderError,
1361
+ REASONING_EFFORT_LEVELS,
1349
1362
  SESSION_MARKER_EVENT_TYPES,
1350
1363
  SYSTEM_INJECTION_PREFIXES,
1351
1364
  SddError,
@@ -1377,6 +1390,7 @@ export {
1377
1390
  isImageBlock,
1378
1391
  isParseError,
1379
1392
  isPluginError,
1393
+ isReasoningEffort,
1380
1394
  isRetryableKind,
1381
1395
  isSddError,
1382
1396
  isSessionError,
@@ -142,6 +142,21 @@ export interface SubagentConfig {
142
142
  end: string;
143
143
  mode?: 'advisory' | 'enforce' | undefined;
144
144
  } | undefined;
145
+ /**
146
+ * Model-driven completion policy. When set, this subagent is NEVER killed
147
+ * by the wall-clock watchdog at its deadline: instead the deadline (or an
148
+ * explicit `Director.requestFinish()`) triggers an in-band
149
+ * `subagent.finish_requested` notification that the agent loop folds into
150
+ * the conversation between tool batches — the model then finishes its task
151
+ * in its own turn within `graceMs` of legitimate working time. Only after
152
+ * that grace window elapses does the existing terminal stop apply, so the
153
+ * subagent still has a bounded maximum lifetime.
154
+ *
155
+ * `undefined` (default) keeps the legacy watchdog behavior unchanged.
156
+ */
157
+ gracefulFinish?: boolean | {
158
+ graceMs?: number | undefined;
159
+ } | undefined;
145
160
  /**
146
161
  * Runtime request overrides for THIS subagent. When present, these are merged
147
162
  * over the leader's `Config.modelRuntime` before the subagent request pipeline
@@ -23,6 +23,21 @@ import type { Tool } from './tool.js';
23
23
  * cached tokens twice and skew cache-hit-ratio reporting.
24
24
  */
25
25
  export type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
26
+ /**
27
+ * The canonical runtime list of {@link ReasoningEffort} values, in
28
+ * menu/display order. Single source of truth for every surface that needs to
29
+ * iterate the levels (CLI `/settings` + `/setmodel`, the TUI picker, the
30
+ * WebUI dropdown) — import this instead of re-declaring a local array, which
31
+ * is how drift crept in before.
32
+ *
33
+ * `satisfies` pins the literal to the union: a value here core's type doesn't
34
+ * know is a compile error. Note the reverse is NOT caught — core adding a
35
+ * level does not force this array to grow, so consumers validating user input
36
+ * against it must decide deliberately whether to expose the new level.
37
+ */
38
+ export declare const REASONING_EFFORT_LEVELS: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
39
+ /** Type guard for untrusted strings (CLI args, WS payloads, config files). */
40
+ export declare function isReasoningEffort(value: unknown): value is ReasoningEffort;
26
41
  export type CacheTtl = '5m' | '1h';
27
42
  /**
28
43
  * Provider-agnostic response-format directive.
@@ -119,7 +134,20 @@ export interface RequestCacheControl {
119
134
  export interface ReasoningConfig {
120
135
  default: 'enabled' | 'disabled' | 'adaptive' | 'always_on';
121
136
  disableSupported: boolean;
122
- effortSupported: boolean;
137
+ /**
138
+ * Tri-state effort support:
139
+ * `true` — the catalog documents this model's effort levels
140
+ * (`effortLevels` is authoritative).
141
+ * `false` — the catalog documents effort control as absent
142
+ * (toggle-only or budget_tokens-only reasoning options).
143
+ * `undefined` — the model is known to reason (`reasoning: true`) but its
144
+ * effort vocabulary is not documented. The resolver forwards
145
+ * the requested effort; each wire adapter then applies its
146
+ * own transport-level gating (allowlist, mapping, or omit),
147
+ * so an undocumented model can only match-or-omit — never
148
+ * receive a field shape it did not advertise.
149
+ */
150
+ effortSupported?: boolean | undefined;
123
151
  effortLevels: ReasoningEffort[];
124
152
  preserveThinking: 'unsupported' | 'optional' | 'always_on';
125
153
  }
@@ -0,0 +1,57 @@
1
+ You are the Chaos Monkey ("Kaos Maymunu") — a mutation-testing saboteur for the
2
+ WrongStack fleet. Your job is to prove whether a test suite actually pins down
3
+ the code it claims to cover, by deliberately breaking that code and watching
4
+ which mutants survive.
5
+
6
+ Core belief: green tests prove nothing if they cannot detect sabotage. A mutant
7
+ that survives means the tests are fake or insufficient — and that is the most
8
+ valuable finding you can return.
9
+
10
+ ## Your task contract
11
+
12
+ The director hands you a mutation plan: an exact list of mutation ids, each with
13
+ file, line, column, kind, original token and replacement token. The plan is
14
+ authoritative — you NEVER invent, move, or "improve" mutations. Your freedom is
15
+ execution order and diagnosis, never the mutation set.
16
+
17
+ ## Check pass (per mutant)
18
+
19
+ 1. Apply exactly ONE mutation from the plan to its anchored (file, line, column).
20
+ If the anchored token no longer matches `original`, mark the mutant
21
+ `skipped` with the drift as evidence — do not hunt for a "similar" site.
22
+ 2. Run the provided test command exactly as given.
23
+ 3. Record the outcome:
24
+ - Tests fail → mutant `killed` (quote the first failing assertion).
25
+ - Tests pass → mutant `survived` (this is a weak-test finding, not your failure).
26
+ 4. Restore the original source byte-for-byte before moving to the next mutant.
27
+ The suite is only honest if every mutant ran against pristine code except
28
+ its own single mutation.
29
+
30
+ ## Hard rules
31
+
32
+ - **One mutation at a time.** Never stack mutants; a stacked run measures nothing.
33
+ - **Always restore.** Your worktree must be clean of sabotage at the end of the
34
+ pass. If restore fails, stop and report which file is left mutated.
35
+ - **Stay inside the plan's files.** No refactors, no fixes, no formatting churn
36
+ — even when the mutated code looks wrong to you. You are the saboteur, not
37
+ the reviewer.
38
+ - **Deterministic.** Same plan + same suite → same report.
39
+ - **One-shot lifecycle.** Finish the assigned pass, submit the report, stop.
40
+
41
+ ## Report
42
+
43
+ Submit via `submit_result`, then repeat it as your final text (fenced JSON):
44
+
45
+ ```json
46
+ {
47
+ "summary": "<one line: N killed / M survived / K skipped>",
48
+ "mutants": [
49
+ { "id": "<plan id>", "file": "...", "line": 0, "kind": "...",
50
+ "status": "killed | survived | skipped",
51
+ "evidence": "<failing assertion, or 'suite green' for survivors>" }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ Order survivors first — they are the actionable findings. For each survivor,
57
+ name the boundary or behavior the tests failed to assert.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/core",
3
- "version": "0.308.7",
3
+ "version": "0.309.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack core: kernel, types, defaults, and shared utilities for the WrongStack CLI agent.",
6
6
  "repository": {
@@ -182,8 +182,8 @@
182
182
  "wrongstackApiVersion": "0.1.10",
183
183
  "dependencies": {
184
184
  "zod": "4.4.3",
185
- "@wrongstack/persistence": "0.308.7",
186
- "@wrongstack/kanban": "0.308.7"
185
+ "@wrongstack/kanban": "0.309.0",
186
+ "@wrongstack/persistence": "0.309.0"
187
187
  },
188
188
  "devDependencies": {
189
189
  "@types/node": "^26.2.0",