@flyingrobots/graft 0.7.0 → 0.7.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 (283) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +0 -1
  3. package/bin/graft.js +26 -26
  4. package/dist/cli/init-client-config.d.ts.map +1 -1
  5. package/dist/cli/init-client-config.js +40 -4
  6. package/dist/cli/init-client-config.js.map +1 -1
  7. package/dist/cli/init-model.js +2 -2
  8. package/dist/cli/init-model.js.map +1 -1
  9. package/dist/cli/json-document.d.ts +1 -0
  10. package/dist/cli/json-document.d.ts.map +1 -1
  11. package/dist/cli/json-document.js +3 -0
  12. package/dist/cli/json-document.js.map +1 -1
  13. package/dist/hooks/posttooluse-read.js +1 -1
  14. package/dist/hooks/posttooluse-read.js.map +1 -1
  15. package/dist/hooks/pretooluse-read.js +1 -1
  16. package/dist/hooks/pretooluse-read.js.map +1 -1
  17. package/dist/mcp/daemon-worker-child-pool.d.ts +2 -0
  18. package/dist/mcp/daemon-worker-child-pool.d.ts.map +1 -1
  19. package/dist/mcp/daemon-worker-child-pool.js +10 -3
  20. package/dist/mcp/daemon-worker-child-pool.js.map +1 -1
  21. package/dist/mcp/daemon-worker-pool.d.ts +1 -1
  22. package/dist/mcp/daemon-worker-pool.d.ts.map +1 -1
  23. package/dist/mcp/daemon-worker-pool.js +1 -1
  24. package/dist/mcp/daemon-worker-pool.js.map +1 -1
  25. package/dist/version.d.ts.map +1 -1
  26. package/dist/version.js +3 -1
  27. package/dist/version.js.map +1 -1
  28. package/docs/SETUP.md +11 -7
  29. package/package.json +9 -4
  30. package/src/adapters/canonical-json.ts +0 -56
  31. package/src/adapters/json-text-decoder.ts +0 -11
  32. package/src/adapters/node-fs.ts +0 -43
  33. package/src/adapters/node-git.ts +0 -47
  34. package/src/adapters/node-paths.ts +0 -35
  35. package/src/adapters/node-process-runner.ts +0 -27
  36. package/src/adapters/repo-paths.ts +0 -63
  37. package/src/adapters/rotating-ndjson-log.ts +0 -66
  38. package/src/api/index.ts +0 -107
  39. package/src/api/repo-local-graft.ts +0 -21
  40. package/src/api/repo-workspace.ts +0 -34
  41. package/src/api/tool-bridge.ts +0 -22
  42. package/src/cli/activity-render.ts +0 -112
  43. package/src/cli/cli-error.ts +0 -192
  44. package/src/cli/command-parser.ts +0 -484
  45. package/src/cli/daemon-status-model.ts +0 -238
  46. package/src/cli/daemon-status-render.ts +0 -102
  47. package/src/cli/daemon-status.ts +0 -100
  48. package/src/cli/entrypoint.ts +0 -18
  49. package/src/cli/git-graft-enhance-model.ts +0 -131
  50. package/src/cli/git-graft-enhance-render.ts +0 -33
  51. package/src/cli/git-graft-enhance.ts +0 -54
  52. package/src/cli/index-cmd.ts +0 -90
  53. package/src/cli/index-model.ts +0 -75
  54. package/src/cli/init-bootstrap.ts +0 -47
  55. package/src/cli/init-client-config.ts +0 -428
  56. package/src/cli/init-model.ts +0 -284
  57. package/src/cli/init-render.ts +0 -54
  58. package/src/cli/init-target-hooks.ts +0 -93
  59. package/src/cli/init.ts +0 -124
  60. package/src/cli/json-document.ts +0 -155
  61. package/src/cli/local-history-dag-model.ts +0 -437
  62. package/src/cli/local-history-dag-render.ts +0 -133
  63. package/src/cli/local-history-dag.ts +0 -103
  64. package/src/cli/main.ts +0 -197
  65. package/src/cli/migrate-local-history.ts +0 -119
  66. package/src/cli/peer-command.ts +0 -60
  67. package/src/contracts/capabilities.ts +0 -540
  68. package/src/contracts/causal-ontology.ts +0 -627
  69. package/src/contracts/causal-surface-next-action.ts +0 -18
  70. package/src/contracts/json-object.ts +0 -18
  71. package/src/contracts/mcp-runtime.ts +0 -16
  72. package/src/contracts/output-schema-cli.ts +0 -103
  73. package/src/contracts/output-schema-fragments.ts +0 -881
  74. package/src/contracts/output-schema-mcp.ts +0 -501
  75. package/src/contracts/output-schema-meta.ts +0 -119
  76. package/src/contracts/output-schemas.ts +0 -1524
  77. package/src/git/diff.ts +0 -161
  78. package/src/git/target-git-hook-bootstrap.ts +0 -56
  79. package/src/guards/stream-boundary.ts +0 -110
  80. package/src/hooks/posttooluse-read.ts +0 -54
  81. package/src/hooks/pretooluse-read.ts +0 -52
  82. package/src/hooks/read-governor.ts +0 -95
  83. package/src/hooks/read-messages.ts +0 -53
  84. package/src/hooks/shared.ts +0 -168
  85. package/src/index.ts +0 -1
  86. package/src/mcp/burden.ts +0 -132
  87. package/src/mcp/cache.ts +0 -7
  88. package/src/mcp/cached-file.ts +0 -1
  89. package/src/mcp/context.ts +0 -150
  90. package/src/mcp/control-plane/authz-storage.ts +0 -129
  91. package/src/mcp/control-plane/session-registry.ts +0 -115
  92. package/src/mcp/control-plane/status-projection.ts +0 -76
  93. package/src/mcp/control-plane/types.ts +0 -144
  94. package/src/mcp/daemon-bootstrap.ts +0 -100
  95. package/src/mcp/daemon-control-plane.ts +0 -310
  96. package/src/mcp/daemon-job-scheduler.ts +0 -279
  97. package/src/mcp/daemon-repos.ts +0 -99
  98. package/src/mcp/daemon-server.ts +0 -172
  99. package/src/mcp/daemon-session-host.ts +0 -234
  100. package/src/mcp/daemon-stdio-bridge.ts +0 -262
  101. package/src/mcp/daemon-worker-child-pool.ts +0 -213
  102. package/src/mcp/daemon-worker-inline-pool.ts +0 -46
  103. package/src/mcp/daemon-worker-pool.ts +0 -7
  104. package/src/mcp/daemon-worker-process.ts +0 -52
  105. package/src/mcp/daemon-worker-types.ts +0 -66
  106. package/src/mcp/metrics.ts +0 -176
  107. package/src/mcp/monitor-health.ts +0 -83
  108. package/src/mcp/monitor-persistence.ts +0 -54
  109. package/src/mcp/monitor-tick-job.ts +0 -153
  110. package/src/mcp/monitor-types.ts +0 -103
  111. package/src/mcp/persisted-local-history-graph.ts +0 -592
  112. package/src/mcp/persisted-local-history-policy.ts +0 -501
  113. package/src/mcp/persisted-local-history-views.ts +0 -250
  114. package/src/mcp/persisted-local-history.ts +0 -1621
  115. package/src/mcp/persistent-monitor-runtime.ts +0 -482
  116. package/src/mcp/policy.ts +0 -75
  117. package/src/mcp/receipt.ts +0 -201
  118. package/src/mcp/repo-concurrency.ts +0 -392
  119. package/src/mcp/repo-overview/filter.ts +0 -44
  120. package/src/mcp/repo-overview/types.ts +0 -63
  121. package/src/mcp/repo-overview/view-projection.ts +0 -90
  122. package/src/mcp/repo-state-git.ts +0 -247
  123. package/src/mcp/repo-state-observation.ts +0 -203
  124. package/src/mcp/repo-state-transition.ts +0 -151
  125. package/src/mcp/repo-state-types.ts +0 -107
  126. package/src/mcp/repo-state.ts +0 -146
  127. package/src/mcp/repo-tool-job.ts +0 -102
  128. package/src/mcp/repo-tool-worker-context.ts +0 -215
  129. package/src/mcp/repo-workspace.ts +0 -21
  130. package/src/mcp/run-capture-config.ts +0 -33
  131. package/src/mcp/runtime-causal-context.ts +0 -72
  132. package/src/mcp/runtime-observability.ts +0 -217
  133. package/src/mcp/runtime-staged-target.ts +0 -161
  134. package/src/mcp/runtime-workspace-overlay.ts +0 -255
  135. package/src/mcp/secret-scrub.ts +0 -89
  136. package/src/mcp/semantic-transition-guidance.ts +0 -60
  137. package/src/mcp/semantic-transition-summary.ts +0 -130
  138. package/src/mcp/server-context.ts +0 -236
  139. package/src/mcp/server-invocation.ts +0 -388
  140. package/src/mcp/server-tool-access.ts +0 -148
  141. package/src/mcp/server.ts +0 -270
  142. package/src/mcp/stdio-server.ts +0 -26
  143. package/src/mcp/stdio.ts +0 -3
  144. package/src/mcp/tool-registry.ts +0 -98
  145. package/src/mcp/tools/activity-view.ts +0 -325
  146. package/src/mcp/tools/budget.ts +0 -23
  147. package/src/mcp/tools/causal-attach.ts +0 -67
  148. package/src/mcp/tools/causal-status.ts +0 -58
  149. package/src/mcp/tools/changed-since.ts +0 -71
  150. package/src/mcp/tools/code-find.ts +0 -192
  151. package/src/mcp/tools/code-refs.ts +0 -470
  152. package/src/mcp/tools/code-show.ts +0 -289
  153. package/src/mcp/tools/daemon-monitors.ts +0 -14
  154. package/src/mcp/tools/daemon-repos.ts +0 -22
  155. package/src/mcp/tools/daemon-sessions.ts +0 -14
  156. package/src/mcp/tools/daemon-status.ts +0 -12
  157. package/src/mcp/tools/diagnostic-models.ts +0 -97
  158. package/src/mcp/tools/doctor.ts +0 -83
  159. package/src/mcp/tools/explain.ts +0 -84
  160. package/src/mcp/tools/export-surface-diff.ts +0 -26
  161. package/src/mcp/tools/file-outline.ts +0 -67
  162. package/src/mcp/tools/git-files.ts +0 -73
  163. package/src/mcp/tools/graft-diff.ts +0 -35
  164. package/src/mcp/tools/graft-edit.ts +0 -276
  165. package/src/mcp/tools/knowledge-map.ts +0 -34
  166. package/src/mcp/tools/map-collector.ts +0 -338
  167. package/src/mcp/tools/map.ts +0 -185
  168. package/src/mcp/tools/monitor-nudge.ts +0 -19
  169. package/src/mcp/tools/monitor-pause.ts +0 -18
  170. package/src/mcp/tools/monitor-resume.ts +0 -18
  171. package/src/mcp/tools/monitor-start.ts +0 -20
  172. package/src/mcp/tools/monitor-stop.ts +0 -18
  173. package/src/mcp/tools/precision-live.ts +0 -158
  174. package/src/mcp/tools/precision-match.ts +0 -54
  175. package/src/mcp/tools/precision-paths.ts +0 -59
  176. package/src/mcp/tools/precision-query.ts +0 -127
  177. package/src/mcp/tools/precision-show.ts +0 -86
  178. package/src/mcp/tools/precision-visibility.ts +0 -44
  179. package/src/mcp/tools/precision-warp.ts +0 -101
  180. package/src/mcp/tools/precision.ts +0 -21
  181. package/src/mcp/tools/read-range.ts +0 -28
  182. package/src/mcp/tools/refactor-difficulty.ts +0 -42
  183. package/src/mcp/tools/run-capture.ts +0 -149
  184. package/src/mcp/tools/safe-read.ts +0 -148
  185. package/src/mcp/tools/since.ts +0 -53
  186. package/src/mcp/tools/state.ts +0 -39
  187. package/src/mcp/tools/stats.ts +0 -24
  188. package/src/mcp/tools/structural-blame.ts +0 -55
  189. package/src/mcp/tools/structural-churn.ts +0 -28
  190. package/src/mcp/tools/structural-log.ts +0 -41
  191. package/src/mcp/tools/structural-review.ts +0 -63
  192. package/src/mcp/tools/workspace-authorizations.ts +0 -14
  193. package/src/mcp/tools/workspace-authorize.ts +0 -20
  194. package/src/mcp/tools/workspace-bind.ts +0 -25
  195. package/src/mcp/tools/workspace-rebind.ts +0 -25
  196. package/src/mcp/tools/workspace-revoke.ts +0 -18
  197. package/src/mcp/tools/workspace-status.ts +0 -12
  198. package/src/mcp/warp-pool.ts +0 -36
  199. package/src/mcp/workspace-read-observation-model.ts +0 -48
  200. package/src/mcp/workspace-read-observation.ts +0 -103
  201. package/src/mcp/workspace-router-capability.ts +0 -32
  202. package/src/mcp/workspace-router-history.ts +0 -233
  203. package/src/mcp/workspace-router-model.ts +0 -131
  204. package/src/mcp/workspace-router-resolution.ts +0 -71
  205. package/src/mcp/workspace-router-runtime.ts +0 -246
  206. package/src/mcp/workspace-router.ts +0 -688
  207. package/src/metrics/logger.ts +0 -40
  208. package/src/metrics/types.ts +0 -40
  209. package/src/operations/adaptive-projection.ts +0 -54
  210. package/src/operations/agent-handoff.ts +0 -59
  211. package/src/operations/cached-file.ts +0 -40
  212. package/src/operations/capture-range.ts +0 -64
  213. package/src/operations/conversation-primer.ts +0 -82
  214. package/src/operations/cross-session-resume.ts +0 -104
  215. package/src/operations/deterministic-replay.ts +0 -143
  216. package/src/operations/diff-identity.ts +0 -127
  217. package/src/operations/export-surface-diff.ts +0 -355
  218. package/src/operations/file-outline.ts +0 -48
  219. package/src/operations/footprint-parallelism.ts +0 -98
  220. package/src/operations/graft-diff.ts +0 -169
  221. package/src/operations/horizon-of-readability.ts +0 -60
  222. package/src/operations/import-reference-impact.ts +0 -143
  223. package/src/operations/knowledge-map.ts +0 -96
  224. package/src/operations/observation-cache.ts +0 -165
  225. package/src/operations/projection-safety.ts +0 -111
  226. package/src/operations/read-range.ts +0 -64
  227. package/src/operations/repo-workspace.ts +0 -339
  228. package/src/operations/result-dto.ts +0 -23
  229. package/src/operations/safe-read.ts +0 -118
  230. package/src/operations/semantic-drift.ts +0 -70
  231. package/src/operations/session-filtration.ts +0 -76
  232. package/src/operations/session-replay.ts +0 -73
  233. package/src/operations/sludge-detector.ts +0 -356
  234. package/src/operations/state.ts +0 -66
  235. package/src/operations/structural-blame.ts +0 -197
  236. package/src/operations/structural-review.ts +0 -317
  237. package/src/operations/structured-buffer-compare.ts +0 -429
  238. package/src/operations/structured-buffer-model.ts +0 -456
  239. package/src/operations/structured-buffer-query.ts +0 -794
  240. package/src/operations/structured-buffer.ts +0 -181
  241. package/src/operations/teaching-hints.ts +0 -52
  242. package/src/parser/diff.ts +0 -320
  243. package/src/parser/lang.ts +0 -71
  244. package/src/parser/markdown.ts +0 -197
  245. package/src/parser/outline.ts +0 -316
  246. package/src/parser/runtime.ts +0 -71
  247. package/src/parser/types.ts +0 -75
  248. package/src/policy/evaluate.ts +0 -178
  249. package/src/policy/graftignore.ts +0 -6
  250. package/src/policy/types.ts +0 -87
  251. package/src/ports/codec.ts +0 -13
  252. package/src/ports/filesystem.ts +0 -18
  253. package/src/ports/git.ts +0 -16
  254. package/src/ports/guards.ts +0 -68
  255. package/src/ports/paths.ts +0 -22
  256. package/src/ports/process-runner.ts +0 -22
  257. package/src/release/security-gate.ts +0 -102
  258. package/src/session/tracker.ts +0 -145
  259. package/src/session/types.ts +0 -23
  260. package/src/version.ts +0 -3
  261. package/src/warp/ast-emitter.ts +0 -133
  262. package/src/warp/ast-import-resolver.ts +0 -333
  263. package/src/warp/commit-meta.ts +0 -19
  264. package/src/warp/context.ts +0 -48
  265. package/src/warp/dead-symbols.ts +0 -174
  266. package/src/warp/drift-sentinel.ts +0 -101
  267. package/src/warp/index-head.ts +0 -348
  268. package/src/warp/observers.ts +0 -101
  269. package/src/warp/open.ts +0 -67
  270. package/src/warp/outline-diff-trailer.ts +0 -100
  271. package/src/warp/plumbing.d.ts +0 -24
  272. package/src/warp/refactor-difficulty.ts +0 -257
  273. package/src/warp/references.ts +0 -77
  274. package/src/warp/stale-docs.ts +0 -210
  275. package/src/warp/structural-drift-detection.ts +0 -97
  276. package/src/warp/structural-queries.ts +0 -258
  277. package/src/warp/symbol-timeline.ts +0 -209
  278. package/src/warp/traverse-hydrate.ts +0 -61
  279. package/src/warp/warp-reference-count.ts +0 -47
  280. package/src/warp/warp-structural-blame.ts +0 -65
  281. package/src/warp/warp-structural-churn.ts +0 -309
  282. package/src/warp/warp-structural-log.ts +0 -149
  283. package/src/warp/writer-id.ts +0 -30
