@wrongstack/core 0.309.0 → 0.310.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/chronicle/index.js +113 -41
  2. package/dist/chronicle/metrics-ingest.d.ts +7 -0
  3. package/dist/chronicle/metrics-schema.d.ts +4 -1
  4. package/dist/chronicle/project-server.js +94 -38
  5. package/dist/chronicle/query.d.ts +1 -0
  6. package/dist/chronicle/sqlite-journal-schema.d.ts +2 -1
  7. package/dist/chronicle/types.d.ts +2 -0
  8. package/dist/{agent-status-helpers.d.ts → coordination/agent-status-helpers.d.ts} +1 -1
  9. package/dist/{agent-status-tracker.d.ts → coordination/agent-status-tracker.d.ts} +2 -2
  10. package/dist/{middleware → coordination}/collab-pause.d.ts +1 -1
  11. package/dist/coordination/director-kanban-queue-helpers.d.ts +1 -1
  12. package/dist/coordination/director.d.ts +7 -0
  13. package/dist/coordination/explore-companion.d.ts +9 -6
  14. package/dist/coordination/fleet-status-tool.d.ts +1 -1
  15. package/dist/coordination/index.d.ts +5 -1
  16. package/dist/coordination/index.js +2392 -405
  17. package/dist/coordination/kanban-dispatch-port.d.ts +30 -0
  18. package/dist/coordination/kanban-ops-port.d.ts +21 -0
  19. package/dist/coordination/mailbox-hooks.d.ts +1 -1
  20. package/dist/coordination/mailbox-project-server.js +14 -9
  21. package/dist/coordination/mutation-engine.d.ts +5 -3
  22. package/dist/core/context.d.ts +35 -44
  23. package/dist/core/conversation-state.d.ts +16 -52
  24. package/dist/core/index.js +92 -25
  25. package/dist/core/provider-runner.d.ts +2 -2
  26. package/dist/core/run-env.d.ts +7 -28
  27. package/dist/core/streaming-response-builder.d.ts +2 -2
  28. package/dist/execution/index.js +138 -162
  29. package/dist/goal/index.js +100 -22
  30. package/dist/hq/auth-store.d.ts +9 -0
  31. package/dist/hq/cost-bridge.d.ts +1 -1
  32. package/dist/hq/index.js +69 -7
  33. package/dist/index.d.ts +12 -5
  34. package/dist/index.js +21185 -26807
  35. package/dist/infrastructure/index.js +232 -135
  36. package/dist/infrastructure/provider-cache-ledger.d.ts +1 -1
  37. package/dist/infrastructure/token-counter.d.ts +5 -1
  38. package/dist/kernel/events/file-events.d.ts +6 -0
  39. package/dist/kernel/events/provider-events.d.ts +10 -7
  40. package/dist/kernel/events/session-events.d.ts +4 -4
  41. package/dist/kernel/events/tool-events.d.ts +12 -2
  42. package/dist/observability/index.js +1 -1
  43. package/dist/plugin/index.js +218 -47
  44. package/dist/prompts/index.js +360 -3
  45. package/dist/security/auto-approve-policy.d.ts +2 -2
  46. package/dist/security/index.d.ts +1 -0
  47. package/dist/security/index.js +224 -68
  48. package/dist/security/kanban-boundary.d.ts +1 -1
  49. package/dist/security/kanban-governance-port.d.ts +28 -0
  50. package/dist/security/permission-helpers.d.ts +11 -0
  51. package/dist/security/permission-policy.d.ts +10 -1
  52. package/dist/security/yolo-risk.d.ts +17 -0
  53. package/dist/session-catalog/index.js +34 -5
  54. package/dist/session-catalog/project-server.js +34 -5
  55. package/dist/session-catalog/protocol.d.ts +1 -1
  56. package/dist/session-catalog/registry.d.ts +1 -1
  57. package/dist/session-catalog/store-schema.d.ts +1 -1
  58. package/dist/session-catalog/store.d.ts +1 -1
  59. package/dist/skills/index.js +39 -6
  60. package/dist/storage/annotations-store.d.ts +1 -1
  61. package/dist/storage/board-store-port.d.ts +45 -0
  62. package/dist/storage/completed-work-checkpoint.d.ts +1 -1
  63. package/dist/storage/config-loader/types.d.ts +1 -1
  64. package/dist/storage/event-bus-port.d.ts +27 -0
  65. package/dist/storage/file-session-writer.d.ts +47 -5
  66. package/dist/storage/goal-coordination.d.ts +1 -1
  67. package/dist/storage/goal-store.d.ts +1 -1
  68. package/dist/storage/index.d.ts +3 -4
  69. package/dist/storage/index.js +1107 -1462
  70. package/dist/storage/plan-store.d.ts +1 -1
  71. package/dist/storage/queue-store.d.ts +1 -1
  72. package/dist/storage/replay-log-store.d.ts +1 -1
  73. package/dist/storage/session-recovery.d.ts +10 -0
  74. package/dist/storage/session-resume-validation.d.ts +13 -1
  75. package/dist/storage/session-store/events.d.ts +1 -1
  76. package/dist/storage/session-store/load-session-data.d.ts +1 -1
  77. package/dist/storage/session-store/rename-session.d.ts +1 -1
  78. package/dist/storage/session-store/resume-session.d.ts +3 -1
  79. package/dist/storage/session-store/session-store-index.d.ts +2 -1
  80. package/dist/storage/session-store/types.d.ts +1 -1
  81. package/dist/storage/session-store.d.ts +70 -0
  82. package/dist/storage/session-summary-tracker.d.ts +8 -0
  83. package/dist/storage/session-write-buffer.d.ts +31 -2
  84. package/dist/storage/task-store.d.ts +1 -1
  85. package/dist/storage/todos-checkpoint.d.ts +1 -1
  86. package/dist/storage/tool-audit-log.d.ts +1 -1
  87. package/dist/tasking/index.js +100 -22
  88. package/dist/tasking/task-tracker.d.ts +24 -1
  89. package/dist/types/compactor.d.ts +2 -2
  90. package/dist/types/context.d.ts +212 -0
  91. package/dist/types/conversation-state.d.ts +109 -0
  92. package/dist/types/error-handler.d.ts +2 -2
  93. package/dist/types/file-event-record.d.ts +6 -0
  94. package/dist/types/index.d.ts +3 -3
  95. package/dist/types/index.js +17 -1
  96. package/dist/types/permission.d.ts +3 -3
  97. package/dist/types/plugin.d.ts +3 -3
  98. package/dist/types/provider-runner.d.ts +2 -2
  99. package/dist/types/provider.d.ts +10 -0
  100. package/dist/types/run-env.d.ts +32 -0
  101. package/dist/types/session.d.ts +3 -0
  102. package/dist/types/slash-command.d.ts +2 -2
  103. package/dist/types/token-counter.d.ts +30 -1
  104. package/dist/types/tool-executor.d.ts +2 -2
  105. package/dist/types/tool.d.ts +20 -5
  106. package/dist/utils/context-breakdown.d.ts +2 -2
  107. package/dist/utils/context-evidence.d.ts +12 -12
  108. package/dist/utils/crash-shield.d.ts +9 -0
  109. package/dist/utils/heap-watchdog.js +11 -3
  110. package/dist/utils/index.d.ts +2 -1
  111. package/dist/utils/index.js +126 -160
  112. package/dist/utils/regex-guard.d.ts +7 -30
  113. package/dist/utils/terminal-sanitize.d.ts +41 -0
  114. package/dist/utils/todos-format.d.ts +1 -1
  115. package/dist/utils/tool-subject.d.ts +1 -1
  116. package/dist/utils/tree-kill.d.ts +2 -0
  117. package/dist/utils/tree-kill.js +1 -0
  118. package/instructions/agents/chaos-monkey.md +5 -1
  119. package/instructions/coordination/subagent-baseline.md +10 -0
  120. package/instructions/system-lite.md +2 -0
  121. package/instructions/system-pro.md +40 -0
  122. package/instructions/system.md +15 -0
  123. package/package.json +7 -10
  124. package/dist/defaults/index.d.ts +0 -69
  125. package/dist/defaults/index.js +0 -37755
  126. /package/dist/{fleet-notifier.d.ts → coordination/fleet-notifier.d.ts} +0 -0
  127. /package/dist/{session-registry-atomic-file.d.ts → session-catalog/session-registry-atomic-file.d.ts} +0 -0
  128. /package/dist/{session-registry-types.d.ts → session-catalog/session-registry-types.d.ts} +0 -0
  129. /package/dist/{session-registry.d.ts → session-catalog/session-registry.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import type { Context } from '../../core/context.js';
1
+ import type { AgentContext } from '../../types/context.js';
2
2
  import type { ToolOutputMetadata } from '../../types/context-evidence.js';
3
3
  import type { PermissionDecision } from '../../types/permission.js';
4
4
  import type { RiskTier, Tool, ToolErrorCategory, ToolProgressEvent } from '../../types/tool.js';
@@ -6,6 +6,8 @@ export interface ToolEventMap {
6
6
  'tool.started': {
7
7
  sessionId?: string | undefined;
8
8
  traceId?: string | undefined;
9
+ logicalRequestId?: string | undefined;
10
+ promptManifestId?: string | undefined;
9
11
  agentId?: string | undefined;
10
12
  agentName?: string | undefined;
11
13
  name: string;
@@ -29,6 +31,8 @@ export interface ToolEventMap {
29
31
  id: string;
30
32
  sessionId: string;
31
33
  traceId?: string | undefined;
34
+ logicalRequestId?: string | undefined;
35
+ promptManifestId?: string | undefined;
32
36
  agentId: string;
33
37
  durationMs: number;
34
38
  outputChars: number;
@@ -41,6 +45,8 @@ export interface ToolEventMap {
41
45
  id: string;
42
46
  sessionId: string;
43
47
  traceId?: string | undefined;
48
+ logicalRequestId?: string | undefined;
49
+ promptManifestId?: string | undefined;
44
50
  agentId: string;
45
51
  durationMs: number;
46
52
  category: ToolErrorCategory;
@@ -82,6 +88,8 @@ export interface ToolEventMap {
82
88
  'permission.evaluated': {
83
89
  sessionId?: string | undefined;
84
90
  traceId?: string | undefined;
91
+ logicalRequestId?: string | undefined;
92
+ promptManifestId?: string | undefined;
85
93
  agentId?: string | undefined;
86
94
  name: string;
87
95
  id: string;
@@ -147,7 +155,7 @@ export interface ToolEventMap {
147
155
  */
148
156
  'tool.loop_detected': {
149
157
  sessionId?: string | undefined;
150
- ctx: Context;
158
+ ctx: AgentContext;
151
159
  /** Comma-separated tool names involved in the loop, or empty string for pure message loops. */
152
160
  tools: string;
153
161
  /** Number of repeats detected (consecutive iterations, or identical calls within the window for `scope: 'call'`). */
@@ -184,6 +192,8 @@ export interface ToolEventMap {
184
192
  'tool.executed': {
185
193
  sessionId?: string | undefined;
186
194
  traceId?: string | undefined;
195
+ logicalRequestId?: string | undefined;
196
+ promptManifestId?: string | undefined;
187
197
  agentId?: string | undefined;
188
198
  agentName?: string | undefined;
189
199
  /**
@@ -388,7 +388,7 @@ var SENSITIVE_FLAG_PATTERNS = [
388
388
  // redaction function (false positive = cosmetic noise; false negative = leak).
389
389
  /(?<![-\w])-(?:password|p|a)(?:[=\s]+)?[^\s,-]+/gi,
390
390
  // env var–style secrets: TOKEN=x, API_KEY=y, etc.
391
- /(?:TOKEN|API_KEY|API_SECRET|AUTH_TOKEN|GITHUB_TOKEN|GH_TOKEN|BEARER|JWT|OAUTH|CREDENTIAL|SECRET|PRIVATE_KEY|PASSWORD|PASSWD)\s*[=:]\s*[^\s,]+/gi,
391
+ /(?:TOKEN|API_KEY|API_SECRET|AUTH_TOKEN|GITHUB_TOKEN|GH_TOKEN|BEARER|JWT|OAUTH|CREDENTIAL|SECRET|PRIVATE_KEY|PASSWORD|PASSWD|PASSPHRASE)\s*[=:]\s*[^\s,]+/gi,
392
392
  // Generic high-entropy look: base64 strings >32 chars or hex strings >32 digits — but only
393
393
  // when preceded by a flag name (e.g. --github-token=EyJ...). Global flag so
394
394
  // every such flag in the command line is redacted, not just the first.
@@ -1945,18 +1945,88 @@ var DefaultPluginAPI = class {
1945
1945
  list: () => tr.list()
1946
1946
  };
1947
1947
  const pr = init.providerRegistry;
1948
+ const providerTypesIOwn = /* @__PURE__ */ new Set();
1949
+ const assertCanMutateProvider = (type, op) => {
1950
+ if (isOfficial) return;
1951
+ if (providerTypesIOwn.has(type)) return;
1952
+ if (!pr.has(type)) return;
1953
+ throw new Error(
1954
+ `Plugin "${owner}" may not ${op} provider "${type}" \u2014 it was not registered by this plugin. Replacing an existing provider would route prompts and credentials through plugin code.`
1955
+ );
1956
+ };
1948
1957
  this.providers = {
1949
- register: (f) => pr.register(f),
1950
- unregister: (type) => pr.unregister(type),
1958
+ register: (f) => {
1959
+ assertCanMutateProvider(f.type, "replace");
1960
+ pr.register(f);
1961
+ providerTypesIOwn.add(f.type);
1962
+ },
1963
+ unregister: (type) => {
1964
+ assertCanMutateProvider(type, "unregister");
1965
+ providerTypesIOwn.delete(type);
1966
+ return pr.unregister(type);
1967
+ },
1951
1968
  create: (cfg) => pr.create(cfg),
1952
1969
  list: () => pr.list()
1953
1970
  };
1954
- this.mcp = init.mcpRegistry ?? noopMcp;
1971
+ const mcpRegistry = init.mcpRegistry;
1972
+ if (!mcpRegistry) {
1973
+ this.mcp = noopMcp;
1974
+ } else {
1975
+ const mcpServersIStarted = /* @__PURE__ */ new Set();
1976
+ const assertOwnsMcpServer = (name, op) => {
1977
+ if (isOfficial) return;
1978
+ if (mcpServersIStarted.has(name)) return;
1979
+ if (!mcpRegistry.list().some((s) => s.name === name)) return;
1980
+ throw new Error(
1981
+ `Plugin "${owner}" may not ${op} MCP server "${name}" \u2014 it was not started by this plugin.`
1982
+ );
1983
+ };
1984
+ this.mcp = {
1985
+ start: async (cfg) => {
1986
+ const name = cfg?.name;
1987
+ if (typeof name === "string" && mcpRegistry.list().some((s) => s.name === name)) {
1988
+ assertOwnsMcpServer(name, "start");
1989
+ }
1990
+ await mcpRegistry.start(cfg);
1991
+ if (typeof name === "string") mcpServersIStarted.add(name);
1992
+ },
1993
+ stop: async (name) => {
1994
+ assertOwnsMcpServer(name, "stop");
1995
+ await mcpRegistry.stop(name);
1996
+ },
1997
+ restart: async (name) => {
1998
+ assertOwnsMcpServer(name, "restart");
1999
+ await mcpRegistry.restart(name);
2000
+ },
2001
+ list: () => mcpRegistry.list()
2002
+ };
2003
+ }
1955
2004
  const scr = init.slashCommandRegistry;
1956
2005
  const official = init.official === true;
2006
+ const commandsIOwn = /* @__PURE__ */ new Set();
1957
2007
  this.slashCommands = scr ? {
1958
- register: (cmd) => scr.register(cmd, owner, { official }),
1959
- unregister: (name) => scr.unregister(name),
2008
+ register: (cmd) => {
2009
+ scr.register(cmd, owner, { official });
2010
+ for (const key of [cmd.name, ...cmd.aliases ?? []]) {
2011
+ commandsIOwn.add(key);
2012
+ commandsIOwn.add(`${owner}:${key}`);
2013
+ }
2014
+ },
2015
+ unregister: (name) => {
2016
+ if (!official && !commandsIOwn.has(name) && scr.get(name) !== void 0) {
2017
+ throw new Error(
2018
+ `Plugin "${owner}" may not unregister slash command "${name}" \u2014 it was not registered by this plugin.`
2019
+ );
2020
+ }
2021
+ for (const key of [
2022
+ name,
2023
+ `${owner}:${name}`,
2024
+ name.startsWith(`${owner}:`) ? name.slice(owner.length + 1) : name
2025
+ ]) {
2026
+ commandsIOwn.delete(key);
2027
+ }
2028
+ return scr.unregister(name);
2029
+ },
1960
2030
  get: (name) => scr.get(name),
1961
2031
  list: () => scr.list()
1962
2032
  } : noopSlashCommands;
@@ -5089,7 +5159,11 @@ function relationKeys(event) {
5089
5159
  add("attempt", event.correlation.attemptId, "explicit");
5090
5160
  add("decision", event.attributes?.decisionId, "explicit");
5091
5161
  add("network_request", event.attributes?.requestId, "explicit");
5092
- add("prompt_manifest", event.attributes?.promptManifest?.manifestId, "explicit");
5162
+ add(
5163
+ "prompt_manifest",
5164
+ event.correlation.promptManifestId ?? event.attributes?.promptManifest?.manifestId,
5165
+ "explicit"
5166
+ );
5093
5167
  add("resource_lineage", event.resource?.id, "inferred");
5094
5168
  if (event.eventType === "metrics.rollup" && event.attributes?.signal === "tool.resource.observed") {
5095
5169
  const resources = event.attributes.resources;
@@ -5111,6 +5185,7 @@ function matches(event, query) {
5111
5185
  if (!equal(query.agentId, event.scope.agentId) || !equal(query.taskId, event.scope.taskId)) return false;
5112
5186
  if (!equal(query.providerId, event.runtime?.providerId) || !equal(query.modelId, event.runtime?.modelId)) return false;
5113
5187
  if (!equal(query.traceId, event.correlation.traceId) || !equal(query.logicalRequestId, event.correlation.logicalRequestId)) return false;
5188
+ if (!equal(query.promptManifestId, event.correlation.promptManifestId)) return false;
5114
5189
  if (!equal(query.attemptId, event.correlation.attemptId) || !equal(query.toolCallId, event.correlation.toolCallId)) return false;
5115
5190
  if (!equal(query.resourceKind, event.resource?.kind) || !equal(query.resourceId, event.resource?.id)) return false;
5116
5191
  if (query.path && normalize(event.resource?.path) !== normalize(query.path)) return false;
@@ -5251,6 +5326,7 @@ function isChronicleEvent(value) {
5251
5326
  ]) && typeof value.correlation.traceId === "string" && typeof value.correlation.spanId === "string" && optionalStrings(value.correlation, [
5252
5327
  "parentSpanId",
5253
5328
  "logicalRequestId",
5329
+ "promptManifestId",
5254
5330
  "attemptId",
5255
5331
  "toolCallId"
5256
5332
  ]) && isRuntime(value.runtime) && isResource(value.resource) && (value.attributes === void 0 || isRecord2(value.attributes)) && (value.tags === void 0 || isStringRecord(value.tags));
@@ -5440,7 +5516,7 @@ var ChronicleQuotaManager = class {
5440
5516
  };
5441
5517
 
5442
5518
  // src/chronicle/sqlite-journal-schema.ts
5443
- import { createRequire } from "node:module";
5519
+ import { loadRuntimeDatabaseSync } from "@wrongstack/persistence";
5444
5520
 
5445
5521
  // src/utils/index.ts
5446
5522
  init_atomic_write();
@@ -5829,25 +5905,47 @@ function withSqliteExperimentalWarningSuppressed(run) {
5829
5905
  }
5830
5906
  }
5831
5907
 
5908
+ // src/utils/win32-cmd.ts
5909
+ var WIN32_CMD_META = /[&|<>"%\r\n\0]/;
5910
+ function buildWin32CmdShimInvocation(command, args = []) {
5911
+ assertSafeWin32CmdArgs([command, ...args]);
5912
+ const line = ["call", quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(" ");
5913
+ return {
5914
+ command: process.env["COMSPEC"] ?? "cmd.exe",
5915
+ args: ["/d", "/c", line],
5916
+ windowsVerbatimArguments: true
5917
+ };
5918
+ }
5919
+ function assertSafeWin32CmdArgs(args) {
5920
+ for (const arg of args) {
5921
+ if (typeof arg === "string" && WIN32_CMD_META.test(arg)) {
5922
+ throw new Error(
5923
+ 'win32 cmd shim spawn: argument contains a shell metacharacter (one of & | < > ", or a newline) that could enable command injection through the .cmd/.bat wrapper - refusing to run. Offending argument: ' + JSON.stringify(arg)
5924
+ );
5925
+ }
5926
+ }
5927
+ }
5928
+ function quoteWin32CmdArg(arg) {
5929
+ return `"${arg}"`;
5930
+ }
5931
+
5832
5932
  // src/chronicle/sqlite-journal-schema.ts
5833
5933
  var CHRONICLE_SQLITE_FILE = "chronicle.sqlite";
5834
5934
  var LEGACY_JSONL_MIGRATION_KEY = "legacy-jsonl-v1";
5835
5935
  var LEGACY_JSONL_QUARANTINE_KEY = "legacy-jsonl-v1:quarantine";
5836
5936
  var LEGACY_JSONL_BOUNDARY_KEY = "legacy-jsonl-v1:boundary";
5837
- var SCHEMA_VERSION = 2;
5937
+ var SCHEMA_VERSION = 3;
5838
5938
  var Ctor;
5839
5939
  function loadDatabaseSync() {
5840
5940
  if (Ctor) return Ctor;
5841
- if (Ctor === null) throw new Error("node:sqlite is unavailable in this runtime");
5941
+ if (Ctor === null) throw new Error("SQLite is unavailable in this runtime");
5842
5942
  try {
5843
- Ctor = withSqliteExperimentalWarningSuppressed(
5844
- () => createRequire(import.meta.url)("node:sqlite").DatabaseSync
5845
- );
5943
+ Ctor = withSqliteExperimentalWarningSuppressed(loadRuntimeDatabaseSync);
5846
5944
  return Ctor;
5847
5945
  } catch (error) {
5848
5946
  Ctor = null;
5849
5947
  throw new Error(
5850
- "The Chronicle journal needs Node's built-in SQLite (node:sqlite, Node >= 22.5): " + (error instanceof Error ? error.message : String(error))
5948
+ "The Chronicle journal needs node:sqlite (Node >= 22.5) or bun:sqlite: " + (error instanceof Error ? error.message : String(error))
5851
5949
  );
5852
5950
  }
5853
5951
  }
@@ -5862,6 +5960,7 @@ function projectEvent(event) {
5862
5960
  taskId: event.scope.taskId ?? null,
5863
5961
  traceId: event.correlation?.traceId ?? null,
5864
5962
  logicalRequestId: event.correlation?.logicalRequestId ?? null,
5963
+ promptManifestId: event.correlation?.promptManifestId ?? null,
5865
5964
  resourceKind: event.resource?.kind ?? null,
5866
5965
  resourceId: event.resource?.id ?? null,
5867
5966
  resourcePath: event.resource?.path ?? null,
@@ -5886,6 +5985,7 @@ function ensureChronicleSchema(db) {
5886
5985
  task_id TEXT,
5887
5986
  trace_id TEXT,
5888
5987
  logical_request_id TEXT,
5988
+ prompt_manifest_id TEXT,
5889
5989
  resource_kind TEXT,
5890
5990
  resource_id TEXT,
5891
5991
  resource_path TEXT,
@@ -5913,6 +6013,10 @@ function ensureChronicleSchema(db) {
5913
6013
  if (version < 2) {
5914
6014
  db.exec("DROP INDEX IF EXISTS events_resource_path");
5915
6015
  }
6016
+ if (version > 0 && version < 3) {
6017
+ db.exec("ALTER TABLE events ADD COLUMN prompt_manifest_id TEXT");
6018
+ }
6019
+ db.exec("CREATE INDEX IF NOT EXISTS events_prompt_manifest ON events(prompt_manifest_id)");
5916
6020
  if (version !== SCHEMA_VERSION) {
5917
6021
  db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);
5918
6022
  }
@@ -5949,6 +6053,7 @@ function pushDown(query) {
5949
6053
  eq("task_id", query.taskId);
5950
6054
  eq("trace_id", query.traceId);
5951
6055
  eq("logical_request_id", query.logicalRequestId);
6056
+ eq("prompt_manifest_id", query.promptManifestId);
5952
6057
  eq("resource_kind", query.resourceKind);
5953
6058
  eq("resource_id", query.resourceId);
5954
6059
  if (query.eventTypes?.length) {
@@ -6305,6 +6410,7 @@ var ChronicleSqliteJournal = class {
6305
6410
  row.taskId,
6306
6411
  row.traceId,
6307
6412
  row.logicalRequestId,
6413
+ row.promptManifestId,
6308
6414
  row.resourceKind,
6309
6415
  row.resourceId,
6310
6416
  row.resourcePath,
@@ -6464,9 +6570,9 @@ var ChronicleSqliteJournal = class {
6464
6570
  const insert = this.db.prepare(
6465
6571
  `INSERT INTO events (
6466
6572
  day, sequence, event_id, hash, previous_hash, occurred_at, event_type, outcome,
6467
- project_id, session_id, agent_id, task_id, trace_id, logical_request_id,
6573
+ project_id, session_id, agent_id, task_id, trace_id, logical_request_id, prompt_manifest_id,
6468
6574
  resource_kind, resource_id, resource_path, duration_ns, payload
6469
- ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6575
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6470
6576
  );
6471
6577
  const checkpoint = this.db.prepare(
6472
6578
  `INSERT INTO chain_checkpoint (day, sequence, hash) VALUES (?, ?, ?)
@@ -6495,6 +6601,7 @@ var ChronicleSqliteJournal = class {
6495
6601
  row.taskId,
6496
6602
  row.traceId,
6497
6603
  row.logicalRequestId,
6604
+ row.promptManifestId,
6498
6605
  row.resourceKind,
6499
6606
  row.resourceId,
6500
6607
  row.resourcePath,
@@ -6581,9 +6688,9 @@ var ChronicleSqliteJournal = class {
6581
6688
  insert: this.db.prepare(
6582
6689
  `INSERT INTO events (
6583
6690
  day, sequence, event_id, hash, previous_hash, occurred_at, event_type, outcome,
6584
- project_id, session_id, agent_id, task_id, trace_id, logical_request_id,
6691
+ project_id, session_id, agent_id, task_id, trace_id, logical_request_id, prompt_manifest_id,
6585
6692
  resource_kind, resource_id, resource_path, duration_ns, payload
6586
- ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6693
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6587
6694
  ),
6588
6695
  trimBoundary: this.db.prepare(
6589
6696
  "SELECT day, sequence, hash FROM events ORDER BY day, sequence LIMIT 1 OFFSET ?"
@@ -6624,8 +6731,8 @@ var ChronicleSqliteJournal = class {
6624
6731
  };
6625
6732
 
6626
6733
  // src/chronicle/metrics-schema.ts
6627
- import { createRequire as createRequire2 } from "node:module";
6628
- var SCHEMA_VERSION2 = 3;
6734
+ import { loadRuntimeDatabaseSync as loadRuntimeDatabaseSync2 } from "@wrongstack/persistence";
6735
+ var SCHEMA_VERSION2 = 4;
6629
6736
  var READ_CHUNK_BYTES = 1024 * 1024;
6630
6737
  var SQLITE_SOURCE_PREFIX = "sqlite:";
6631
6738
  var SQLITE_INGEST_BATCH = 2e3;
@@ -6643,16 +6750,14 @@ var EMPTY_FAMILIES = {
6643
6750
  var Ctor2;
6644
6751
  function loadDatabaseSync2() {
6645
6752
  if (Ctor2) return Ctor2;
6646
- if (Ctor2 === null) throw new Error("node:sqlite is unavailable in this runtime");
6753
+ if (Ctor2 === null) throw new Error("SQLite is unavailable in this runtime");
6647
6754
  try {
6648
- Ctor2 = withSqliteExperimentalWarningSuppressed(
6649
- () => createRequire2(import.meta.url)("node:sqlite").DatabaseSync
6650
- );
6755
+ Ctor2 = withSqliteExperimentalWarningSuppressed(loadRuntimeDatabaseSync2);
6651
6756
  return Ctor2;
6652
6757
  } catch (error) {
6653
6758
  Ctor2 = null;
6654
6759
  throw new Error(
6655
- "Chronicle metrics need Node's built-in SQLite (node:sqlite, Node >= 22.5): " + (error instanceof Error ? error.message : String(error))
6760
+ "Chronicle metrics need node:sqlite (Node >= 22.5) or bun:sqlite: " + (error instanceof Error ? error.message : String(error))
6656
6761
  );
6657
6762
  }
6658
6763
  }
@@ -6713,6 +6818,9 @@ function ensureMetricsSchema(db) {
6713
6818
  tool_name TEXT NOT NULL DEFAULT '',
6714
6819
  provider_id TEXT NOT NULL DEFAULT '',
6715
6820
  model_id TEXT NOT NULL DEFAULT '',
6821
+ logical_request_id TEXT NOT NULL DEFAULT '',
6822
+ prompt_manifest_id TEXT NOT NULL DEFAULT '',
6823
+ provenance_confidence TEXT NOT NULL DEFAULT 'unknown',
6716
6824
  source TEXT NOT NULL DEFAULT ''
6717
6825
  );
6718
6826
  CREATE INDEX IF NOT EXISTS idx_file_lineage_path ON file_lineage(path_key, occurred_at);
@@ -6805,6 +6913,20 @@ var ChronicleMetricsIngester = class {
6805
6913
  db;
6806
6914
  directory;
6807
6915
  dbPath;
6916
+ /**
6917
+ * Prepared-statement cache for the per-event ingest hot paths. Preparing
6918
+ * the same SQL on every event was the dominant ingest cost; node:sqlite
6919
+ * statements are reusable, so each distinct SQL string is prepared once.
6920
+ */
6921
+ stmtCache = /* @__PURE__ */ new Map();
6922
+ stmt(sql) {
6923
+ let s = this.stmtCache.get(sql);
6924
+ if (!s) {
6925
+ s = this.db.prepare(sql);
6926
+ this.stmtCache.set(sql, s);
6927
+ }
6928
+ return s;
6929
+ }
6808
6930
  async refresh() {
6809
6931
  const result = {
6810
6932
  ingestedEvents: 0,
@@ -6834,7 +6956,7 @@ var ChronicleMetricsIngester = class {
6834
6956
  let base;
6835
6957
  if (!migrated) {
6836
6958
  base = offsets.get(key) ?? 0;
6837
- } else if (boundary !== null && boundary.has(key)) {
6959
+ } else if (boundary?.has(key)) {
6838
6960
  base = Math.max(offsets.get(key) ?? 0, boundary.get(key));
6839
6961
  } else {
6840
6962
  base = rebuilt ? 0 : offsets.get(key) ?? 0;
@@ -7057,11 +7179,11 @@ var ChronicleMetricsIngester = class {
7057
7179
  }
7058
7180
  ingestDailyCounters(event) {
7059
7181
  const day = eventDay(event);
7060
- this.db.prepare("INSERT OR IGNORE INTO daily_counters (day) VALUES (?)").run(day);
7061
- const bump = (sql, ...params) => this.db.prepare(`UPDATE daily_counters SET ${sql} WHERE day = ?`).run(...params, day);
7182
+ this.stmt("INSERT OR IGNORE INTO daily_counters (day) VALUES (?)").run(day);
7183
+ const bump = (sql, ...params) => this.stmt(`UPDATE daily_counters SET ${sql} WHERE day = ?`).run(...params, day);
7062
7184
  const family = signalFamily(event);
7063
7185
  const failed = isTerminalFailure(event) ? 1 : 0;
7064
- this.db.prepare(
7186
+ this.stmt(
7065
7187
  `INSERT INTO family_daily (day, family, count, failure_count) VALUES (?, ?, 1, ?)
7066
7188
  ON CONFLICT(day, family) DO UPDATE SET count = count + 1, failure_count = failure_count + excluded.failure_count`
7067
7189
  ).run(day, family, failed);
@@ -7070,12 +7192,15 @@ var ChronicleMetricsIngester = class {
7070
7192
  bump("cancellations = cancellations + 1");
7071
7193
  if (family === "agent") bump("agent_events = agent_events + 1");
7072
7194
  if (event.correlation.logicalRequestId) {
7073
- this.db.prepare(
7195
+ this.stmt(
7074
7196
  "INSERT OR IGNORE INTO logical_request_daily (day, logical_request_id) VALUES (?, ?)"
7075
7197
  ).run(day, event.correlation.logicalRequestId);
7076
7198
  }
7077
7199
  if (event.scope.agentId) {
7078
- this.db.prepare("INSERT OR IGNORE INTO agent_daily (day, agent_id) VALUES (?, ?)").run(day, event.scope.agentId);
7200
+ this.stmt("INSERT OR IGNORE INTO agent_daily (day, agent_id) VALUES (?, ?)").run(
7201
+ day,
7202
+ event.scope.agentId
7203
+ );
7079
7204
  }
7080
7205
  const type = event.eventType;
7081
7206
  if (type === "decision.requested") bump("decisions = decisions + 1");
@@ -7097,7 +7222,10 @@ var ChronicleMetricsIngester = class {
7097
7222
  if (event.resource?.kind === "file" || type.startsWith("file.")) {
7098
7223
  bump("file_events_all = file_events_all + 1");
7099
7224
  if (event.resource?.path) {
7100
- this.db.prepare("INSERT OR IGNORE INTO file_seen_daily (day, path_key) VALUES (?, ?)").run(day, normalizePathKey(event.resource.path));
7225
+ this.stmt("INSERT OR IGNORE INTO file_seen_daily (day, path_key) VALUES (?, ?)").run(
7226
+ day,
7227
+ normalizePathKey(event.resource.path)
7228
+ );
7101
7229
  }
7102
7230
  }
7103
7231
  }
@@ -7106,8 +7234,10 @@ var ChronicleMetricsIngester = class {
7106
7234
  const providerId = event.runtime?.providerId ?? asString(readPath2(event.attributes ?? {}, "from.providerId")) ?? "";
7107
7235
  const modelId = event.runtime?.modelId ?? asString(readPath2(event.attributes ?? {}, "from.model")) ?? "";
7108
7236
  if (!providerId && !modelId) return;
7109
- this.db.prepare("INSERT OR IGNORE INTO provider_daily (day, provider_id, model_id) VALUES (?, ?, ?)").run(day, providerId, modelId);
7110
- const update = (sql, ...params) => this.db.prepare(
7237
+ this.stmt(
7238
+ "INSERT OR IGNORE INTO provider_daily (day, provider_id, model_id) VALUES (?, ?, ?)"
7239
+ ).run(day, providerId, modelId);
7240
+ const update = (sql, ...params) => this.stmt(
7111
7241
  `UPDATE provider_daily SET ${sql} WHERE day = ? AND provider_id = ? AND model_id = ?`
7112
7242
  ).run(...params, day, providerId, modelId);
7113
7243
  const duration = durationMs2(event);
@@ -7148,7 +7278,7 @@ var ChronicleMetricsIngester = class {
7148
7278
  if (typeof cost !== "number" || !Number.isFinite(cost)) return;
7149
7279
  const scopeKey2 = `${event.scope.projectId ?? ""}\0${event.scope.sessionId ?? ""}\0${event.scope.agentId ?? ""}`;
7150
7280
  const occurredAt = event.occurredAt ?? event.observedAt;
7151
- this.db.prepare(
7281
+ this.stmt(
7152
7282
  `INSERT INTO token_cost (scope_key, day, occurred_at, sequence, cost) VALUES (?, ?, ?, ?, ?)
7153
7283
  ON CONFLICT(scope_key) DO UPDATE SET
7154
7284
  day = excluded.day, occurred_at = excluded.occurred_at,
@@ -7162,8 +7292,8 @@ var ChronicleMetricsIngester = class {
7162
7292
  const taskId = event.scope.taskId ?? stringAt(attributes, "taskId");
7163
7293
  if (!taskId) return;
7164
7294
  const occurredAt = event.occurredAt ?? event.observedAt;
7165
- this.db.prepare("INSERT OR IGNORE INTO task_outcomes (task_id) VALUES (?)").run(taskId);
7166
- const set = (sql, ...params) => this.db.prepare(`UPDATE task_outcomes SET ${sql} WHERE task_id = ?`).run(...params, taskId);
7295
+ this.stmt("INSERT OR IGNORE INTO task_outcomes (task_id) VALUES (?)").run(taskId);
7296
+ const set = (sql, ...params) => this.stmt(`UPDATE task_outcomes SET ${sql} WHERE task_id = ?`).run(...params, taskId);
7167
7297
  const lineage = [
7168
7298
  ["run_id", stringAt(attributes, "runId")],
7169
7299
  ["board_id", event.scope.kanbanBoardId ?? stringAt(attributes, "boardId")],
@@ -7210,11 +7340,12 @@ var ChronicleMetricsIngester = class {
7210
7340
  if (!operation || operation === "read") return;
7211
7341
  const filePath = event.resource?.path ?? stringAt(attributes, "filePath");
7212
7342
  if (!filePath) return;
7213
- this.db.prepare(
7343
+ this.stmt(
7214
7344
  `INSERT OR IGNORE INTO file_lineage
7215
7345
  (event_id, path, path_key, operation, occurred_at, session_id, agent_id, task_id, board_id, run_id,
7216
- tool_name, provider_id, model_id, source)
7217
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
7346
+ tool_name, provider_id, model_id, logical_request_id, prompt_manifest_id,
7347
+ provenance_confidence, source)
7348
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
7218
7349
  ).run(
7219
7350
  event.eventId,
7220
7351
  normalizeKey2(filePath),
@@ -7229,10 +7360,17 @@ var ChronicleMetricsIngester = class {
7229
7360
  stringAt(attributes, "toolName") ?? "",
7230
7361
  event.runtime?.providerId ?? stringAt(attributes, "provider") ?? "",
7231
7362
  event.runtime?.modelId ?? stringAt(attributes, "model") ?? "",
7363
+ event.correlation.logicalRequestId ?? stringAt(attributes, "logicalRequestId") ?? "",
7364
+ event.correlation.promptManifestId ?? stringAt(attributes, "promptManifestId") ?? "",
7365
+ provenanceConfidence(attributes),
7232
7366
  stringAt(attributes, "source") ?? (event.eventType === "file.event" ? "tool" : "external")
7233
7367
  );
7234
7368
  }
7235
7369
  };
7370
+ function provenanceConfidence(attributes) {
7371
+ const value = attributes["provenanceConfidence"];
7372
+ return value === "explicit" || value === "correlated" || value === "inferred" ? value : "unknown";
7373
+ }
7236
7374
 
7237
7375
  // src/chronicle/metrics-store.ts
7238
7376
  var ChronicleMetricsStore = class _ChronicleMetricsStore {
@@ -7364,7 +7502,8 @@ var ChronicleMetricsStore = class _ChronicleMetricsStore {
7364
7502
  const where = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
7365
7503
  params.push(clampLimit(options.limit, 200));
7366
7504
  const projection = `path, operation, occurred_at, session_id, agent_id, task_id, board_id, run_id,
7367
- tool_name, provider_id, model_id, source`;
7505
+ tool_name, provider_id, model_id, logical_request_id, prompt_manifest_id,
7506
+ provenance_confidence, source`;
7368
7507
  const sql = options.latestPerPath ? `SELECT ${projection} FROM (
7369
7508
  SELECT ${projection}, ROW_NUMBER() OVER (
7370
7509
  PARTITION BY path_key ORDER BY occurred_at DESC, event_id DESC
@@ -7385,6 +7524,9 @@ var ChronicleMetricsStore = class _ChronicleMetricsStore {
7385
7524
  toolName: row.tool_name,
7386
7525
  providerId: row.provider_id,
7387
7526
  modelId: row.model_id,
7527
+ logicalRequestId: row.logical_request_id,
7528
+ promptManifestId: row.prompt_manifest_id,
7529
+ provenanceConfidence: row.provenance_confidence,
7388
7530
  source: row.source
7389
7531
  }));
7390
7532
  }
@@ -8034,6 +8176,7 @@ function legacyPartitionPath(location) {
8034
8176
  function useSqliteStore() {
8035
8177
  return process.env["WRONGSTACK_CHRONICLE_STORE"] !== "jsonl";
8036
8178
  }
8179
+ var MAX_OPEN_JOURNAL_DAYS = 2;
8037
8180
  var InlineChronicleProjectAccess = class {
8038
8181
  constructor(options) {
8039
8182
  this.options = options;
@@ -8142,6 +8285,26 @@ var InlineChronicleProjectAccess = class {
8142
8285
  get chronicleDirectory() {
8143
8286
  return path16.join(this.options.projectDir, "chronicle");
8144
8287
  }
8288
+ /**
8289
+ * Drop journals for days we will not write to again.
8290
+ *
8291
+ * Mirrors the daemon's `pruneJournals` in `project-server.ts`: without a cap
8292
+ * an inline access object that lives across midnight kept one open
8293
+ * `ChronicleJournal` (with its write buffer and file handle) per day, forever.
8294
+ */
8295
+ pruneJournals(currentDay) {
8296
+ if (this.journals.size <= MAX_OPEN_JOURNAL_DAYS) return;
8297
+ const keep = new Set(
8298
+ [...this.journals.keys()].sort().reverse().slice(0, MAX_OPEN_JOURNAL_DAYS)
8299
+ );
8300
+ keep.add(currentDay);
8301
+ for (const [day, journal] of this.journals) {
8302
+ if (keep.has(day)) continue;
8303
+ this.journals.delete(day);
8304
+ void journal.flush().catch(() => {
8305
+ });
8306
+ }
8307
+ }
8145
8308
  journalForToday() {
8146
8309
  const now = /* @__PURE__ */ new Date();
8147
8310
  const day = now.toISOString().slice(0, 10);
@@ -8158,6 +8321,7 @@ var InlineChronicleProjectAccess = class {
8158
8321
  retentionDays: this.options.retentionDays
8159
8322
  });
8160
8323
  this.journals.set(day, journal);
8324
+ this.pruneJournals(day);
8161
8325
  }
8162
8326
  return journal;
8163
8327
  }
@@ -11600,14 +11764,21 @@ async function openInEditor(filePath, env = process.env) {
11600
11764
  context: { filePath }
11601
11765
  });
11602
11766
  }
11603
- const child = spawn5(parts[0], [...parts.slice(1), filePath], {
11767
+ const editorArgs = [...parts.slice(1), filePath];
11768
+ const child = shell ? (() => {
11769
+ const inv = buildWin32CmdShimInvocation(parts[0], editorArgs);
11770
+ return spawn5(inv.command, inv.args, {
11771
+ stdio: "ignore",
11772
+ detached: true,
11773
+ windowsVerbatimArguments: inv.windowsVerbatimArguments,
11774
+ // Suppresses the console flash the cmd.exe wrapper would otherwise
11775
+ // show before the editor appears. Repo convention — see
11776
+ // `core/tests/architecture/spawn-convention.test.ts`.
11777
+ windowsHide: true
11778
+ });
11779
+ })() : spawn5(parts[0], editorArgs, {
11604
11780
  stdio: "ignore",
11605
11781
  detached: true,
11606
- shell,
11607
- // `shell` routes through cmd.exe on win32, which flashes a console window
11608
- // before the editor appears. A GUI editor is unaffected by the flag; the
11609
- // shell wrapper is what it suppresses. Repo convention — see
11610
- // `core/tests/architecture/spawn-convention.test.ts`.
11611
11782
  windowsHide: true
11612
11783
  });
11613
11784
  child.unref();
@@ -13789,7 +13960,7 @@ function walk2(node, vault, transform) {
13789
13960
  }
13790
13961
  return out;
13791
13962
  }
13792
- var SECRET_KEY_PATTERN = /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key|token\b)/i;
13963
+ var SECRET_KEY_PATTERN = /(?:api[-_]?key|auth[-_]?token|authorization|proxy-authorization|cookie|bearer|secret|password|passwd|pwd|refresh[-_]?token|session[-_]?key|access[_-]?token|private[_-]?key|token\b)/i;
13793
13964
  var NON_SECRET_OVERRIDES = /* @__PURE__ */ new Set(["publickey", "public_key"]);
13794
13965
  function isSecretField(name) {
13795
13966
  const lc = name.toLowerCase();