@wrongstack/core 0.272.2 → 0.273.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 (71) hide show
  1. package/dist/{agent-bridge-DFQYEeXf.d.ts → agent-bridge-DpKIxHhE.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-BZa_IEcd.d.ts → agent-subagent-runner-Dx7fZ1bE.d.ts} +14 -7
  3. package/dist/{brain-etbcbRwV.d.ts → brain-BDcQaku-.d.ts} +112 -5
  4. package/dist/{compactor-72ug-ZRB.d.ts → compactor-BuSdj3fq.d.ts} +1 -1
  5. package/dist/{config-rRS8yorV.d.ts → config-CR2yoG8c.d.ts} +61 -4
  6. package/dist/{context-Dw55zZ_Q.d.ts → context-DulAr8Zo.d.ts} +24 -0
  7. package/dist/coordination/index.d.ts +23 -16
  8. package/dist/coordination/index.js +192 -38
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/{default-config-B0cj-Hry.d.ts → default-config-BbX4ojZs.d.ts} +1 -0
  11. package/dist/defaults/index.d.ts +29 -28
  12. package/dist/defaults/index.js +3238 -234
  13. package/dist/defaults/index.js.map +1 -1
  14. package/dist/execution/index.d.ts +16 -16
  15. package/dist/execution/index.js +83 -3
  16. package/dist/execution/index.js.map +1 -1
  17. package/dist/execution/prompt-enhancer.d.ts +1 -1
  18. package/dist/extension/index.d.ts +6 -6
  19. package/dist/{global-mailbox-DJ4EoRr0.d.ts → global-mailbox-CwcubDkA.d.ts} +1 -1
  20. package/dist/{goal-preamble-hM8BH7TK.d.ts → goal-preamble-Bu0a2uCG.d.ts} +10 -10
  21. package/dist/{goal-store-CWlbT0TO.d.ts → goal-store-CTmFuZ8J.d.ts} +1 -1
  22. package/dist/hq/index.d.ts +5 -5
  23. package/dist/hq/index.js +1 -0
  24. package/dist/hq/index.js.map +1 -1
  25. package/dist/{index-DWm_PE9L.d.ts → index-CTq5wU3m.d.ts} +14 -6
  26. package/dist/{index-2Lhk5v0o.d.ts → index-CxP-HBhX.d.ts} +8 -2
  27. package/dist/index.d.ts +230 -48
  28. package/dist/index.js +3731 -648
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/kernel/index.d.ts +94 -14
  32. package/dist/kernel/index.js.map +1 -1
  33. package/dist/{mcp-servers-BpWHTKlE.d.ts → mcp-servers-BQaOE71z.d.ts} +3 -3
  34. package/dist/models/index.d.ts +5 -5
  35. package/dist/{models-registry-CXQFUn5t.d.ts → models-registry-BEcny4kP.d.ts} +1 -1
  36. package/dist/{multi-agent-coordinator-jyimfo7D.d.ts → multi-agent-coordinator-Bx8EFkv2.d.ts} +1 -1
  37. package/dist/{null-fleet-bus-DOGQcvrY.d.ts → null-fleet-bus-BC5ZXCQw.d.ts} +16 -6
  38. package/dist/observability/index.d.ts +2 -2
  39. package/dist/{parallel-eternal-engine-rItJBYp9.d.ts → parallel-eternal-engine-C345TI3n.d.ts} +10 -9
  40. package/dist/{path-resolver-DrpF5MGK.d.ts → path-resolver-C-W_wzkF.d.ts} +3 -3
  41. package/dist/{permission-CC7XFYWG.d.ts → permission-CsBGZkxp.d.ts} +1 -1
  42. package/dist/{permission-policy-cYR4RJmw.d.ts → permission-policy-g3Sg0GdZ.d.ts} +2 -2
  43. package/dist/{pipeline-Ckkn3AOA.d.ts → pipeline-xnw_24Z8.d.ts} +2 -2
  44. package/dist/{plan-templates-BvHw5Znw.d.ts → plan-templates-DGaiYEcS.d.ts} +32 -6
  45. package/dist/{provider-model-resolve-nZqnCeaR.d.ts → provider-model-resolve-Cz6OlIOp.d.ts} +3 -3
  46. package/dist/{provider-runner-zVOn1p67.d.ts → provider-runner-7J0HqF6B.d.ts} +3 -3
  47. package/dist/{retry-policy-BV7nzeAd.d.ts → retry-policy-kqXJOVkX.d.ts} +1 -1
  48. package/dist/sdd/index.d.ts +1114 -14
  49. package/dist/sdd/index.js +5516 -2949
  50. package/dist/sdd/index.js.map +1 -1
  51. package/dist/{secret-vault-eMBKfheR.d.ts → secret-vault-CMQUr-eB.d.ts} +1 -1
  52. package/dist/security/index.d.ts +5 -5
  53. package/dist/security/index.js +6 -0
  54. package/dist/security/index.js.map +1 -1
  55. package/dist/{selector-C4ORTOid.d.ts → selector-B4r34PWR.d.ts} +1 -1
  56. package/dist/{session-event-bridge-CeNpUL9w.d.ts → session-event-bridge-BD3LoyLC.d.ts} +1 -1
  57. package/dist/{session-reader-BepLSnGL.d.ts → session-reader-DjrKGD9c.d.ts} +1 -1
  58. package/dist/storage/index.d.ts +12 -12
  59. package/dist/storage/index.js +380 -13
  60. package/dist/storage/index.js.map +1 -1
  61. package/dist/tools/index.d.ts +2 -2
  62. package/dist/tools/index.js.map +1 -1
  63. package/dist/types/index.d.ts +20 -20
  64. package/dist/types/index.js +31 -0
  65. package/dist/types/index.js.map +1 -1
  66. package/dist/utils/index.d.ts +30 -4
  67. package/dist/utils/index.js +110 -1
  68. package/dist/utils/index.js.map +1 -1
  69. package/dist/{index-DqW4o62H.d.ts → worktree-manager-DHdrWQ_7.d.ts} +48 -90
  70. package/dist/{wstack-paths-hOpNLmvf.d.ts → wstack-paths-BqkDAkoh.d.ts} +2 -0
  71. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { M as Message } from './context-Dw55zZ_Q.js';
