@wrongstack/core 0.303.0 → 0.305.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.
Files changed (76) hide show
  1. package/dist/chronicle/project-server.js +18 -48
  2. package/dist/coordination/agents/index.js +530 -130
  3. package/dist/coordination/agents/project-agent-consolidation.d.ts +5 -0
  4. package/dist/coordination/agents/project-agent-directive-outcome.d.ts +57 -0
  5. package/dist/coordination/agents/project-agent-identity.d.ts +26 -12
  6. package/dist/coordination/agents/project-agent-learning-policy.d.ts +22 -1
  7. package/dist/coordination/agents/project-agent-learning-structured.d.ts +46 -1
  8. package/dist/coordination/agents/project-agent-quarantine.d.ts +63 -0
  9. package/dist/coordination/agents/project-agent-skill-layer.d.ts +55 -10
  10. package/dist/coordination/agents/types.d.ts +10 -2
  11. package/dist/coordination/director-prompts.d.ts +19 -6
  12. package/dist/coordination/director-tools.d.ts +2 -2
  13. package/dist/coordination/fleet.d.ts +0 -6
  14. package/dist/coordination/index.d.ts +2 -1
  15. package/dist/coordination/index.js +1581 -955
  16. package/dist/coordination/mailbox-project-server.js +28 -57
  17. package/dist/core/agent-types.d.ts +4 -2
  18. package/dist/core/agent.d.ts +1 -0
  19. package/dist/core/context.d.ts +15 -0
  20. package/dist/core/conversation-state.d.ts +14 -0
  21. package/dist/core/fallback-profile-manager.d.ts +70 -2
  22. package/dist/core/index.js +308 -108
  23. package/dist/core/system-prompt-blocks.d.ts +1 -1
  24. package/dist/core/system-prompt-builder.d.ts +13 -1
  25. package/dist/core/system-prompt-glossary.d.ts +73 -0
  26. package/dist/core/system-prompt-memory-skills.d.ts +2 -2
  27. package/dist/defaults/index.js +910 -693
  28. package/dist/execution/council-orchestrator.d.ts +3 -13
  29. package/dist/execution/index.js +211 -75
  30. package/dist/execution/one-shot-llm.d.ts +5 -0
  31. package/dist/hq/index.js +17 -7
  32. package/dist/hq/protocol/kanban.d.ts +21 -0
  33. package/dist/hq/protocol.js +5 -1
  34. package/dist/hq/redaction.d.ts +14 -0
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +3505 -2539
  37. package/dist/infrastructure/index.js +247 -122
  38. package/dist/plugin/index.js +101 -3
  39. package/dist/registry/index.js +11 -0
  40. package/dist/registry/tool-registry.d.ts +8 -0
  41. package/dist/replay/hash.d.ts +9 -0
  42. package/dist/replay/index.js +14 -4
  43. package/dist/replay/replay-provider-runner.d.ts +31 -1
  44. package/dist/security/index.js +25 -20
  45. package/dist/security/secret-vault.d.ts +2 -0
  46. package/dist/session-catalog/index.js +62 -8
  47. package/dist/session-catalog/project-server.js +109 -78
  48. package/dist/session-catalog/protocol.d.ts +11 -4
  49. package/dist/session-catalog/store.d.ts +2 -2
  50. package/dist/storage/index.js +224 -67
  51. package/dist/storage/memory-consolidator.d.ts +4 -2
  52. package/dist/storage/session-resume-validation.d.ts +24 -0
  53. package/dist/storage/session-store/directory-scan.d.ts +5 -1
  54. package/dist/storage/session-store/fork-session.d.ts +13 -1
  55. package/dist/storage/session-store/load-cache.d.ts +11 -0
  56. package/dist/storage/session-store/prune-helpers.d.ts +5 -0
  57. package/dist/storage/session-store.d.ts +18 -0
  58. package/dist/tools/index.js +174 -74
  59. package/dist/types/config/mcp-features.d.ts +31 -1
  60. package/dist/types/config/root.d.ts +12 -0
  61. package/dist/types/config/tools.d.ts +22 -0
  62. package/dist/types/config/ui.d.ts +7 -4
  63. package/dist/types/default-config.d.ts +1 -0
  64. package/dist/types/index.js +24 -1
  65. package/dist/types/session.d.ts +9 -1
  66. package/dist/utils/index.d.ts +1 -0
  67. package/dist/utils/index.js +214 -76
  68. package/dist/utils/project-state-guard.d.ts +21 -0
  69. package/dist/utils/session-scoped-path.d.ts +17 -0
  70. package/dist/utils/todos-format.d.ts +20 -0
  71. package/instructions/leader-after-task.md +3 -4
  72. package/instructions/system-lite.md +10 -13
  73. package/instructions/system-pro.md +18 -25
  74. package/instructions/system.md +18 -23
  75. package/package.json +3 -3
  76. package/skills/wrongstack-kanban/SKILL.md +95 -124
