@wrongstack/core 0.273.0 → 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 (60) hide show
  1. package/dist/{agent-bridge-BZ2enORi.d.ts → agent-bridge-DpKIxHhE.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-ehb4xGvd.d.ts → agent-subagent-runner-Dx7fZ1bE.d.ts} +7 -7
  3. package/dist/{brain-BxN2k2HP.d.ts → brain-BDcQaku-.d.ts} +11 -5
  4. package/dist/{compactor-72ug-ZRB.d.ts → compactor-BuSdj3fq.d.ts} +1 -1
  5. package/dist/{config-C8IYxlO8.d.ts → config-CR2yoG8c.d.ts} +54 -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 +113 -13
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +26 -26
  11. package/dist/defaults/index.js +405 -8
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +15 -15
  14. package/dist/execution/index.js +75 -1
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +6 -6
  18. package/dist/{global-mailbox-C9dsc9Y_.d.ts → global-mailbox-CwcubDkA.d.ts} +1 -1
  19. package/dist/{goal-preamble-NhflDjYb.d.ts → goal-preamble-Bu0a2uCG.d.ts} +9 -9
  20. package/dist/{goal-store-Cx363x7Z.d.ts → goal-store-CTmFuZ8J.d.ts} +1 -1
  21. package/dist/hq/index.d.ts +5 -5
  22. package/dist/{index-B7fHDt0B.d.ts → index-CTq5wU3m.d.ts} +5 -5
  23. package/dist/{index-BbVprU-9.d.ts → index-CxP-HBhX.d.ts} +2 -2
  24. package/dist/index.d.ts +63 -42
  25. package/dist/index.js +598 -56
  26. package/dist/index.js.map +1 -1
  27. package/dist/infrastructure/index.d.ts +6 -6
  28. package/dist/kernel/index.d.ts +11 -11
  29. package/dist/kernel/index.js.map +1 -1
  30. package/dist/{mcp-servers-B6fSRNC1.d.ts → mcp-servers-BQaOE71z.d.ts} +3 -3
  31. package/dist/models/index.d.ts +5 -5
  32. package/dist/{models-registry-4C6Wr91w.d.ts → models-registry-BEcny4kP.d.ts} +1 -1
  33. package/dist/{multi-agent-coordinator-q1skFeNP.d.ts → multi-agent-coordinator-Bx8EFkv2.d.ts} +1 -1
  34. package/dist/{null-fleet-bus-C9rrgQwc.d.ts → null-fleet-bus-BC5ZXCQw.d.ts} +6 -6
  35. package/dist/observability/index.d.ts +2 -2
  36. package/dist/{parallel-eternal-engine-CtXly2Sf.d.ts → parallel-eternal-engine-C345TI3n.d.ts} +9 -9
  37. package/dist/{path-resolver-Bim6G5Jz.d.ts → path-resolver-C-W_wzkF.d.ts} +3 -3
  38. package/dist/{permission-CC7XFYWG.d.ts → permission-CsBGZkxp.d.ts} +1 -1
  39. package/dist/{permission-policy-cYR4RJmw.d.ts → permission-policy-g3Sg0GdZ.d.ts} +2 -2
  40. package/dist/{pipeline-CNVKuQDQ.d.ts → pipeline-xnw_24Z8.d.ts} +2 -2
  41. package/dist/{plan-templates-C4wXMmiM.d.ts → plan-templates-DGaiYEcS.d.ts} +31 -5
  42. package/dist/{provider-model-resolve-DFd3IPpw.d.ts → provider-model-resolve-Cz6OlIOp.d.ts} +3 -3
  43. package/dist/{provider-runner-BpM0mdBE.d.ts → provider-runner-7J0HqF6B.d.ts} +3 -3
  44. package/dist/{retry-policy-BV7nzeAd.d.ts → retry-policy-kqXJOVkX.d.ts} +1 -1
  45. package/dist/sdd/index.d.ts +9 -9
  46. package/dist/{secret-vault-eMBKfheR.d.ts → secret-vault-CMQUr-eB.d.ts} +1 -1
  47. package/dist/security/index.d.ts +5 -5
  48. package/dist/{selector-C4ORTOid.d.ts → selector-B4r34PWR.d.ts} +1 -1
  49. package/dist/{session-event-bridge-CeNpUL9w.d.ts → session-event-bridge-BD3LoyLC.d.ts} +1 -1
  50. package/dist/{session-reader-BepLSnGL.d.ts → session-reader-DjrKGD9c.d.ts} +1 -1
  51. package/dist/storage/index.d.ts +11 -11
  52. package/dist/storage/index.js +377 -11
  53. package/dist/storage/index.js.map +1 -1
  54. package/dist/tools/index.d.ts +2 -2
  55. package/dist/types/index.d.ts +19 -19
  56. package/dist/types/index.js +28 -0
  57. package/dist/types/index.js.map +1 -1
  58. package/dist/utils/index.d.ts +2 -2
  59. package/dist/{worktree-manager-BDuXTaWL.d.ts → worktree-manager-DHdrWQ_7.d.ts} +1 -1
  60. package/package.json +1 -1