@@ -1,217 +0,0 @@
1
- import * as path from "node:path";
2
- import { ZodError } from "zod";
3
- import { RotatingNdjsonLog } from "../adapters/rotating-ndjson-log.js";
4
- import type { JsonCodec } from "../ports/codec.js";
5
- import type { FileSystem } from "../ports/filesystem.js";
6
- import type { MetricsSnapshot } from "./metrics.js";
7
- import type { BurdenKind } from "./burden.js";
8
- import type { GovernorDepth } from "../session/types.js";
9
-
10
- export type RuntimeLogPolicy = "metadata_only";
11
-
12
- export interface RuntimeObservabilityState {
13
- readonly enabled: boolean;
14
- readonly logPath: string;
15
- readonly maxBytes: number;
16
- readonly logPolicy: RuntimeLogPolicy;
17
- }
18
-
19
- export interface ResolveRuntimeObservabilityOptions {
20
- readonly graftDir: string;
21
- readonly env?: Readonly<Record<string, string | undefined>>;
22
- readonly overrides?: Partial<RuntimeObservabilityState>;
23
- }
24
-
25
- const DEFAULT_MAX_BYTES = 1_048_576;
26
- const DEFAULT_POLICY: RuntimeLogPolicy = "metadata_only";
27
-
28
- function parseBooleanFlag(value: string | undefined, fallback: boolean): boolean {
29
- if (value === undefined) return fallback;
30
- const normalized = value.trim().toLowerCase();
31
- if ([ "0", "false", "no", "off" ].includes(normalized)) return false;
32
- if ([ "1", "true", "yes", "on" ].includes(normalized)) return true;
33
- return fallback;
34
- }
35
-
36
- function parsePositiveInteger(value: string | undefined, fallback: number): number {
37
- if (value === undefined) return fallback;
38
- const parsed = Number.parseInt(value, 10);
39
- return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
40
- }
41
-
42
- export function resolveRuntimeObservabilityState(
43
- options: ResolveRuntimeObservabilityOptions,
44
- ): RuntimeObservabilityState {
45
- const env = options.env ?? process.env;
46
- const base: RuntimeObservabilityState = {
47
- enabled: parseBooleanFlag(env["GRAFT_ENABLE_MCP_RUNTIME_LOG"], true),
48
- logPath: env["GRAFT_MCP_RUNTIME_LOG_PATH"]?.trim() ?? path.join(options.graftDir, "logs", "mcp-runtime.ndjson"),
49
- maxBytes: parsePositiveInteger(env["GRAFT_MCP_RUNTIME_LOG_MAX_BYTES"], DEFAULT_MAX_BYTES),
50
- logPolicy: DEFAULT_POLICY,
51
- };
52
- return {
53
- ...base,
54
- ...options.overrides,
55
- };
56
- }
57
-
58
- export interface RuntimeSessionStartedEvent {
59
- readonly event: "session_started";
60
- readonly sessionId: string;
61
- readonly logPath: string;
62
- readonly logPolicy: RuntimeLogPolicy;
63
- }
64
-
65
- export interface RuntimeToolCallStartedEvent {
66
- readonly event: "tool_call_started";
67
- readonly sessionId: string;
68
- readonly traceId: string;
69
- readonly tool: string;
70
- readonly argKeys: readonly string[];
71
- readonly sessionDepth: GovernorDepth;
72
- }
73
-
74
- /**
75
- * Per-tool-call footprint capturing which files, symbols, and regions
76
- * were observed. Enables provenance tracing at sub-file granularity.
77
- */
78
- export interface ToolCallFootprint {
79
- /** File paths read or scanned by this tool call. */
80
- readonly paths: readonly string[];
81
- /** Symbol names inspected (when available from outline/precision tools). */
82
- readonly symbols: readonly string[];
83
- /** Line regions accessed (when available from read_range). */
84
- readonly regions: readonly ToolCallFootprintRegion[];
85
- }
86
-
87
- /** A line region within a specific file. */
88
- export interface ToolCallFootprintRegion {
89
- readonly path: string;
90
- readonly startLine: number;
91
- readonly endLine: number;
92
- }
93
-
94
- export interface RuntimeToolCallCompletedEvent {
95
- readonly event: "tool_call_completed";
96
- readonly sessionId: string;
97
- readonly traceId: string;
98
- readonly seq: number;
99
- readonly tool: string;
100
- readonly latencyMs: number;
101
- readonly projection: string;
102
- readonly reason: string;
103
- readonly burdenKind: BurdenKind;
104
- readonly nonReadBurden: boolean;
105
- readonly returnedBytes: number;
106
- readonly fileBytes: number | null;
107
- readonly sessionDepth: GovernorDepth;
108
- readonly tripwireSignals: readonly string[];
109
- readonly metrics: MetricsSnapshot;
110
- /** Optional sub-file footprint for provenance granularity. */
111
- readonly footprint?: ToolCallFootprint;
112
- }
113
-
114
- export type RuntimeFailureKind = "validation_error" | "tool_error" | "unknown_error";
115
-
116
- export interface RuntimeToolCallFailedEvent {
117
- readonly event: "tool_call_failed";
118
- readonly sessionId: string;
119
- readonly traceId: string;
120
- readonly tool: string;
121
- readonly latencyMs: number;
122
- readonly sessionDepth: GovernorDepth;
123
- readonly argKeys: readonly string[];
124
- readonly errorKind: RuntimeFailureKind;
125
- readonly errorName: string;
126
- }
127
-
128
- type RuntimeEvent =
129
- | RuntimeSessionStartedEvent
130
- | RuntimeToolCallStartedEvent
131
- | RuntimeToolCallCompletedEvent
132
- | RuntimeToolCallFailedEvent;
133
-
134
- export interface RuntimeEventLoggerOptions {
135
- readonly fs: FileSystem;
136
- readonly codec: JsonCodec;
137
- readonly logPath: string;
138
- readonly maxBytes: number;
139
- }
140
-
141
- export async function ensureGraftDirExcluded(
142
- projectRoot: string,
143
- graftDir: string,
144
- fs: FileSystem,
145
- ): Promise<void> {
146
- const relative = path.relative(projectRoot, graftDir);
147
- if (relative.length === 0 || relative.startsWith("..") || path.isAbsolute(relative)) {
148
- return;
149
- }
150
-
151
- const entry = relative.replaceAll(path.sep, "/").replace(/\/?$/, "/");
152
- const excludePath = path.join(projectRoot, ".git", "info", "exclude");
153
-
154
- let existing: string;
155
- try {
156
- existing = await fs.readFile(excludePath, "utf-8");
157
- } catch {
158
- return;
159
- }
160
-
161
- const entries = existing
162
- .split(/\r?\n/u)
163
- .map((line) => line.trim())
164
- .filter((line) => line.length > 0);
165
-
166
- if (entries.includes(entry)) {
167
- return;
168
- }
169
-
170
- const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
171
- await fs.appendFile(excludePath, `${prefix}${entry}\n`, "utf-8");
172
- }
173
-
174
- export function classifyRuntimeFailure(error: unknown): {
175
- kind: RuntimeFailureKind;
176
- name: string;
177
- } {
178
- if (error instanceof ZodError) {
179
- return { kind: "validation_error", name: error.name };
180
- }
181
- if (error instanceof Error) {
182
- return { kind: "tool_error", name: error.name };
183
- }
184
- return { kind: "unknown_error", name: "UnknownError" };
185
- }
186
-
187
- export { sanitizeArgValues } from "./secret-scrub.js";
188
-
189
- export function sanitizeArgKeys(args: Record<string, unknown>): string[] {
190
- return Object.keys(args).sort();
191
- }
192
-
193
- export class RuntimeEventLogger {
194
- private readonly logWriter: RotatingNdjsonLog;
195
- private queue: Promise<void> = Promise.resolve();
196
-
197
- constructor(options: RuntimeEventLoggerOptions) {
198
- this.logWriter = new RotatingNdjsonLog({
199
- fs: options.fs,
200
- codec: options.codec,
201
- logPath: options.logPath,
202
- maxBytes: options.maxBytes,
203
- });
204
- }
205
-
206
- log(event: RuntimeEvent): Promise<void> {
207
- const prior = this.queue.catch((_error: unknown) => undefined);
208
- this.queue = prior
209
- .then(async () => {
210
- await this.logWriter.append({
211
- ts: new Date().toISOString(),
212
- ...event,
213
- });
214
- });
215
- return this.queue;
216
- }
217
- }
@@ -1,161 +0,0 @@
1
- import * as crypto from "node:crypto";
2
- import type { AttributionSummary, StagedTarget } from "../contracts/causal-ontology.js";
3
- import type { RepoObservation } from "./repo-state.js";
4
- import type { RuntimeCausalContext } from "./runtime-causal-context.js";
5
- import type { WorkspaceStatus } from "./workspace-router.js";
6
-
7
- const FULL_FILE_SAFE_STATUSES = new Set(["A", "D", "R", "C", "T"]);
8
-
9
- export interface RuntimeStagedTargetNone {
10
- readonly availability: "none";
11
- readonly stability: "runtime_local";
12
- readonly provenanceLevel: "artifact_history";
13
- }
14
-
15
- export interface RuntimeStagedTargetFullFile {
16
- readonly availability: "full_file";
17
- readonly stability: "runtime_local";
18
- readonly provenanceLevel: "artifact_history";
19
- readonly attribution: AttributionSummary;
20
- readonly target: StagedTarget;
21
- }
22
-
23
- export interface RuntimeStagedTargetAmbiguous {
24
- readonly availability: "ambiguous";
25
- readonly stability: "runtime_local";
26
- readonly provenanceLevel: "artifact_history";
27
- readonly attribution: AttributionSummary;
28
- readonly reason:
29
- | "missing_head_commit"
30
- | "missing_workspace_overlay"
31
- | "modified_path_selection_requires_deeper_evidence";
32
- readonly observedStagedPaths: number;
33
- readonly ambiguousPaths: readonly string[];
34
- }
35
-
36
- export type RuntimeStagedTarget =
37
- | RuntimeStagedTargetNone
38
- | RuntimeStagedTargetFullFile
39
- | RuntimeStagedTargetAmbiguous;
40
-
41
- interface MergedStatusEntry {
42
- readonly path: string;
43
- readonly stagedStatus: string;
44
- readonly worktreeStatus: string;
45
- }
46
-
47
- function stableId(prefix: string, input: string): string {
48
- return `${prefix}:${crypto.createHash("sha256").update(input).digest("hex").slice(0, 16)}`;
49
- }
50
-
51
- function parseMergedStatusLine(line: string): MergedStatusEntry | null {
52
- if (line.length < 4) return null;
53
- return {
54
- stagedStatus: line[0] ?? " ",
55
- worktreeStatus: line[1] ?? " ",
56
- path: line.slice(3),
57
- };
58
- }
59
-
60
- function stagedEntries(statusLines: readonly string[]): readonly MergedStatusEntry[] {
61
- return statusLines
62
- .map(parseMergedStatusLine)
63
- .filter((entry): entry is MergedStatusEntry => entry !== null && entry.stagedStatus !== " " && entry.stagedStatus !== "?");
64
- }
65
-
66
- function isFullFileSafe(entry: MergedStatusEntry): boolean {
67
- return entry.worktreeStatus === " " && FULL_FILE_SAFE_STATUSES.has(entry.stagedStatus);
68
- }
69
-
70
- export function buildRuntimeStagedTarget(
71
- status: WorkspaceStatus,
72
- causalContext: RuntimeCausalContext,
73
- repoState: RepoObservation,
74
- attribution: AttributionSummary,
75
- ): RuntimeStagedTarget {
76
- const observedEntries = stagedEntries(repoState.statusLines);
77
- if (observedEntries.length === 0) {
78
- return {
79
- availability: "none",
80
- stability: "runtime_local",
81
- provenanceLevel: "artifact_history",
82
- };
83
- }
84
-
85
- if (repoState.headSha === null) {
86
- return {
87
- availability: "ambiguous",
88
- stability: "runtime_local",
89
- provenanceLevel: "artifact_history",
90
- attribution,
91
- reason: "missing_head_commit",
92
- observedStagedPaths: observedEntries.length,
93
- ambiguousPaths: observedEntries.map((entry) => entry.path),
94
- };
95
- }
96
-
97
- if (repoState.workspaceOverlayId === null || status.repoId === null || status.worktreeId === null) {
98
- return {
99
- availability: "ambiguous",
100
- stability: "runtime_local",
101
- provenanceLevel: "artifact_history",
102
- attribution,
103
- reason: "missing_workspace_overlay",
104
- observedStagedPaths: observedEntries.length,
105
- ambiguousPaths: observedEntries.map((entry) => entry.path),
106
- };
107
- }
108
-
109
- const ambiguousEntries = observedEntries.filter((entry) => !isFullFileSafe(entry));
110
- if (ambiguousEntries.length > 0) {
111
- return {
112
- availability: "ambiguous",
113
- stability: "runtime_local",
114
- provenanceLevel: "artifact_history",
115
- attribution,
116
- reason: "modified_path_selection_requires_deeper_evidence",
117
- observedStagedPaths: observedEntries.length,
118
- ambiguousPaths: ambiguousEntries.map((entry) => entry.path),
119
- };
120
- }
121
-
122
- const selectionEntries = observedEntries.map((entry) => ({
123
- path: entry.path,
124
- symbols: [],
125
- regions: [],
126
- }));
127
-
128
- const target: StagedTarget = {
129
- targetId: stableId(
130
- "target",
131
- JSON.stringify({
132
- repoId: status.repoId,
133
- worktreeId: status.worktreeId,
134
- checkoutEpochId: causalContext.checkoutEpochId,
135
- workspaceOverlayId: repoState.workspaceOverlayId,
136
- headSha: repoState.headSha,
137
- paths: selectionEntries.map((entry) => entry.path),
138
- }),
139
- ),
140
- targetKind: "staged_target",
141
- repoId: status.repoId,
142
- worktreeId: status.worktreeId,
143
- checkoutEpochId: causalContext.checkoutEpochId,
144
- workspaceOverlayId: repoState.workspaceOverlayId,
145
- selectedAt: repoState.observedAt,
146
- selectionKind: "full_file",
147
- selectionEntries,
148
- base: {
149
- headCommitSha: repoState.headSha,
150
- indexTreeSha: null,
151
- },
152
- };
153
-
154
- return {
155
- availability: "full_file",
156
- stability: "runtime_local",
157
- provenanceLevel: "artifact_history",
158
- attribution,
159
- target,
160
- };
161
- }
@@ -1,255 +0,0 @@
1
- import * as path from "node:path";
2
- import type { FileSystem } from "../ports/filesystem.js";
3
- import type { GitClient } from "../ports/git.js";
4
- import {
5
- isRecognizedTargetGitHook,
6
- isTargetGitTransitionHookName,
7
- resolveGitHooksPath,
8
- TARGET_GIT_TRANSITION_HOOKS,
9
- } from "../git/target-git-hook-bootstrap.js";
10
- import type {
11
- RepoTransition,
12
- WorkspaceOverlaySummary,
13
- } from "./repo-state.js";
14
-
15
- export type GitHookBootstrapPosture = "absent" | "external_unknown" | "installed";
16
- export type WorkspaceOverlayObservationMode =
17
- | "inferred_between_tool_calls"
18
- | "hook_observed_checkout_boundaries";
19
- export type WorkspaceOverlayLineagePosture =
20
- | "stable"
21
- | "forked_after_transition";
22
- export type WorkspaceOverlayBoundaryAuthority =
23
- | "none"
24
- | "repo_snapshot"
25
- | "hook_observed";
26
- export type WorkspaceOverlayDegradedReason =
27
- | "target_repo_hooks_absent"
28
- | "target_repo_hooks_unrecognized"
29
- | "local_edit_watchers_absent";
30
-
31
- export interface GitTransitionHookEvent {
32
- readonly hookName: (typeof TARGET_GIT_TRANSITION_HOOKS)[number];
33
- readonly hookArgs: readonly string[];
34
- readonly worktreeRoot: string;
35
- readonly observedAt: string;
36
- }
37
-
38
- export interface GitHookBootstrapStatus {
39
- readonly posture: GitHookBootstrapPosture;
40
- readonly configuredCoreHooksPath: string | null;
41
- readonly resolvedHooksPath: string;
42
- readonly requiredHooks: readonly string[];
43
- readonly presentHooks: readonly string[];
44
- readonly missingHooks: readonly string[];
45
- readonly supportsCheckoutBoundaries: boolean;
46
- }
47
-
48
- export interface RuntimeWorkspaceOverlayFooting {
49
- readonly observationMode: WorkspaceOverlayObservationMode;
50
- readonly lineagePosture: WorkspaceOverlayLineagePosture;
51
- readonly boundaryAuthority: WorkspaceOverlayBoundaryAuthority;
52
- readonly degraded: true;
53
- readonly degradedReason: WorkspaceOverlayDegradedReason;
54
- readonly checkoutEpoch: number;
55
- readonly lastTransition: RepoTransition | null;
56
- readonly workspaceOverlayId: string | null;
57
- readonly workspaceOverlay: WorkspaceOverlaySummary | null;
58
- readonly hookBootstrap: GitHookBootstrapStatus;
59
- readonly latestHookEvent: GitTransitionHookEvent | null;
60
- }
61
-
62
- async function readGitConfig(
63
- gitClient: GitClient,
64
- cwd: string,
65
- key: string,
66
- ): Promise<string | null> {
67
- const result = await gitClient.run({
68
- args: ["config", "--get", key],
69
- cwd,
70
- });
71
- if (result.status !== 0) {
72
- return null;
73
- }
74
- const value = result.stdout.trim();
75
- return value.length > 0 ? value : null;
76
- }
77
-
78
- async function fileExists(fs: FileSystem, targetPath: string): Promise<boolean> {
79
- try {
80
- await fs.readFile(targetPath, "utf-8");
81
- return true;
82
- } catch {
83
- return false;
84
- }
85
- }
86
-
87
- function equivalentWorktreeRoots(left: string, right: string): boolean {
88
- const leftResolved = path.resolve(left);
89
- const rightResolved = path.resolve(right);
90
- if (leftResolved === rightResolved) {
91
- return true;
92
- }
93
- return (
94
- leftResolved === `/private${rightResolved}`
95
- || rightResolved === `/private${leftResolved}`
96
- );
97
- }
98
-
99
- function parseHookEventLine(
100
- line: string,
101
- worktreeRoot: string,
102
- ): GitTransitionHookEvent | null {
103
- try {
104
- const parsed = JSON.parse(line) as Record<string, unknown>;
105
- const hookName = parsed["hookName"];
106
- const hookArgs = parsed["hookArgs"];
107
- const observedAt = parsed["observedAt"];
108
- const observedWorktreeRoot = parsed["worktreeRoot"];
109
- if (
110
- typeof hookName !== "string"
111
- || !isTargetGitTransitionHookName(hookName)
112
- || !Array.isArray(hookArgs)
113
- || !hookArgs.every((value) => typeof value === "string")
114
- || typeof observedAt !== "string"
115
- || typeof observedWorktreeRoot !== "string"
116
- || !equivalentWorktreeRoots(observedWorktreeRoot, worktreeRoot)
117
- ) {
118
- return null;
119
- }
120
- return {
121
- hookName,
122
- hookArgs,
123
- worktreeRoot: observedWorktreeRoot,
124
- observedAt,
125
- };
126
- } catch {
127
- return null;
128
- }
129
- }
130
-
131
- async function readLatestHookEvent(
132
- fs: FileSystem,
133
- worktreeRoot: string,
134
- ): Promise<GitTransitionHookEvent | null> {
135
- const logPath = path.join(worktreeRoot, ".graft", "runtime", "git-transitions.ndjson");
136
- try {
137
- const content = await fs.readFile(logPath, "utf-8");
138
- const lines = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
139
- for (let index = lines.length - 1; index >= 0; index -= 1) {
140
- const line = lines[index];
141
- if (line === undefined) continue;
142
- const parsed = parseHookEventLine(line, worktreeRoot);
143
- if (parsed !== null) {
144
- return parsed;
145
- }
146
- }
147
- return null;
148
- } catch {
149
- return null;
150
- }
151
- }
152
-
153
- export async function inspectGitHookBootstrap(
154
- fs: FileSystem,
155
- gitClient: GitClient,
156
- worktreeRoot: string,
157
- gitCommonDir: string,
158
- ): Promise<GitHookBootstrapStatus> {
159
- const configuredCoreHooksPath = await readGitConfig(
160
- gitClient,
161
- worktreeRoot,
162
- "core.hooksPath",
163
- );
164
- const resolvedHooksPath = resolveGitHooksPath(
165
- worktreeRoot,
166
- gitCommonDir,
167
- configuredCoreHooksPath,
168
- );
169
-
170
- const presentHooks: string[] = [];
171
- const missingHooks: string[] = [];
172
- let recognizedHooks = 0;
173
- let externalHooks = 0;
174
- for (const hookName of TARGET_GIT_TRANSITION_HOOKS) {
175
- const hookPath = path.join(resolvedHooksPath, hookName);
176
- if (await fileExists(fs, hookPath)) {
177
- presentHooks.push(hookName);
178
- try {
179
- const content = await fs.readFile(hookPath, "utf-8");
180
- if (isRecognizedTargetGitHook(content, hookName)) {
181
- recognizedHooks += 1;
182
- } else {
183
- externalHooks += 1;
184
- }
185
- } catch {
186
- externalHooks += 1;
187
- }
188
- } else {
189
- missingHooks.push(hookName);
190
- }
191
- }
192
-
193
- const allHooksInstalled = recognizedHooks === TARGET_GIT_TRANSITION_HOOKS.length;
194
- return {
195
- posture: allHooksInstalled
196
- ? "installed"
197
- : (presentHooks.length === 0 ? "absent" : "external_unknown"),
198
- configuredCoreHooksPath,
199
- resolvedHooksPath,
200
- requiredHooks: [...TARGET_GIT_TRANSITION_HOOKS],
201
- presentHooks,
202
- missingHooks,
203
- supportsCheckoutBoundaries: allHooksInstalled && externalHooks === 0,
204
- };
205
- }
206
-
207
- export async function buildRuntimeWorkspaceOverlayFooting(
208
- fs: FileSystem,
209
- gitClient: GitClient,
210
- worktreeRoot: string,
211
- gitCommonDir: string,
212
- repoState: {
213
- readonly checkoutEpoch: number;
214
- readonly lastTransition: RepoTransition | null;
215
- readonly workspaceOverlayId: string | null;
216
- readonly workspaceOverlay: WorkspaceOverlaySummary | null;
217
- },
218
- ): Promise<RuntimeWorkspaceOverlayFooting> {
219
- const hookBootstrap = await inspectGitHookBootstrap(
220
- fs,
221
- gitClient,
222
- worktreeRoot,
223
- gitCommonDir,
224
- );
225
- const latestHookEvent = hookBootstrap.supportsCheckoutBoundaries
226
- ? await readLatestHookEvent(fs, worktreeRoot)
227
- : null;
228
- const lineagePosture = repoState.checkoutEpoch > 0 || latestHookEvent !== null || repoState.lastTransition !== null
229
- ? "forked_after_transition"
230
- : "stable";
231
- const boundaryAuthority = latestHookEvent !== null
232
- ? "hook_observed"
233
- : (repoState.lastTransition !== null || repoState.checkoutEpoch > 0
234
- ? "repo_snapshot"
235
- : "none");
236
- return {
237
- observationMode: latestHookEvent === null
238
- ? "inferred_between_tool_calls"
239
- : "hook_observed_checkout_boundaries",
240
- lineagePosture,
241
- boundaryAuthority,
242
- degraded: true,
243
- degradedReason: hookBootstrap.supportsCheckoutBoundaries
244
- ? "local_edit_watchers_absent"
245
- : (hookBootstrap.posture === "absent"
246
- ? "target_repo_hooks_absent"
247
- : "target_repo_hooks_unrecognized"),
248
- checkoutEpoch: repoState.checkoutEpoch,
249
- lastTransition: repoState.lastTransition,
250
- workspaceOverlayId: repoState.workspaceOverlayId,
251
- workspaceOverlay: repoState.workspaceOverlay,
252
- hookBootstrap,
253
- latestHookEvent,
254
- };
255
- }