@@ -2,10 +2,10 @@
2
2
 
3
3
  // src/coordination/mailbox-project-server.ts
4
4
  import { randomBytes as randomBytes2 } from "node:crypto";
5
- import * as fs5 from "node:fs";
6
5
  import * as fsPromises from "node:fs/promises";
7
6
  import * as net from "node:net";
8
7
  import * as path7 from "node:path";
8
+ import { bindProjectEndpoint } from "@wrongstack/persistence";
9
9
 
10
10
  // src/kernel/events.ts
11
11
  var MAX_WILDCARDS = 500;
@@ -1173,17 +1173,9 @@ var MailboxEventEmitter = class {
1173
1173
 
1174
1174
  // src/coordination/mailbox-project-server-endpoint.ts
1175
1175
  import { createHash as createHash2 } from "node:crypto";
1176
- import * as fs2 from "node:fs";
1177
1176
  import * as os2 from "node:os";
1178
1177
  import * as path4 from "node:path";
1179
1178
 
1180
- // src/utils/socket-path.ts
1181
- import {
1182
- assertUnixSocketPathWithinLimit,
1183
- checkUnixSocketPath,
1184
- unixSocketPathLimit
1185
- } from "@wrongstack/persistence";
1186
-
1187
1179
  // src/coordination/mailbox-project-server-protocol.ts
1188
1180
  var MAILBOX_PROJECT_SERVER_PROTOCOL_VERSION = 4;
1189
1181
  var MAILBOX_PROJECT_SERVER_MAX_FRAME_CHARS = 16 * 1024 * 1024;
@@ -1317,16 +1309,10 @@ function mailboxProjectServerEndpoint(projectDir2) {
1317
1309
  function mailboxProjectServerMetadataPath(projectDir2) {
1318
1310
  return path4.join(path4.resolve(projectDir2), MAILBOX_PROJECT_SERVER_METADATA_FILE);
1319
1311
  }
1320
- function ensureMailboxProjectServerSocketDirectory(endpoint2) {
1321
- if (process.platform !== "win32") {
1322
- assertUnixSocketPathWithinLimit(endpoint2, "mailbox");
1323
- fs2.mkdirSync(path4.dirname(endpoint2), { recursive: true, mode: 448 });
1324
- }
1325
- }
1326
1312
 
1327
1313
  // src/coordination/sqlite-mailbox.ts
1328
1314
  import { randomUUID as randomUUID2 } from "node:crypto";
1329
- import * as fs4 from "node:fs";
1315
+ import * as fs3 from "node:fs";
1330
1316
  import * as path6 from "node:path";
1331
1317
 
1332
1318
  // src/coordination/global-mailbox-completion.ts
@@ -2218,7 +2204,7 @@ function credentialRotate(db, transaction, credentialId, options) {
2218
2204
  }
2219
2205
 
2220
2206
  // src/coordination/sqlite-mailbox-schema.ts
2221
- import * as fs3 from "node:fs";
2207
+ import * as fs2 from "node:fs";
2222
2208
  import { createRequire } from "node:module";
2223
2209
  import * as path5 from "node:path";
2224
2210
 
@@ -2473,7 +2459,7 @@ function migrateLegacyCredentials(ctx) {
2473
2459
  const legacyPath = path5.join(ctx.projectDir, CREDENTIAL_STORE_FILE);
2474
2460
  const credentials = [];
2475
2461
  try {
2476
- for (const line of fs3.readFileSync(legacyPath, "utf8").split(/\r?\n/u)) {
2462
+ for (const line of fs2.readFileSync(legacyPath, "utf8").split(/\r?\n/u)) {
2477
2463
  if (!line.trim()) continue;
2478
2464
  try {
2479
2465
  const credential = JSON.parse(line);
@@ -2526,7 +2512,7 @@ function migrateLegacyFiles(ctx) {
2526
2512
  function readLegacyMessages(projectDir2) {
2527
2513
  try {
2528
2514
  return parseMailboxFile(
2529
- fs3.readFileSync(path5.join(projectDir2, GLOBAL_MAILBOX_FILE), "utf8")
2515
+ fs2.readFileSync(path5.join(projectDir2, GLOBAL_MAILBOX_FILE), "utf8")
2530
2516
  );
2531
2517
  } catch (error) {
2532
2518
  if (error.code === "ENOENT") return [];
@@ -2535,7 +2521,7 @@ function readLegacyMessages(projectDir2) {
2535
2521
  }
2536
2522
  function readLegacyRegistry(filePath, parseEntry) {
2537
2523
  try {
2538
- const raw = JSON.parse(fs3.readFileSync(filePath, "utf8"));
2524
+ const raw = JSON.parse(fs2.readFileSync(filePath, "utf8"));
2539
2525
  const result = /* @__PURE__ */ new Map();
2540
2526
  for (const [id, value] of Object.entries(raw)) {
2541
2527
  const parsed = parseEntry(value);
@@ -2555,7 +2541,7 @@ var HEARTBEAT_TRACKING_TTL_MS = 30 * 6e4;
2555
2541
  var SqliteMailbox = class {
2556
2542
  constructor(projectDir2, events2, eventEmitter2) {
2557
2543
  this.projectDir = projectDir2;
2558
- fs4.mkdirSync(projectDir2, { recursive: true });
2544
+ fs3.mkdirSync(projectDir2, { recursive: true });
2559
2545
  this.databasePath = path6.join(projectDir2, SQLITE_MAILBOX_FILE);
2560
2546
  this.messagePath = this.databasePath;
2561
2547
  this.events = events2;
@@ -3451,7 +3437,6 @@ async function stop(_reason) {
3451
3437
  }
3452
3438
  await stopMemoryWatchdog();
3453
3439
  }
3454
- ensureMailboxProjectServerSocketDirectory(endpoint);
3455
3440
  var server = net.createServer((socket) => {
3456
3441
  if (stopping) {
3457
3442
  socket.destroy();
@@ -3481,42 +3466,28 @@ var leaseSweep = setInterval(() => {
3481
3466
  scheduleIdleStop();
3482
3467
  }, leaseSweepMs);
3483
3468
  leaseSweep.unref?.();
3484
- var probingExistingEndpoint = false;
3485
- function listenForOwnership() {
3486
- server.listen(endpoint);
3487
- }
3488
- server.on("error", (error) => {
3489
- if (error.code === "EADDRINUSE" && process.platform === "win32") {
3469
+ void (async () => {
3470
+ const bind = await bindProjectEndpoint({ server, endpoint, service: "mailbox" });
3471
+ if (bind.outcome === "already-owned") {
3490
3472
  process.exitCode = 0;
3491
3473
  return;
3492
3474
  }
3493
- if (error.code === "EADDRINUSE" && !probingExistingEndpoint) {
3494
- probingExistingEndpoint = true;
3495
- const probe = net.createConnection(endpoint);
3496
- probe.once("connect", () => {
3497
- probe.destroy();
3498
- process.exitCode = 0;
3499
- });
3500
- probe.once("error", () => {
3501
- probe.destroy();
3502
- try {
3503
- fs5.rmSync(endpoint, { force: true });
3504
- } catch {
3505
- }
3506
- probingExistingEndpoint = false;
3507
- listenForOwnership();
3508
- });
3475
+ if (bind.outcome === "failed") {
3476
+ process.stderr.write(`mailbox project server failed: ${bind.error.message}
3477
+ `);
3478
+ process.exitCode = 1;
3509
3479
  return;
3510
3480
  }
3511
- process.exitCode = 1;
3512
- });
3513
- server.on("listening", () => {
3514
- if (process.platform !== "win32") {
3515
- try {
3516
- fs5.chmodSync(endpoint, 384);
3517
- } catch {
3518
- }
3481
+ if (bind.reclaimedStaleEndpoint) {
3482
+ process.stderr.write(`mailbox project server reclaimed stale endpoint ${endpoint}
3483
+ `);
3519
3484
  }
3485
+ server.on("error", (error) => {
3486
+ if (stopping) return;
3487
+ process.stderr.write(`mailbox project server error: ${error.message}
3488
+ `);
3489
+ process.exitCode = 1;
3490
+ });
3520
3491
  try {
3521
3492
  mailbox = new SqliteMailbox(projectDir, events, eventEmitter);
3522
3493
  } catch {
@@ -3526,16 +3497,16 @@ server.on("listening", () => {
3526
3497
  return;
3527
3498
  }
3528
3499
  stopAutoCompact = mailbox.startAutoCompactTimer();
3529
- void writeMetadata().then(() => {
3500
+ try {
3501
+ await writeMetadata();
3530
3502
  markMetadataWritten?.();
3531
3503
  scheduleIdleStop();
3532
- }).catch(() => {
3504
+ } catch {
3533
3505
  void stop("metadata-write-failed").finally(() => {
3534
3506
  process.exitCode = 1;
3535
3507
  });
3536
- });
3537
- });
3538
- listenForOwnership();
3508
+ }
3509
+ })();
3539
3510
  for (const signal of ["SIGINT", "SIGTERM"]) {
3540
3511
  process.once(signal, () => {
3541
3512
  void stop(signal).finally(() => {
@@ -2,17 +2,17 @@
2
2
  * Agent type definitions — extracted from agent.ts to break circular
3
3
  * dependencies and keep the Agent class focused on runtime logic.
4
4
  */
5
- import { Pipeline } from '../kernel/pipeline.js';
6
5
  import type { ExtensionRegistry } from '../extension/registry.js';
7
6
  import type { Container } from '../kernel/container.js';
8
7
  import type { EventBus } from '../kernel/events.js';
8
+ import { Pipeline } from '../kernel/pipeline.js';
9
9
  import type { ProviderRegistry } from '../registry/provider-registry.js';
10
10
  import type { ToolRegistry } from '../registry/tool-registry.js';
11
11
  import type { ContentBlock, TextBlock, ToolResultBlock, ToolUseBlock } from '../types/blocks.js';
12
+ import type { LoopDetectionConfig } from '../types/config.js';
12
13
  import type { WrongStackError } from '../types/errors.js';
13
14
  import type { Tracer } from '../types/observability.js';
14
15
  import type { PermissionPolicy } from '../types/permission.js';
15
- import type { LoopDetectionConfig } from '../types/config.js';
16
16
  import type { Request, Response } from '../types/provider.js';
17
17
  import type { Tool } from '../types/tool.js';
18
18
  import type { ToolExecutorLike } from '../types/tool-executor.js';
@@ -44,6 +44,8 @@ export interface AgentInit {
44
44
  perIterationOutputCapBytes?: number | undefined;
45
45
  autoExtendLimit?: boolean | undefined;
46
46
  autonomousContinue?: boolean | undefined;
47
+ /** Rebuild the host system prompt from the live direct/catalog tool surfaces before each run. */
48
+ refreshSystemPrompt?: boolean | undefined;
47
49
  confirmAwaiter?: import('../types/tool-executor.js').ConfirmAwaiter | undefined;
48
50
  permissionPolicy?: PermissionPolicy | undefined;
49
51
  tracer?: Tracer | undefined;
@@ -33,6 +33,7 @@ export declare class Agent {
33
33
  /** Resolved loop-detector settings (see `tools.loopDetection`). */
34
34
  readonly loopDetection: ResolvedLoopDetectionConfig;
35
35
  private readonly autonomousContinue;
36
+ private readonly refreshSystemPrompt;
36
37
  readonly tracer: Tracer | undefined;
37
38
  readonly extensions: ExtensionRegistry;
38
39
  private readonly _toolHandler;
@@ -21,6 +21,17 @@ export interface TodoItem {
21
21
  kanbanBoardId?: string | undefined;
22
22
  /** Durable Kanban card represented by this todo row. */
23
23
  kanbanTaskId?: string | undefined;
24
+ /**
25
+ * Titles of the unfinished work this row waits on, derived from the Kanban
26
+ * card's dependencies. Always board-derived, never model-supplied.
27
+ *
28
+ * A blocked item is genuinely `pending` — it has not started — so this is an
29
+ * extra field rather than a fourth status. What was missing was never the
30
+ * state but the *reason*: the board computes readiness on every mutation,
31
+ * then the projection dropped it, leaving blocked work indistinguishable
32
+ * from ready work on screen and in the model's context.
33
+ */
34
+ blockedBy?: string[] | undefined;
24
35
  }
25
36
  export interface RunOptions {
26
37
  signal?: AbortSignal | undefined;
@@ -333,6 +344,10 @@ export declare class Context implements RunEnv {
333
344
  /** Wait until every exact conversation-state event queued so far is in the writer buffer. */
334
345
  flushConversationJournal(): Promise<void>;
335
346
  private conversationJournalBytes;
347
+ private _journalDropCount;
348
+ private _journalDropWarnAt;
349
+ /** Throttled notice that a conversation event never reached the journal. */
350
+ private warnConversationJournalDrop;
336
351
  private enqueueConversationJournal;
337
352
  private startConversationJournalDrain;
338
353
  get state(): ConversationState;
@@ -88,6 +88,20 @@ export declare class ConversationState {
88
88
  * cap determines the starting index for the sum but does not gate it.
89
89
  */
90
90
  private overflowCount;
91
+ /**
92
+ * Front eviction must not retain a `tool_result` after evicting the
93
+ * immediately preceding assistant `tool_use`. Long tool-heavy sessions sit
94
+ * at the retention cap, so an unsafe boundary would create a fresh orphan on
95
+ * nearly every append and make the request-time repair discard protocol
96
+ * history continuously.
97
+ *
98
+ * Move the boundary backward to retain the complete exchange for one more
99
+ * eviction cycle. Moving it forward would also drop non-protocol text/images
100
+ * that may share either message. The temporary one-message cap overshoot is
101
+ * the minimum lossless representation; once enough newer messages exist, the
102
+ * next eviction boundary naturally moves past both halves together.
103
+ */
104
+ private protocolSafeDropCount;
91
105
  /**
92
106
  * Append a content block to the trailing user message's content array.
93
107
  * Mutates only that one message (a single indexed assignment) — avoids
@@ -98,14 +98,60 @@ export declare class FallbackProfileManager {
98
98
  model: string;
99
99
  }): FallbackChain;
100
100
  /**
101
- * Resolve every usable configured target as an uncapped last-resort chain.
101
+ * Resolve every usable configured target as a bounded last-resort chain.
102
102
  * Normal smart defaults stay bounded; callers append this only after the
103
- * preferred chain and only when automatic fallback is enabled.
103
+ * preferred chain and only when automatic fallback is enabled. The cap
104
+ * ({@link MAX_LAST_RESORT_CANDIDATES}) prevents a config with many providers
105
+ * from producing a degenerate chain of doomed requests during a systemic
106
+ * outage — by this point the smart default, bridge, and default profile
107
+ * have already failed.
104
108
  */
105
109
  resolveAllConfigured(exclude?: {
106
110
  providerId: string;
107
111
  model: string;
108
112
  }): FallbackChain;
113
+ /**
114
+ * Effective cap for the last-resort append. Reads the user-configurable
115
+ * {@link Config.fallbackMaxLastResortCandidates} when set and valid;
116
+ * otherwise falls back to the compiled-in default
117
+ * {@link MAX_LAST_RESORT_CANDIDATES}.
118
+ */
119
+ private lastResortCap;
120
+ /**
121
+ * Build the complete fallback candidate chain shared by the agent-loop
122
+ * extension and the one-shot orchestrator. Centralizes the bridge → primary →
123
+ * selected → default-profile → all-configured ladder and the fromExplicitSource
124
+ * gate so both consumers produce identical ordering and depth semantics.
125
+ *
126
+ * Layering (each step deduped against all prior):
127
+ * 1. Bridge (emergency continuity route).
128
+ * 2. Configured primary, when the live context drifted from it.
129
+ * 3. The selected chain (explicit refs → named profile → smart default),
130
+ * via {@link resolveEffective}.
131
+ * 4. The "default" profile — extra depth, ONLY when the chain was auto-derived.
132
+ * 5. Every other configured provider — last resort, ONLY when the chain was
133
+ * auto-derived AND `effectiveFallbackAuto` is true.
134
+ *
135
+ * Returns the empty chain when `closedWorld` is true and no explicit
136
+ * refs/profile resolved — a model allowlist never leaks to unlisted models.
137
+ *
138
+ * @internal caller-aware options (primary, closedWorld) are accepted because
139
+ * the agent loop has context the manager does not own; the resolution
140
+ * pipeline itself is identical for both callers.
141
+ */
142
+ resolveCandidates(current: {
143
+ providerId: string;
144
+ model: string;
145
+ }, opts?: {
146
+ fallbackModels?: readonly string[] | undefined;
147
+ fallbackProfile?: string | undefined;
148
+ fallbackAuto?: boolean | undefined;
149
+ primary?: {
150
+ providerId: string;
151
+ model: string;
152
+ } | undefined;
153
+ closedWorld?: boolean | undefined;
154
+ }): FallbackChain;
109
155
  checkProvider(providerId: string): ProviderHealth;
110
156
  /**
111
157
  * Atomically replace the active immutable snapshot while preserving this
@@ -127,4 +173,26 @@ export declare class FallbackProfileManager {
127
173
  */
128
174
  private smartDefault;
129
175
  }
176
+ /**
177
+ * Maximum number of candidates the last-resort append ({@link FallbackProfileManager.resolveAllConfigured})
178
+ * may produce. By the time this append fires, the smart default, the bridge, and the
179
+ * default profile have all failed — in a systemic outage every additional entry is a
180
+ * doomed request. The cap bounds the blast radius without starving legitimate diversity
181
+ * (3× the smart-default depth of 4). Exposed for tests and for callers that want to
182
+ * surface it in config tooling.
183
+ */
184
+ export declare const MAX_LAST_RESORT_CANDIDATES = 12;
185
+ /**
186
+ * Deduplicate fallback chain entries by `providerId/model`, dropping the
187
+ * current (already-active) model and any repeat. Preserves first-occurrence
188
+ * order so the layering produced by {@link FallbackProfileManager.resolveCandidates}
189
+ * determines which entry wins when the same model appears at multiple depths.
190
+ *
191
+ * Shared by both the agent-loop and one-shot paths so dedup semantics can never
192
+ * drift between them.
193
+ */
194
+ export declare function dedupeChain(entries: readonly FallbackChainEntry[], current: {
195
+ providerId: string;
196
+ model: string;
197
+ }): FallbackChain;
130
198
  //# sourceMappingURL=fallback-profile-manager.d.ts.map