@@ -746,6 +746,91 @@ var DefaultSessionStore = class _DefaultSessionStore {
746
746
  }
747
747
  }
748
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
+ }
749
834
  async list(limit = 20) {
750
835
  try {
751
836
  await ensureDir(this.dir);
@@ -3660,6 +3745,9 @@ function isContextWindowModeId(id) {
3660
3745
  return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
3661
3746
  }
3662
3747
 
3748
+ // src/types/config.ts
3749
+ var DEFAULT_TUI_THINKING_WORD = "thinking";
3750
+
3663
3751
  // src/types/default-config.ts
3664
3752
  var DEFAULT_TOOLS_CONFIG = Object.freeze({
3665
3753
  defaultExecutionStrategy: "smart",
@@ -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,
@@ -3727,6 +3820,13 @@ var BEHAVIOR_DEFAULTS = {
3727
3820
  allowOutsideProjectRoot: true
3728
3821
  },
3729
3822
  mcpServers: {},
3823
+ fallbackAuto: true,
3824
+ maxConcurrent: 4,
3825
+ yolo: false,
3826
+ nextPrediction: false,
3827
+ hints: true,
3828
+ debugStream: false,
3829
+ configScope: "global",
3730
3830
  indexing: {
3731
3831
  onSessionStart: true,
3732
3832
  onEdit: true,
@@ -3734,8 +3834,55 @@ var BEHAVIOR_DEFAULTS = {
3734
3834
  debounceMs: 400
3735
3835
  },
3736
3836
  session: { ...DEFAULT_SESSION_LOGGING_CONFIG },
3737
- 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
+ }
3738
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
+ }
3739
3886
  function envBool(v) {
3740
3887
  return !/^(0|false|no|off)$/i.test(v.trim());
3741
3888
  }
@@ -3787,27 +3934,139 @@ var defaultIndexing = {
3787
3934
  watchExternal: true,
3788
3935
  debounceMs: 400
3789
3936
  };
3790
- 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",
3791
3977
  "provider",
3978
+ "model",
3792
3979
  "apiKey",
3793
3980
  "baseUrl",
3981
+ "maxConcurrent",
3794
3982
  "providers",
3983
+ "models",
3984
+ "modelMatrix",
3985
+ "context",
3986
+ "tools",
3795
3987
  "mcpServers",
3988
+ "fallbackModels",
3989
+ "fallbackAuto",
3796
3990
  "hooks",
3797
3991
  "plugins",
3798
- "sync",
3992
+ "log",
3993
+ "features",
3799
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",
3800
4009
  "extensions"
3801
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;
3802
4044
  function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => console.warn(msg)) {
4045
+ if (!driftChecked) {
4046
+ assertInProjectAllowListComplete();
4047
+ driftChecked = true;
4048
+ }
3803
4049
  const stripped = [];
3804
4050
  const out = {};
3805
4051
  for (const [k, v] of Object.entries(inProject)) {
3806
- if (IN_PROJECT_FORBIDDEN_KEYS.has(k)) {
3807
- stripped.push(k);
4052
+ if (IN_PROJECT_ALLOWED_KEYS.has(k)) {
4053
+ out[k] = v;
3808
4054
  continue;
3809
4055
  }
3810
- 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
+ }
3811
4070
  }
3812
4071
  if (stripped.length > 0) {
3813
4072
  warn(
@@ -3816,7 +4075,7 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
3816
4075
  event: "config.in_project_unsafe_fields_ignored",
3817
4076
  path: sourcePath,
3818
4077
  ignoredKeys: stripped,
3819
- 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.`,
3820
4079
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3821
4080
  })
3822
4081
  );
@@ -3860,6 +4119,7 @@ var DefaultConfigLoader = class {
3860
4119
  }
3861
4120
  async load(opts = {}) {
3862
4121
  let cfg = { ...BEHAVIOR_DEFAULTS };
4122
+ await this.ensureGlobalDefaults();
3863
4123
  const inProjectCollides = samePath(this.paths.inProjectConfig, this.paths.globalConfig) || samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);
3864
4124
  const [global, local, inProject] = await Promise.all([
3865
4125
  this.readJson(this.paths.globalConfig),
@@ -3924,6 +4184,80 @@ var DefaultConfigLoader = class {
3924
4184
  }
3925
4185
  return Object.freeze(cfg);
3926
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
+ }
3927
4261
  /**
3928
4262
  * Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
3929
4263
  * by the vault (if provided). The file is isolated from the main config
@@ -4386,6 +4720,34 @@ var DefaultSessionReader = class {
4386
4720
  ids = filtered.map((s) => s.id);
4387
4721
  }
4388
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
+ }
4389
4751
  for (const id of ids) {
4390
4752
  let data;
4391
4753
  try {
@@ -6127,6 +6489,10 @@ var AGENT_REAP_MS = 3e4;
6127
6489
  var AGENT_SWEEP_INTERVAL_MS = 1e4;
6128
6490
  var PARTIAL_TEXT_CAP = 1200;
6129
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
+ }
6130
6496
  var AgentStatusTracker = class {
6131
6497
  events;
6132
6498
  registry;
@@ -6278,7 +6644,7 @@ var AgentStatusTracker = class {
6278
6644
  this.events.onPattern("ctx.pct", (_e, payload) => {
6279
6645
  const p = payload;
6280
6646
  if (typeof p?.load === "number" && Number.isFinite(p.load)) {
6281
- this.leaderCtxPct = Math.round(p.load * 100);
6647
+ this.leaderCtxPct = clampPct(Math.round(p.load * 100));
6282
6648
  this.flush();
6283
6649
  }
6284
6650
  })
@@ -6320,7 +6686,7 @@ var AgentStatusTracker = class {
6320
6686
  const p = payload;
6321
6687
  if (!p?.subagentId) return;
6322
6688
  const entry = touch(p.subagentId);
6323
- 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));
6324
6690
  this.flush();
6325
6691
  })
6326
6692
  );
@@ -6481,7 +6847,7 @@ var AgentStatusTracker = class {
6481
6847
  const providerMax = c.provider?.capabilities?.maxContext;
6482
6848
  const maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : void 0;
6483
6849
  if (typeof c.lastRequestTokens === "number" && c.lastRequestTokens > 0 && maxContext !== void 0) {
6484
- this.leaderCtxPct = Math.round(c.lastRequestTokens / maxContext * 100);
6850
+ this.leaderCtxPct = clampPct(Math.round(c.lastRequestTokens / maxContext * 100));
6485
6851
  }
6486
6852
  }
6487
6853
  };