@wrongstack/core 0.308.5 → 0.308.7

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.
@@ -75,12 +75,12 @@ export declare function makeMailSendTool(opts?: MailToolsOptions): {
75
75
  required: string[];
76
76
  };
77
77
  execute(input: unknown, ctx: Context): Promise<{
78
- ok: boolean;
79
- error: string;
80
78
  messageId?: never;
81
- from?: never;
82
79
  to?: never;
83
80
  summary?: never;
81
+ ok: boolean;
82
+ error: string;
83
+ from?: never;
84
84
  } | {
85
85
  error?: never;
86
86
  ok: boolean;
@@ -2029,6 +2029,7 @@ function inferRuntimeCapabilities(toolNames) {
2029
2029
  var skillSet = (...names) => names;
2030
2030
  var ROLE_SKILL_SETS = {
2031
2031
  explore: skillSet("research-web", "node-modern", "typescript-strict"),
2032
+ "explore-companion": skillSet("node-modern", "typescript-strict"),
2032
2033
  search: skillSet("bug-hunter", "typescript-strict", "research-web"),
2033
2034
  research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
2034
2035
  analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
@@ -5417,6 +5418,28 @@ var SHADOW_AGENT = {
5417
5418
  ...defineAgent("shadow-agent", "Shadow"),
5418
5419
  skillNames: [...SHADOW_AGENT_SKILLS]
5419
5420
  };
5421
+ var EXPLORE_COMPANION_AGENT = {
5422
+ ...defineAgent("explore-companion", "Explore Companion"),
5423
+ tools: [...TOOLS.read, ...TOOLS.index],
5424
+ // Read-only, triple-enforced: allowlist has no write/bash, and the
5425
+ // disabled list blocks the escape hatches explicitly.
5426
+ disabledTools: [
5427
+ "write",
5428
+ "edit",
5429
+ "replace",
5430
+ "patch",
5431
+ "bash",
5432
+ "exec",
5433
+ "delegate",
5434
+ "spawn_subagent",
5435
+ "assign_task"
5436
+ ],
5437
+ skillNames: [...ROLE_SKILL_SETS["explore-companion"]],
5438
+ spawnBudgetExempt: true,
5439
+ // Findings travel via mailbox + submit_result, not the leader's stream.
5440
+ textStream: "silent",
5441
+ toolStream: "silent"
5442
+ };
5420
5443
  var CRITIC_AGENT = defineAgent("critic", "Critic");
5421
5444
  var GENERIC_AGENT = defineAgent("generic", "Generic Project Agent");
5422
5445
  function withDispatchMetadata(definition) {
@@ -5435,6 +5458,7 @@ var FLEET_ROSTER = {
5435
5458
  critic: CRITIC_AGENT,
5436
5459
  generic: GENERIC_AGENT,
5437
5460
  "shadow-agent": SHADOW_AGENT,
5461
+ "explore-companion": EXPLORE_COMPANION_AGENT,
5438
5462
  ...Object.fromEntries(
5439
5463
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, withDispatchMetadata(d)])
5440
5464
  )
@@ -5458,6 +5482,13 @@ var FLEET_ROSTER_BUDGETS = {
5458
5482
  maxTokens: 96e3,
5459
5483
  maxCostUsd: 0.5
5460
5484
  },
5485
+ "explore-companion": {
5486
+ idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
5487
+ maxIterations: 3e3,
5488
+ maxToolCalls: 8e3,
5489
+ maxTokens: 96e3,
5490
+ maxCostUsd: 0.5
5491
+ },
5461
5492
  ...Object.fromEntries(
5462
5493
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
5463
5494
  )
@@ -8307,7 +8338,7 @@ function buildKanbanFleetTaskPrompt(board, task, lease) {
8307
8338
  const dependencyLines = (task.dependsOn ?? []).map((depId) => board.tasks.find((candidate) => candidate.id === depId)).filter((dep) => Boolean(dep)).map((dep) => `- ${dep.title} [${dep.status}] (${dep.id})`);
8308
8339
  const checks = task.successCriteria?.map((check) => `- ${check.description}`).join("\n");
8309
8340
  const metrics = task.goalMetrics?.map(
8310
- (metric) => `- ${metric.name}: ${metric.current ?? "n/a"}${metric.target !== void 0 ? ` / ${metric.target}` : ""}${metric.unit ? ` ${metric.unit}` : ""} [${metric.status}]`
8341
+ (metric) => `- ${metric.name}: ${metric.current ?? "n/a"}${metric.target !== void 0 ? ` / ${metric.direction === "at_most" ? "\u2264" : "\u2265"} ${metric.target}` : ""}${metric.unit ? ` ${metric.unit}` : ""} [${metric.status}]`
8311
8342
  ).join("\n");
8312
8343
  const chain = task.chain ? [
8313
8344
  `chainId: ${task.chain.chainId}`,
@@ -1,6 +1,6 @@
1
1
  import { estimateMessageTokens } from '../utils/token-estimate.js';
2
2
  export { buildSmartDigest, type ContentScore, extractText, hasLargeToolResult, hasToolUse, scoreMessage, } from './compaction-scoring.js';
3
- export { type AcknowledgedToolReceiptCollapse, type AcknowledgedToolResultElision, analyzeFileToolLifecycle, collapseAcknowledgedToolReceipts, compactionDebugEnabled, type CompactionMetrics, didFileMutationRun, eliseAcknowledgedToolResults, eliseOldToolResults, type EliseResult, emitCompactionMetrics, extractPathHints, type FileToolLifecycle, findPreserveStart, firstErrorLine, isElidedResultContent, isElidedToolInput, isFileMutationToolName, isReadToolName, normalizePathKey, readPathOf, safeToolResultString, sameFilePath, setCompactionDebugLogger, summarizeToolResultElision, summarizeToolUseInputElision, } from './compaction-elision.js';
3
+ export { collapseAcknowledgedToolReceipts, eliseAcknowledgedToolResults, eliseOldToolResults, type EliseResult, findPreserveStart, isElidedResultContent, isElidedToolInput, normalizePathKey, readPathOf, setCompactionDebugLogger, summarizeToolResultElision, summarizeToolUseInputElision, } from './compaction-elision.js';
4
4
  export { buildLosslessDigest, type DedupResult, dedupStaleReads, elideMessageToolIo, enforceHardBudget, findExchangeStart, findSafeBoundary, type HardBudgetResult, hasTextContent, headTailTruncate, truncateMessageText, } from './compaction-budget.js';
5
5
  export { CompactionSummaryCache, type CompactionSummaryCacheOptions, compactionSummaryKey, isPlaceholderSummary, PLACEHOLDER_SUMMARIES, } from './compaction-summary-cache.js';
6
6
  export declare const estimateMessages: typeof estimateMessageTokens;
@@ -11,9 +11,7 @@ export interface CompactionMetrics {
11
11
  tokensSaved: number;
12
12
  changed: boolean;
13
13
  }
14
- export declare function compactionDebugEnabled(): boolean;
15
14
  export declare function setCompactionDebugLogger(logger: Logger | undefined): void;
16
- export declare function emitCompactionMetrics(event: string, metrics: CompactionMetrics): void;
17
15
  export interface EliseResult {
18
16
  messages: Message[];
19
17
  saved: number;
@@ -30,13 +28,8 @@ export interface FileToolLifecycle {
30
28
  activeReadIds: Set<string>;
31
29
  staleReadPaths: Map<string, string>;
32
30
  }
33
- export declare function isReadToolName(name: string): boolean;
34
- export declare function isFileMutationToolName(name: string): boolean;
35
- export declare function didFileMutationRun(use: ToolUseBlock): boolean;
36
- export declare function sameFilePath(a: string, b: string): boolean;
37
31
  export declare function readPathOf(input: Record<string, unknown> | undefined): string | undefined;
38
32
  export declare function normalizePathKey(p: string): string;
39
- export declare function analyzeFileToolLifecycle(messages: readonly Message[], acknowledgedBefore: number): FileToolLifecycle;
40
33
  export declare function isElidedResultContent(content: string): boolean;
41
34
  export declare function isElidedToolInput(input: Record<string, unknown> | undefined): boolean;
42
35
  export declare function eliseAcknowledgedToolResults(messages: readonly Message[], opts: {
@@ -52,7 +45,4 @@ export declare function eliseOldToolResults(messages: readonly Message[], opts:
52
45
  }, findPreserveStartFn?: (messages: readonly Message[], preserveK: number) => number): EliseResult;
53
46
  export declare function summarizeToolUseInputElision(block: ToolUseBlock, tokens: number): Record<string, unknown>;
54
47
  export declare function summarizeToolResultElision(block: ToolResultBlock, tokens: number): string;
55
- export declare function safeToolResultString(content: unknown): string;
56
- export declare function extractPathHints(content: unknown): string[];
57
- export declare function firstErrorLine(content: unknown): string | undefined;
58
48
  //# sourceMappingURL=compaction-elision.d.ts.map
@@ -13481,6 +13481,7 @@ function inferRuntimeCapabilities(toolNames) {
13481
13481
  var skillSet = (...names) => names;
13482
13482
  var ROLE_SKILL_SETS = {
13483
13483
  explore: skillSet("research-web", "node-modern", "typescript-strict"),
13484
+ "explore-companion": skillSet("node-modern", "typescript-strict"),
13484
13485
  search: skillSet("bug-hunter", "typescript-strict", "research-web"),
13485
13486
  research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
13486
13487
  analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
@@ -16937,6 +16938,28 @@ var SHADOW_AGENT = {
16937
16938
  ...defineAgent("shadow-agent", "Shadow"),
16938
16939
  skillNames: [...SHADOW_AGENT_SKILLS]
16939
16940
  };
16941
+ var EXPLORE_COMPANION_AGENT = {
16942
+ ...defineAgent("explore-companion", "Explore Companion"),
16943
+ tools: [...TOOLS.read, ...TOOLS.index],
16944
+ // Read-only, triple-enforced: allowlist has no write/bash, and the
16945
+ // disabled list blocks the escape hatches explicitly.
16946
+ disabledTools: [
16947
+ "write",
16948
+ "edit",
16949
+ "replace",
16950
+ "patch",
16951
+ "bash",
16952
+ "exec",
16953
+ "delegate",
16954
+ "spawn_subagent",
16955
+ "assign_task"
16956
+ ],
16957
+ skillNames: [...ROLE_SKILL_SETS["explore-companion"]],
16958
+ spawnBudgetExempt: true,
16959
+ // Findings travel via mailbox + submit_result, not the leader's stream.
16960
+ textStream: "silent",
16961
+ toolStream: "silent"
16962
+ };
16940
16963
  var CRITIC_AGENT = defineAgent("critic", "Critic");
16941
16964
  var GENERIC_AGENT = defineAgent("generic", "Generic Project Agent");
16942
16965
  function withDispatchMetadata(definition) {
@@ -16955,6 +16978,7 @@ var FLEET_ROSTER = {
16955
16978
  critic: CRITIC_AGENT,
16956
16979
  generic: GENERIC_AGENT,
16957
16980
  "shadow-agent": SHADOW_AGENT,
16981
+ "explore-companion": EXPLORE_COMPANION_AGENT,
16958
16982
  ...Object.fromEntries(
16959
16983
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, withDispatchMetadata(d)])
16960
16984
  )
@@ -16978,6 +17002,13 @@ var FLEET_ROSTER_BUDGETS = {
16978
17002
  maxTokens: 96e3,
16979
17003
  maxCostUsd: 0.5
16980
17004
  },
17005
+ "explore-companion": {
17006
+ idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
17007
+ maxIterations: 3e3,
17008
+ maxToolCalls: 8e3,
17009
+ maxTokens: 96e3,
17010
+ maxCostUsd: 0.5
17011
+ },
16981
17012
  ...Object.fromEntries(
16982
17013
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
16983
17014
  )
@@ -1427,6 +1427,13 @@ var PhaseOrchestrator = class {
1427
1427
  events;
1428
1428
  stopped = false;
1429
1429
  paused = false;
1430
+ /**
1431
+ * Run-wide abort source. stop() aborts it; every in-flight
1432
+ * ctx.executeTask call observes the abort through its per-task signal
1433
+ * (composed from this controller and the task's own timeout controller).
1434
+ * Recreated by start() so a stopped orchestrator can be reused.
1435
+ */
1436
+ stopController = new AbortController();
1430
1437
  runningPhases = /* @__PURE__ */ new Set();
1431
1438
  tickInterval = null;
1432
1439
  trackerCache = /* @__PURE__ */ new Map();
@@ -1469,6 +1476,7 @@ var PhaseOrchestrator = class {
1469
1476
  async start() {
1470
1477
  this.stopped = false;
1471
1478
  this.paused = false;
1479
+ this.stopController = new AbortController();
1472
1480
  this.normalizeForResume();
1473
1481
  this.graph.startedAt = Date.now();
1474
1482
  this.graph.updatedAt = Date.now();
@@ -1528,6 +1536,7 @@ var PhaseOrchestrator = class {
1528
1536
  /** Stop completely, including active phases. */
1529
1537
  stop() {
1530
1538
  this.stopped = true;
1539
+ this.stopController.abort();
1531
1540
  if (this.tickInterval) {
1532
1541
  clearInterval(this.tickInterval);
1533
1542
  this.tickInterval = null;
@@ -1604,6 +1613,7 @@ var PhaseOrchestrator = class {
1604
1613
  return;
1605
1614
  }
1606
1615
  await this.executePhaseTasks(phase);
1616
+ if (this.stopped) return;
1607
1617
  const failedTasks = this.getFailedTaskCount(phase);
1608
1618
  const completedTasks = this.getCompletedTaskCount(phase);
1609
1619
  this.emit("phase.allTasksDone", {
@@ -1763,34 +1773,47 @@ var PhaseOrchestrator = class {
1763
1773
  agentName: task.assignee
1764
1774
  });
1765
1775
  const handle = this.phaseWorktrees.get(phase.id);
1766
- const taskPromise = this.ctx.executeTask(task, phase.id, {
1767
- cwd: handle?.dir,
1768
- branch: handle?.branch
1776
+ const timeoutController = this.opts.taskTimeoutMs > 0 ? new AbortController() : void 0;
1777
+ const signal = timeoutController ? AbortSignal.any([this.stopController.signal, timeoutController.signal]) : this.stopController.signal;
1778
+ const taskPromise = this.ctx.executeTask(
1779
+ task,
1780
+ phase.id,
1781
+ { cwd: handle?.dir, branch: handle?.branch },
1782
+ signal
1783
+ );
1784
+ if (!timeoutController) return taskPromise;
1785
+ const timeoutMs = this.opts.taskTimeoutMs;
1786
+ const timedOut = /* @__PURE__ */ Symbol("timed_out");
1787
+ const result = await Promise.race([
1788
+ taskPromise,
1789
+ new Promise((resolve4) => {
1790
+ const timer = setTimeout(() => {
1791
+ timeoutController.abort();
1792
+ resolve4(timedOut);
1793
+ }, timeoutMs);
1794
+ taskPromise.then(() => clearTimeout(timer)).catch(() => clearTimeout(timer));
1795
+ })
1796
+ ]);
1797
+ if (result !== timedOut) return result;
1798
+ this.emit("phase.taskTimedOut", {
1799
+ phaseId: phase.id,
1800
+ taskId: task.id,
1801
+ taskTitle: task.title,
1802
+ timeoutMs
1769
1803
  });
1770
- if (this.opts.taskTimeoutMs > 0) {
1771
- const timeoutMs = this.opts.taskTimeoutMs;
1772
- const timedOut = /* @__PURE__ */ Symbol("timed_out");
1773
- const result = await Promise.race([
1774
- taskPromise,
1775
- new Promise((resolve4) => {
1776
- const timer = setTimeout(() => resolve4(timedOut), timeoutMs);
1777
- taskPromise.then(() => clearTimeout(timer)).catch(() => clearTimeout(timer));
1778
- })
1779
- ]);
1780
- if (result === timedOut) {
1781
- this.emit("phase.taskTimedOut", {
1782
- phaseId: phase.id,
1783
- taskId: task.id,
1784
- taskTitle: task.title,
1785
- timeoutMs
1786
- });
1787
- throw new Error(
1788
- `Task "${task.title}" (${task.id}) exceeded timeout of ${timeoutMs} ms`
1789
- );
1790
- }
1791
- return result;
1792
- }
1793
- return taskPromise;
1804
+ const settled = taskPromise.then(
1805
+ () => void 0,
1806
+ () => void 0
1807
+ );
1808
+ const grace = new Promise((resolve4) => {
1809
+ const timer = setTimeout(resolve4, 5e3);
1810
+ timer.unref?.();
1811
+ void settled.then(() => clearTimeout(timer));
1812
+ });
1813
+ await Promise.race([settled, grace]);
1814
+ throw new Error(
1815
+ `Task "${task.title}" (${task.id}) exceeded timeout of ${timeoutMs} ms`
1816
+ );
1794
1817
  }
1795
1818
  markTaskCompleted(phase, task) {
1796
1819
  const tracker = this.getTrackerForPhase(phase);
@@ -1805,6 +1828,10 @@ var PhaseOrchestrator = class {
1805
1828
  const tracker = this.getTrackerForPhase(phase);
1806
1829
  const taskKey = `${phase.id}:${task.id}`;
1807
1830
  const currentRetries = this.taskRetryCounts.get(taskKey) ?? 0;
1831
+ if (this.stopped) {
1832
+ tracker.updateNodeStatus(task.id, "pending", "Stopped before completion");
1833
+ return;
1834
+ }
1808
1835
  if (currentRetries < this.opts.maxRetries) {
1809
1836
  this.taskRetryCounts.set(taskKey, currentRetries + 1);
1810
1837
  tracker.updateNodeStatus(
@@ -19,6 +19,13 @@ export declare class PhaseOrchestrator {
19
19
  private events;
20
20
  private stopped;
21
21
  private paused;
22
+ /**
23
+ * Run-wide abort source. stop() aborts it; every in-flight
24
+ * ctx.executeTask call observes the abort through its per-task signal
25
+ * (composed from this controller and the task's own timeout controller).
26
+ * Recreated by start() so a stopped orchestrator can be reused.
27
+ */
28
+ private stopController;
22
29
  private runningPhases;
23
30
  private tickInterval;
24
31
  private trackerCache;
@@ -223,7 +223,7 @@ export interface PhaseExecutionContext {
223
223
  executeTask: (task: TaskNode, phaseId: string, env?: {
224
224
  cwd?: string | undefined;
225
225
  branch?: string | undefined;
226
- }) => Promise<unknown>;
226
+ }, signal?: AbortSignal | undefined) => Promise<unknown>;
227
227
  /**
228
228
  * Optional verification gate. Called after all tasks in a phase finish,
229
229
  * but before the phase is marked "completed" and its worktree is merged