1
+ import { M as Message } from './context-DulAr8Zo.js';
2
2
 
3
3
  /**
4
4
  * Result of LLM-driven message importance analysis.
@@ -1,4 +1,4 @@
1
- import { S as SessionEvent, b as SessionWriter } from './context-Dw55zZ_Q.js';
1
+ import { S as SessionEvent, b as SessionWriter } from './context-DulAr8Zo.js';
2
2
 
3
3
  type AuditLevel = 'minimal' | 'standard' | 'full';
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { i as ContentBlock, S as SessionEvent, j as SessionMetadata, k as SessionStore } from './context-Dw55zZ_Q.js';
1
+ import { i as ContentBlock, S as SessionEvent, j as SessionMetadata, k as SessionStore } from './context-DulAr8Zo.js';
2
2
 
3
3
  type AttachmentKind = 'text' | 'image' | 'file';
4
4
  interface AttachmentMeta {
@@ -1,20 +1,20 @@
1
- import { M as MemoryBackend, j as FileMemoryBackendOptions } from '../plan-templates-BvHw5Znw.js';
2
- export { A as AbandonedSession, a as AttachmentStoreOptions, C as ConfigLoaderOptions, b as ConfigMigration, c as ConfigMigrationError, d as ConfigSource, D as DEFAULT_CONFIG_MIGRATIONS, e as DefaultAttachmentStore, f as DefaultConfigLoader, g as DefaultConfigStore, h as DefaultMemoryStore, i as DefaultSessionStore, F as FileMemoryBackend, k as MemoryStoreOptions, l as MigrationContext, m as MigrationResult, P as PersistedQueueItem, n as PlanFile, o as PlanItem, p as PlanTemplate, Q as QueueStore, R as RecoveryLock, q as RecoveryLockOptions, S as SessionAnalyzer, r as SessionStoreOptions, T as TodosCheckpointFile, s as addPlanItem, t as attachPlanCheckpoint, u as attachTodosCheckpoint, v as clearPlan, w as deriveTodosFromPlanItem, x as emptyPlan, y as formatPlan, z as formatPlanTemplates, B as getPlanTemplate, E as listPlanTemplates, G as loadPlan, H as loadTodosCheckpoint, I as mutatePlan, J as parseEntries, K as removePlanItem, L as runConfigMigrations, N as savePlan, O as saveTodosCheckpoint, U as setPlanItemStatus } from '../plan-templates-BvHw5Znw.js';
3
- import { M as MemoryScope, b as MemoryEntry, c as MemoryStore, E as EventBus } from '../brain-etbcbRwV.js';
4
- import { l as AgentExtension, j as AfterRunHook } from '../index-DWm_PE9L.js';
5
- import { P as Provider, c as Request, d as Response, S as SessionEvent } from '../context-Dw55zZ_Q.js';
6
- export { D as DefaultSessionReader, f as DefaultSessionReaderOptions, S as SessionReader } from '../session-reader-BepLSnGL.js';
1
+ import { M as MemoryBackend, j as FileMemoryBackendOptions } from '../plan-templates-DGaiYEcS.js';
2
+ export { A as AbandonedSession, a as AttachmentStoreOptions, C as ConfigLoaderOptions, b as ConfigMigration, c as ConfigMigrationError, d as ConfigSource, D as DEFAULT_CONFIG_MIGRATIONS, e as DefaultAttachmentStore, f as DefaultConfigLoader, g as DefaultConfigStore, h as DefaultMemoryStore, i as DefaultSessionStore, F as FileMemoryBackend, k as MemoryStoreOptions, l as MigrationContext, m as MigrationResult, P as PersistedQueueItem, n as PlanFile, o as PlanItem, p as PlanTemplate, Q as QueueStore, R as RecoveryLock, q as RecoveryLockOptions, S as SessionAnalyzer, r as SessionStoreOptions, T as TodosCheckpointFile, s as addPlanItem, t as attachPlanCheckpoint, u as attachTodosCheckpoint, v as clearPlan, w as deriveTodosFromPlanItem, x as emptyPlan, y as formatPlan, z as formatPlanTemplates, B as getPlanTemplate, E as listPlanTemplates, G as loadPlan, H as loadTodosCheckpoint, I as mutatePlan, J as parseEntries, K as removePlanItem, L as runConfigMigrations, N as savePlan, O as saveTodosCheckpoint, U as setPlanItemStatus } from '../plan-templates-DGaiYEcS.js';
3
+ import { M as MemoryScope, b as MemoryEntry, c as MemoryStore, E as EventBus } from '../brain-BDcQaku-.js';
4
+ import { i as AgentExtension, k as AfterRunHook } from '../index-CTq5wU3m.js';
5
+ import { P as Provider, c as Request, d as Response, S as SessionEvent } from '../context-DulAr8Zo.js';
6
+ export { D as DefaultSessionReader, f as DefaultSessionReaderOptions, S as SessionReader } from '../session-reader-DjrKGD9c.js';
7
7
  import { S as SessionRewinder, C as CheckpointInfo, a as RewindResultExtended } from '../session-rewinder-C9HnMkhP.js';
8
8
  import { T as TaskItem } from '../task-format-vGOIftmK.js';
9
9
  export { a as DirectorStateCheckpoint, D as DirectorStateSnapshot, b as DirectorSubagentState, c as DirectorTaskState, l as loadDirectorState } from '../director-state-BfeCUbmk.js';
10
- export { G as GoalFile, J as JournalEntry, M as MAX_JOURNAL_ENTRIES, a as MAX_PROGRESS_HISTORY, P as ProgressSnapshot, b as appendJournal, e as emptyGoal, f as formatGoal, g as goalFilePath, l as loadGoal, p as parseProgressFromText, r as recordProgress, s as saveGoal, c as setProgress, d as summarizeUsage } from '../goal-store-CWlbT0TO.js';
11
- import { W as WstackPaths } from '../wstack-paths-hOpNLmvf.js';
12
- import { X as SyncCategory, l as SyncConfig } from '../config-rRS8yorV.js';
13
- export { A as AuditLevel, C as CORE_RECONSTRUCT_EVENTS, a as STANDARD_AUDIT_EVENTS, S as SessionEventBridge, b as SessionEventBridgeOptions, c as SessionSamplingOptions, T as ToolProgressSamplingOptions, d as createSessionEventBridge, r as resolveAuditLevel, e as resolveSessionLoggingConfig } from '../session-event-bridge-CeNpUL9w.js';
14
- import '../permission-CC7XFYWG.js';
10
+ export { G as GoalFile, J as JournalEntry, M as MAX_JOURNAL_ENTRIES, a as MAX_PROGRESS_HISTORY, P as ProgressSnapshot, b as appendJournal, e as emptyGoal, f as formatGoal, g as goalFilePath, l as loadGoal, p as parseProgressFromText, r as recordProgress, s as saveGoal, c as setProgress, d as summarizeUsage } from '../goal-store-CTmFuZ8J.js';
11
+ import { W as WstackPaths } from '../wstack-paths-BqkDAkoh.js';
12
+ import { Y as SyncCategory, l as SyncConfig } from '../config-CR2yoG8c.js';
13
+ export { A as AuditLevel, C as CORE_RECONSTRUCT_EVENTS, a as STANDARD_AUDIT_EVENTS, S as SessionEventBridge, b as SessionEventBridgeOptions, c as SessionSamplingOptions, T as ToolProgressSamplingOptions, d as createSessionEventBridge, r as resolveAuditLevel, e as resolveSessionLoggingConfig } from '../session-event-bridge-BD3LoyLC.js';
14
+ import '../permission-CsBGZkxp.js';
15
15
  import '../secret-vault-BAKpgFw_.js';
16
16
  import '../logger-B63L5bTg.js';
17
- import '../pipeline-Ckkn3AOA.js';
17
+ import '../pipeline-xnw_24Z8.js';
18
18
  import '../mailbox-types-Ct2hJq0P.js';
19
19
  import '../observability-D-HZN_mF.js';
20
20
  import '../task-graph-u1q9Jkyk.js';
@@ -427,6 +427,7 @@ function resolveWstackPaths(opts) {
427
427
  projectSddSession: path2.join(projectDir, "sdd-session.json"),
428
428
  projectPlan: path2.join(projectDir, "plan.json"),
429
429
  projectAutophase: path2.join(projectDir, "autophase"),
430
+ projectSddBoards: path2.join(projectDir, "sdd-boards"),
430
431
  syncConfig: path2.join(globalRoot, "sync.json"),
431
432
  projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
432
433
  };
@@ -745,6 +746,91 @@ var DefaultSessionStore = class _DefaultSessionStore {
745
746
  }
746
747
  }
747
748
  }
749
+ /**
750
+ * Streaming search over a session's JSONL. Walks the file once, parses
751
+ * each event lazily, and yields only the events that match `predicate`.
752
+ * Stops as soon as `opts.limit` matches are collected.
753
+ *
754
+ * Why this exists: `load()` parses the entire file into memory and
755
+ * rebuilds `messages`/`toolCallEnds` for every caller. `search()` only
756
+ * needs to know which events contain matching text — a per-line
757
+ * predicate is enough. The full parse work (and the `_loadCache` poll)
758
+ * is wasted in that case.
759
+ *
760
+ * Memory: O(hits) regardless of file size. Disk: one linear scan,
761
+ * terminated at `limit` if the caller asked for one.
762
+ *
763
+ * Errors: missing file yields []. Corrupt lines are skipped (same
764
+ * policy as `load()`). Aborting via `signal` rejects with `AbortError`.
765
+ */
766
+ async searchEvents(id, predicate, opts) {
767
+ const file = this.sessionPath(id, ".jsonl");
768
+ const limit = opts?.limit;
769
+ const signal = opts?.signal;
770
+ const out = [];
771
+ let stat7;
772
+ try {
773
+ stat7 = await fsp.stat(file);
774
+ } catch (err) {
775
+ if (err.code === "ENOENT") return [];
776
+ throw err;
777
+ }
778
+ if (stat7.size === 0) return [];
779
+ let fh;
780
+ try {
781
+ fh = await fsp.open(file, "r");
782
+ const CHUNK = 64 * 1024;
783
+ const buf = Buffer.alloc(CHUNK);
784
+ let leftover = "";
785
+ let eventIndex = 0;
786
+ for (let position = 0; ; position += buf.byteLength) {
787
+ if (signal?.aborted) {
788
+ const reason = signal.reason ?? new DOMException("Aborted", "AbortError");
789
+ throw reason;
790
+ }
791
+ const { bytesRead } = await fh.read(buf, 0, CHUNK, position);
792
+ if (bytesRead === 0) break;
793
+ const text = leftover + buf.subarray(0, bytesRead).toString("utf8");
794
+ const parts = text.split("\n");
795
+ leftover = parts.pop() ?? "";
796
+ for (const line of parts) {
797
+ if (!line) continue;
798
+ let ev;
799
+ try {
800
+ const parsed = JSON.parse(line);
801
+ if (parsed === null || typeof parsed !== "object" || typeof parsed.type !== "string" || typeof parsed.ts !== "string") {
802
+ continue;
803
+ }
804
+ ev = parsed;
805
+ } catch {
806
+ continue;
807
+ }
808
+ if (predicate(ev, eventIndex, ev.ts)) {
809
+ out.push({ event: ev, eventIndex, ts: ev.ts });
810
+ if (limit !== void 0 && out.length >= limit) {
811
+ return out;
812
+ }
813
+ }
814
+ eventIndex++;
815
+ }
816
+ }
817
+ if (leftover.trim()) {
818
+ try {
819
+ const parsed = JSON.parse(leftover);
820
+ if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
821
+ const ev = parsed;
822
+ if (predicate(ev, eventIndex, ev.ts)) {
823
+ out.push({ event: ev, eventIndex, ts: ev.ts });
824
+ }
825
+ }
826
+ } catch {
827
+ }
828
+ }
829
+ return out;
830
+ } finally {
831
+ if (fh) await fh.close().catch(() => void 0);
832
+ }
833
+ }
748
834
  async list(limit = 20) {
749
835
  try {
750
836
  await ensureDir(this.dir);
@@ -1777,7 +1863,6 @@ var FileSessionWriter = class _FileSessionWriter {
1777
1863
  }
1778
1864
  const writeFd = await fsp.open(tmpPath, "w", 384);
1779
1865
  try {
1780
- let copied = 0;
1781
1866
  let readOffset = 0;
1782
1867
  while (readOffset < newlineAfterCheckpoint) {
1783
1868
  const toCopy = Math.min(CHUNK_SIZE, newlineAfterCheckpoint - readOffset);
@@ -1786,7 +1871,6 @@ var FileSessionWriter = class _FileSessionWriter {
1786
1871
  if (r === 0) break;
1787
1872
  await writeFd.write(copyBuf, 0, r);
1788
1873
  readOffset += r;
1789
- copied += r;
1790
1874
  }
1791
1875
  const raw = await fsp.readFile(this.filePath);
1792
1876
  const tail = raw.subarray(newlineAfterCheckpoint).toString("utf8");
@@ -3661,6 +3745,9 @@ function isContextWindowModeId(id) {
3661
3745
  return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
3662
3746
  }
3663
3747
 
3748
+ // src/types/config.ts
3749
+ var DEFAULT_TUI_THINKING_WORD = "thinking";
3750
+
3664
3751
  // src/types/default-config.ts
3665
3752
  var DEFAULT_TOOLS_CONFIG = Object.freeze({
3666
3753
  defaultExecutionStrategy: "smart",
@@ -3668,6 +3755,7 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
3668
3755
  iterationTimeoutMs: 3e5,
3669
3756
  sessionTimeoutMs: 18e5,
3670
3757
  perIterationOutputCapBytes: 1e5,
3758
+ descriptionMode: Object.freeze({}),
3671
3759
  autoExtendLimit: true,
3672
3760
  restrictToProjectRoot: false
3673
3761
  });
@@ -3678,6 +3766,10 @@ var DEFAULT_CONTEXT_CONFIG = Object.freeze({
3678
3766
  var DEFAULT_AUTONOMY_CONFIG = Object.freeze({
3679
3767
  autoProceedDelayMs: 45e3
3680
3768
  });
3769
+ var DEFAULT_CIRCUIT_BREAKER_CONFIG = Object.freeze({
3770
+ enabled: false,
3771
+ autoKillResetMs: 6e4
3772
+ });
3681
3773
  var DEFAULT_SESSION_LOGGING_CONFIG = Object.freeze({
3682
3774
  auditLevel: "standard",
3683
3775
  sampling: {
@@ -3704,7 +3796,8 @@ var BEHAVIOR_DEFAULTS = {
3704
3796
  hardThreshold: 0.9,
3705
3797
  autoCompact: true,
3706
3798
  preserveK: DEFAULT_CONTEXT_CONFIG.preserveK,
3707
- eliseThreshold: DEFAULT_CONTEXT_CONFIG.eliseThreshold
3799
+ eliseThreshold: DEFAULT_CONTEXT_CONFIG.eliseThreshold,
3800
+ strategy: "hybrid"
3708
3801
  },
3709
3802
  tools: {
3710
3803
  defaultExecutionStrategy: DEFAULT_TOOLS_CONFIG.defaultExecutionStrategy,
@@ -3712,6 +3805,7 @@ var BEHAVIOR_DEFAULTS = {
3712
3805
  iterationTimeoutMs: DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,
3713
3806
  sessionTimeoutMs: DEFAULT_TOOLS_CONFIG.sessionTimeoutMs,
3714
3807
  perIterationOutputCapBytes: DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,
3808
+ descriptionMode: DEFAULT_TOOLS_CONFIG.descriptionMode,
3715
3809
  autoExtendLimit: DEFAULT_TOOLS_CONFIG.autoExtendLimit,
3716
3810
  restrictToProjectRoot: DEFAULT_TOOLS_CONFIG.restrictToProjectRoot
3717
3811
  },
@@ -3726,6 +3820,13 @@ var BEHAVIOR_DEFAULTS = {
3726
3820
  allowOutsideProjectRoot: true
3727
3821
  },
3728
3822
  mcpServers: {},
3823
+ fallbackAuto: true,
3824
+ maxConcurrent: 4,
3825
+ yolo: false,
3826
+ nextPrediction: false,
3827
+ hints: true,
3828
+ debugStream: false,
3829
+ configScope: "global",
3729
3830
  indexing: {
3730
3831
  onSessionStart: true,
3731
3832
  onEdit: true,
@@ -3733,8 +3834,55 @@ var BEHAVIOR_DEFAULTS = {
3733
3834
  debounceMs: 400
3734
3835
  },
3735
3836
  session: { ...DEFAULT_SESSION_LOGGING_CONFIG },
3736
- autonomy: { autoProceedDelayMs: DEFAULT_AUTONOMY_CONFIG.autoProceedDelayMs }
3837
+ autonomy: {
3838
+ defaultMode: "off",
3839
+ autoProceedDelayMs: DEFAULT_AUTONOMY_CONFIG.autoProceedDelayMs,
3840
+ autoProceedMaxIterations: 50,
3841
+ autonomyNextPrompt: "auto {{suggestion}}",
3842
+ terminalTitleAnimation: true,
3843
+ yolo: false,
3844
+ streamFleet: true,
3845
+ chime: false,
3846
+ confirmExit: true,
3847
+ mouseMode: false,
3848
+ enhance: true,
3849
+ enhanceDelayMs: 6e4,
3850
+ enhanceLanguage: "original",
3851
+ statuslineMode: "detailed",
3852
+ thinkingWord: DEFAULT_TUI_THINKING_WORD
3853
+ },
3854
+ circuitBreaker: { ...DEFAULT_CIRCUIT_BREAKER_CONFIG },
3855
+ modelRuntime: {
3856
+ reasoning: { mode: "auto", effort: "high", preserve: false },
3857
+ cache: {}
3858
+ }
3737
3859
  };
3860
+ function isPlainRecord(value) {
3861
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3862
+ }
3863
+ function cloneJsonValue(value) {
3864
+ return structuredClone(value);
3865
+ }
3866
+ function fillMissingDefaults(target, defaults) {
3867
+ const value = cloneJsonValue(target);
3868
+ const changed = fillMissingDefaultsInPlace(value, defaults);
3869
+ return { value, changed };
3870
+ }
3871
+ function fillMissingDefaultsInPlace(target, defaults) {
3872
+ let changed = false;
3873
+ for (const [key, defaultValue] of Object.entries(defaults)) {
3874
+ if (!Object.prototype.hasOwnProperty.call(target, key)) {
3875
+ target[key] = cloneJsonValue(defaultValue);
3876
+ changed = true;
3877
+ continue;
3878
+ }
3879
+ const current = target[key];
3880
+ if (isPlainRecord(current) && isPlainRecord(defaultValue)) {
3881
+ changed = fillMissingDefaultsInPlace(current, defaultValue) || changed;
3882
+ }
3883
+ }
3884
+ return changed;
3885
+ }
3738
3886
  function envBool(v) {
3739
3887
  return !/^(0|false|no|off)$/i.test(v.trim());
3740
3888
  }
@@ -3786,27 +3934,139 @@ var defaultIndexing = {
3786
3934
  watchExternal: true,
3787
3935
  debounceMs: 400
3788
3936
  };
3789
- var IN_PROJECT_FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
3937
+ var IN_PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
3938
+ "version",
3939
+ "model",
3940
+ "cwd",
3941
+ "context",
3942
+ "tools",
3943
+ "features",
3944
+ "autonomy",
3945
+ "indexing",
3946
+ "session",
3947
+ "log",
3948
+ "launch",
3949
+ "nextPrediction",
3950
+ "hints",
3951
+ "debugStream",
3952
+ "configScope",
3953
+ "maxConcurrent",
3954
+ "fallbackModels",
3955
+ "fallbackAuto",
3956
+ "models",
3957
+ "modelMatrix",
3958
+ "circuitBreaker",
3959
+ "adaptiveConcurrency",
3960
+ "modelRuntime"
3961
+ ]);
3962
+ var KNOWN_DENIED_IN_PROJECT = [
3963
+ { key: "provider", reason: "Provider id override; can intercept prompts/responses." },
3964
+ { key: "apiKey", reason: "Overrides user API key; exfiltrates prompts." },
3965
+ { key: "baseUrl", reason: "Redirects provider endpoint; leaks real API key." },
3966
+ { key: "providers", reason: "Per-provider apiKey/baseUrl/oauthConfig; same redirect/exfil." },
3967
+ { key: "mcpServers", reason: "Arbitrary command/args/env spawned at boot (RCE)." },
3968
+ { key: "hooks", reason: "Shell command arrays on lifecycle events (RCE)." },
3969
+ { key: "plugins", reason: "Dynamic npm package load at boot (RCE)." },
3970
+ { key: "sync", reason: "Carries githubToken credential and target repo." },
3971
+ { key: "yolo", reason: "Disables all permission confirmation prompts." },
3972
+ { key: "extensions", reason: "Per-plugin config can carry command/credential fields." },
3973
+ { key: "hq", reason: "Carries HQ client token credential and endpoint URL." }
3974
+ ];
3975
+ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
3976
+ "version",
3790
3977
  "provider",
3978
+ "model",
3791
3979
  "apiKey",
3792
3980
  "baseUrl",
3981
+ "maxConcurrent",
3793
3982
  "providers",
3983
+ "models",
3984
+ "modelMatrix",
3985
+ "context",
3986
+ "tools",
3794
3987
  "mcpServers",
3988
+ "fallbackModels",
3989
+ "fallbackAuto",
3795
3990
  "hooks",
3796
3991
  "plugins",
3797
- "sync",
3992
+ "log",
3993
+ "features",
3798
3994
  "yolo",
3995
+ "nextPrediction",
3996
+ "cwd",
3997
+ "autonomy",
3998
+ "hints",
3999
+ "debugStream",
4000
+ "configScope",
4001
+ "indexing",
4002
+ "circuitBreaker",
4003
+ "adaptiveConcurrency",
4004
+ "launch",
4005
+ "session",
4006
+ "modelRuntime",
4007
+ "hq",
4008
+ "sync",
3799
4009
  "extensions"
3800
4010
  ]);
4011
+ function assertInProjectAllowListComplete() {
4012
+ const missingFromBoth = [];
4013
+ for (const key of KNOWN_CONFIG_TOP_LEVEL_KEYS) {
4014
+ if (IN_PROJECT_ALLOWED_KEYS.has(key)) continue;
4015
+ const denied = KNOWN_DENIED_IN_PROJECT.find((d) => d.key === key);
4016
+ if (!denied) missingFromBoth.push(key);
4017
+ }
4018
+ const staleDenials = KNOWN_DENIED_IN_PROJECT.filter((d) => !KNOWN_CONFIG_TOP_LEVEL_KEYS.has(d.key)).map((d) => d.key);
4019
+ const duplicate = KNOWN_DENIED_IN_PROJECT.filter((d) => IN_PROJECT_ALLOWED_KEYS.has(d.key)).map((d) => d.key);
4020
+ const problems = [];
4021
+ if (missingFromBoth.length > 0) {
4022
+ problems.push(
4023
+ `new Config field(s) not classified as allowed or denied for in-project config: ` + missingFromBoth.join(", ") + ". Add each to IN_PROJECT_ALLOWED_KEYS (if safe) or KNOWN_DENIED_IN_PROJECT (with a reason)."
4024
+ );
4025
+ }
4026
+ if (staleDenials.length > 0) {
4027
+ problems.push(
4028
+ `KNOWN_DENIED_IN_PROJECT references keys that no longer exist on Config: ` + staleDenials.join(", ") + ". Remove them or restore the field on Config."
4029
+ );
4030
+ }
4031
+ if (duplicate.length > 0) {
4032
+ problems.push(
4033
+ `field(s) appear in BOTH IN_PROJECT_ALLOWED_KEYS and KNOWN_DENIED_IN_PROJECT: ` + duplicate.join(", ") + ". The allow-list wins at runtime; remove from one of the two."
4034
+ );
4035
+ }
4036
+ if (problems.length > 0) {
4037
+ throw new Error(
4038
+ `stripUnsafeInProjectFields drift check failed:
4039
+ - ${problems.join("\n - ")}`
4040
+ );
4041
+ }
4042
+ }
4043
+ var driftChecked = false;
3801
4044
  function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => console.warn(msg)) {
4045
+ if (!driftChecked) {
4046
+ assertInProjectAllowListComplete();
4047
+ driftChecked = true;
4048
+ }
3802
4049
  const stripped = [];
3803
4050
  const out = {};
3804
4051
  for (const [k, v] of Object.entries(inProject)) {
3805
- if (IN_PROJECT_FORBIDDEN_KEYS.has(k)) {
3806
- stripped.push(k);
4052
+ if (IN_PROJECT_ALLOWED_KEYS.has(k)) {
4053
+ out[k] = v;
3807
4054
  continue;
3808
4055
  }
3809
- out[k] = v;
4056
+ stripped.push(k);
4057
+ }
4058
+ const outTools = out["tools"];
4059
+ if (outTools && typeof outTools === "object") {
4060
+ const execCfg = outTools["exec"];
4061
+ if (execCfg && typeof execCfg === "object" && "allow" in execCfg) {
4062
+ const clonedExec = { ...execCfg };
4063
+ delete clonedExec["allow"];
4064
+ out["tools"] = {
4065
+ ...outTools,
4066
+ exec: clonedExec
4067
+ };
4068
+ stripped.push("tools.exec.allow");
4069
+ }
3810
4070
  }
3811
4071
  if (stripped.length > 0) {
3812
4072
  warn(
@@ -3815,7 +4075,7 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
3815
4075
  event: "config.in_project_unsafe_fields_ignored",
3816
4076
  path: sourcePath,
3817
4077
  ignoredKeys: stripped,
3818
- message: `Ignored ${stripped.length} unsafe field(s) from the repo-committed config "${sourcePath}": ${stripped.join(", ")}. These can only be set in your personal ~/.wrongstack/config.json, not in a project-committed file.`,
4078
+ message: `Ignored ${stripped.length} field(s) from the repo-committed config "${sourcePath}": ${stripped.join(", ")}. Only a small allow-list of benign preferences (model, context, tools limits, features, \u2026) may be set by <project>/.wrongstack/config.json. Everything else must live in your personal ~/.wrongstack/config.json.`,
3819
4079
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3820
4080
  })
3821
4081
  );
@@ -3859,6 +4119,7 @@ var DefaultConfigLoader = class {
3859
4119
  }
3860
4120
  async load(opts = {}) {
3861
4121
  let cfg = { ...BEHAVIOR_DEFAULTS };
4122
+ await this.ensureGlobalDefaults();
3862
4123
  const inProjectCollides = samePath(this.paths.inProjectConfig, this.paths.globalConfig) || samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);
3863
4124
  const [global, local, inProject] = await Promise.all([
3864
4125
  this.readJson(this.paths.globalConfig),
@@ -3923,6 +4184,80 @@ var DefaultConfigLoader = class {
3923
4184
  }
3924
4185
  return Object.freeze(cfg);
3925
4186
  }
4187
+ async ensureGlobalDefaults() {
4188
+ const fp = this.paths.globalConfig;
4189
+ const t0 = Date.now();
4190
+ try {
4191
+ await withFileLock(fp, async () => {
4192
+ let parsed;
4193
+ try {
4194
+ const raw = await fsp.readFile(fp, "utf8");
4195
+ const result = safeParse(raw);
4196
+ if (!result.ok || !isPlainRecord(result.value)) {
4197
+ return;
4198
+ }
4199
+ parsed = result.value;
4200
+ } catch (err) {
4201
+ if (err.code !== "ENOENT") {
4202
+ this.events?.emit("storage.error", {
4203
+ sessionId: "~config~",
4204
+ store: "config",
4205
+ filePath: fp,
4206
+ operation: "ensure_defaults",
4207
+ outcome: "failure",
4208
+ error: storageErrorString(err),
4209
+ recoverable: false,
4210
+ durationMs: Date.now() - t0,
4211
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
4212
+ });
4213
+ console.warn(JSON.stringify({
4214
+ level: "warn",
4215
+ event: "config.defaults_read_failed",
4216
+ path: fp,
4217
+ message: toErrorMessage(err),
4218
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4219
+ }));
4220
+ return;
4221
+ }
4222
+ parsed = {};
4223
+ }
4224
+ const { value, changed } = fillMissingDefaults(
4225
+ parsed,
4226
+ BEHAVIOR_DEFAULTS
4227
+ );
4228
+ if (!changed) return;
4229
+ await atomicWrite(fp, JSON.stringify(value, null, 2), { mode: 384 });
4230
+ this.events?.emit("storage.write", {
4231
+ sessionId: "~config~",
4232
+ store: "config",
4233
+ filePath: fp,
4234
+ operation: "ensure_defaults",
4235
+ outcome: "success",
4236
+ durationMs: Date.now() - t0,
4237
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
4238
+ });
4239
+ });
4240
+ } catch (err) {
4241
+ this.events?.emit("storage.error", {
4242
+ sessionId: "~config~",
4243
+ store: "config",
4244
+ filePath: fp,
4245
+ operation: "ensure_defaults",
4246
+ outcome: "failure",
4247
+ error: storageErrorString(err),
4248
+ recoverable: false,
4249
+ durationMs: Date.now() - t0,
4250
+ ...this.traceId !== void 0 ? { traceId: this.traceId } : {}
4251
+ });
4252
+ console.warn(JSON.stringify({
4253
+ level: "warn",
4254
+ event: "config.defaults_write_failed",
4255
+ path: fp,
4256
+ message: toErrorMessage(err),
4257
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4258
+ }));
4259
+ }
4260
+ }
3926
4261
  /**
3927
4262
  * Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
3928
4263
  * by the vault (if provided). The file is isolated from the main config
@@ -4385,6 +4720,34 @@ var DefaultSessionReader = class {
4385
4720
  ids = filtered.map((s) => s.id);
4386
4721
  }
4387
4722
  const hits = [];
4723
+ const streaming = this.store.searchEvents?.bind(this.store);
4724
+ if (streaming) {
4725
+ for (const id of ids) {
4726
+ const matched = await streaming(
4727
+ id,
4728
+ (ev) => {
4729
+ if (allowedTypes && !allowedTypes.has(ev.type)) return false;
4730
+ const text = eventText(ev);
4731
+ if (text === null) return false;
4732
+ return matcher(text) !== null;
4733
+ },
4734
+ { limit: limit - hits.length }
4735
+ );
4736
+ for (const m of matched) {
4737
+ const text = expectDefined(eventText(m.event));
4738
+ const hit = expectDefined(matcher(text));
4739
+ hits.push({
4740
+ sessionId: id,
4741
+ eventIndex: m.eventIndex,
4742
+ ts: m.ts,
4743
+ type: m.event.type,
4744
+ snippet: snippetOf(text, hit.start, hit.end)
4745
+ });
4746
+ if (hits.length >= limit) return hits;
4747
+ }
4748
+ }
4749
+ return hits;
4750
+ }
4388
4751
  for (const id of ids) {
4389
4752
  let data;
4390
4753
  try {
@@ -6126,6 +6489,10 @@ var AGENT_REAP_MS = 3e4;
6126
6489
  var AGENT_SWEEP_INTERVAL_MS = 1e4;
6127
6490
  var PARTIAL_TEXT_CAP = 1200;
6128
6491
  var PARTIAL_FLUSH_THROTTLE_MS = 300;
6492
+ function clampPct(pct) {
6493
+ if (!Number.isFinite(pct)) return 0;
6494
+ return Math.max(0, Math.min(100, pct));
6495
+ }
6129
6496
  var AgentStatusTracker = class {
6130
6497
  events;
6131
6498
  registry;
@@ -6277,7 +6644,7 @@ var AgentStatusTracker = class {
6277
6644
  this.events.onPattern("ctx.pct", (_e, payload) => {
6278
6645
  const p = payload;
6279
6646
  if (typeof p?.load === "number" && Number.isFinite(p.load)) {
6280
- this.leaderCtxPct = Math.round(p.load * 100);
6647
+ this.leaderCtxPct = clampPct(Math.round(p.load * 100));
6281
6648
  this.flush();
6282
6649
  }
6283
6650
  })
@@ -6319,7 +6686,7 @@ var AgentStatusTracker = class {
6319
6686
  const p = payload;
6320
6687
  if (!p?.subagentId) return;
6321
6688
  const entry = touch(p.subagentId);
6322
- if (typeof p.load === "number") entry.ctxPct = Math.round(p.load * 100);
6689
+ if (typeof p.load === "number") entry.ctxPct = clampPct(Math.round(p.load * 100));
6323
6690
  this.flush();
6324
6691
  })
6325
6692
  );
@@ -6480,7 +6847,7 @@ var AgentStatusTracker = class {
6480
6847
  const providerMax = c.provider?.capabilities?.maxContext;
6481
6848
  const maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : void 0;
6482
6849
  if (typeof c.lastRequestTokens === "number" && c.lastRequestTokens > 0 && maxContext !== void 0) {
6483
- this.leaderCtxPct = Math.round(c.lastRequestTokens / maxContext * 100);
6850
+ this.leaderCtxPct = clampPct(Math.round(c.lastRequestTokens / maxContext * 100));
6484
6851
  }
6485
6852
  }
6486
6853
  };