@intutic/sync-daemon 2.2.0 → 2.3.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 (235) hide show
  1. package/README.md +1 -1
  2. package/dist/agentReporter.d.ts +23 -11
  3. package/dist/agentReporter.d.ts.map +1 -1
  4. package/dist/agentReporter.js +20 -13
  5. package/dist/agentReporter.js.map +1 -1
  6. package/dist/configReader.d.ts +1 -3
  7. package/dist/configReader.d.ts.map +1 -1
  8. package/dist/configReader.js +0 -2
  9. package/dist/configReader.js.map +1 -1
  10. package/dist/configWriter.d.ts +11 -12
  11. package/dist/configWriter.d.ts.map +1 -1
  12. package/dist/configWriter.js +35 -58
  13. package/dist/configWriter.js.map +1 -1
  14. package/dist/disconnect/harnesses.d.ts +0 -2
  15. package/dist/disconnect/harnesses.d.ts.map +1 -1
  16. package/dist/disconnect/harnesses.js +157 -80
  17. package/dist/disconnect/harnesses.js.map +1 -1
  18. package/dist/disconnect/index.d.ts.map +1 -1
  19. package/dist/disconnect/index.js +11 -1
  20. package/dist/disconnect/index.js.map +1 -1
  21. package/dist/disconnect/mcp.d.ts +21 -0
  22. package/dist/disconnect/mcp.d.ts.map +1 -1
  23. package/dist/disconnect/mcp.js +71 -0
  24. package/dist/disconnect/mcp.js.map +1 -1
  25. package/dist/disconnect/recognise.d.ts +2 -2
  26. package/dist/disconnect/recognise.d.ts.map +1 -1
  27. package/dist/disconnect/recognise.js +15 -5
  28. package/dist/disconnect/recognise.js.map +1 -1
  29. package/dist/harness/aiderConfigMerger.d.ts +28 -8
  30. package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
  31. package/dist/harness/aiderConfigMerger.js +59 -11
  32. package/dist/harness/aiderConfigMerger.js.map +1 -1
  33. package/dist/harness/antigravityCliHooks.d.ts +61 -0
  34. package/dist/harness/antigravityCliHooks.d.ts.map +1 -0
  35. package/dist/harness/antigravityCliHooks.js +207 -0
  36. package/dist/harness/antigravityCliHooks.js.map +1 -0
  37. package/dist/harness/antigravityHooks.d.ts +12 -2
  38. package/dist/harness/antigravityHooks.d.ts.map +1 -1
  39. package/dist/harness/antigravityHooks.js +36 -16
  40. package/dist/harness/antigravityHooks.js.map +1 -1
  41. package/dist/harness/antigravityProducts.d.ts +40 -0
  42. package/dist/harness/antigravityProducts.d.ts.map +1 -0
  43. package/dist/harness/antigravityProducts.js +109 -0
  44. package/dist/harness/antigravityProducts.js.map +1 -0
  45. package/dist/harness/claudeAgentsMd.d.ts +29 -0
  46. package/dist/harness/claudeAgentsMd.d.ts.map +1 -0
  47. package/dist/harness/claudeAgentsMd.js +80 -0
  48. package/dist/harness/claudeAgentsMd.js.map +1 -0
  49. package/dist/harness/claudeCodeHooks.d.ts +6 -5
  50. package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
  51. package/dist/harness/claudeCodeHooks.js +98 -24
  52. package/dist/harness/claudeCodeHooks.js.map +1 -1
  53. package/dist/harness/clineHooks.d.ts +0 -1
  54. package/dist/harness/clineHooks.d.ts.map +1 -1
  55. package/dist/harness/clineHooks.js +10 -4
  56. package/dist/harness/clineHooks.js.map +1 -1
  57. package/dist/harness/codexHooks.d.ts +2 -0
  58. package/dist/harness/codexHooks.d.ts.map +1 -1
  59. package/dist/harness/codexHooks.js +12 -4
  60. package/dist/harness/codexHooks.js.map +1 -1
  61. package/dist/harness/cursorHooks.d.ts +5 -2
  62. package/dist/harness/cursorHooks.d.ts.map +1 -1
  63. package/dist/harness/cursorHooks.js +9 -3
  64. package/dist/harness/cursorHooks.js.map +1 -1
  65. package/dist/harness/dshHooks.d.ts +5 -5
  66. package/dist/harness/dshHooks.d.ts.map +1 -1
  67. package/dist/harness/dshHooks.js +5 -5
  68. package/dist/harness/gateArtifacts.d.ts +35 -0
  69. package/dist/harness/gateArtifacts.d.ts.map +1 -0
  70. package/dist/harness/gateArtifacts.js +88 -0
  71. package/dist/harness/gateArtifacts.js.map +1 -0
  72. package/dist/harness/gateBody.d.ts +186 -69
  73. package/dist/harness/gateBody.d.ts.map +1 -1
  74. package/dist/harness/gateBody.js +820 -182
  75. package/dist/harness/gateBody.js.map +1 -1
  76. package/dist/harness/gateSightings.d.ts +47 -0
  77. package/dist/harness/gateSightings.d.ts.map +1 -0
  78. package/dist/harness/gateSightings.js +159 -0
  79. package/dist/harness/gateSightings.js.map +1 -0
  80. package/dist/harness/githubCopilotHooks.d.ts.map +1 -1
  81. package/dist/harness/githubCopilotHooks.js +7 -1
  82. package/dist/harness/githubCopilotHooks.js.map +1 -1
  83. package/dist/harness/gooseHardener.d.ts +0 -1
  84. package/dist/harness/gooseHardener.d.ts.map +1 -1
  85. package/dist/harness/gooseHardener.js +1 -1
  86. package/dist/harness/gooseHardener.js.map +1 -1
  87. package/dist/harness/gooseHooks.d.ts +14 -1
  88. package/dist/harness/gooseHooks.d.ts.map +1 -1
  89. package/dist/harness/gooseHooks.js +55 -29
  90. package/dist/harness/gooseHooks.js.map +1 -1
  91. package/dist/harness/grokHooks.d.ts +2 -2
  92. package/dist/harness/grokHooks.d.ts.map +1 -1
  93. package/dist/harness/grokHooks.js +11 -6
  94. package/dist/harness/grokHooks.js.map +1 -1
  95. package/dist/harness/hermesHooks.d.ts +18 -8
  96. package/dist/harness/hermesHooks.d.ts.map +1 -1
  97. package/dist/harness/hermesHooks.js +48 -17
  98. package/dist/harness/hermesHooks.js.map +1 -1
  99. package/dist/harness/mcpAutoWrite.d.ts +24 -9
  100. package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
  101. package/dist/harness/mcpAutoWrite.js +218 -25
  102. package/dist/harness/mcpAutoWrite.js.map +1 -1
  103. package/dist/harness/museHooks.d.ts +3 -3
  104. package/dist/harness/museHooks.d.ts.map +1 -1
  105. package/dist/harness/museHooks.js +9 -5
  106. package/dist/harness/museHooks.js.map +1 -1
  107. package/dist/harness/n8nHooks.d.ts +0 -1
  108. package/dist/harness/n8nHooks.d.ts.map +1 -1
  109. package/dist/harness/n8nHooks.js +4 -1
  110. package/dist/harness/n8nHooks.js.map +1 -1
  111. package/dist/harness/openCodeHooks.d.ts +2 -2
  112. package/dist/harness/openCodeHooks.d.ts.map +1 -1
  113. package/dist/harness/openCodeHooks.js +7 -3
  114. package/dist/harness/openCodeHooks.js.map +1 -1
  115. package/dist/harness/openWebuiHooks.d.ts +0 -1
  116. package/dist/harness/openWebuiHooks.d.ts.map +1 -1
  117. package/dist/harness/openWebuiHooks.js +5 -1
  118. package/dist/harness/openWebuiHooks.js.map +1 -1
  119. package/dist/harness/openclawHooks.d.ts +70 -18
  120. package/dist/harness/openclawHooks.d.ts.map +1 -1
  121. package/dist/harness/openclawHooks.js +213 -161
  122. package/dist/harness/openclawHooks.js.map +1 -1
  123. package/dist/harness/openhandsHooks.d.ts +24 -10
  124. package/dist/harness/openhandsHooks.d.ts.map +1 -1
  125. package/dist/harness/openhandsHooks.js +49 -48
  126. package/dist/harness/openhandsHooks.js.map +1 -1
  127. package/dist/harness/piHooks.d.ts +52 -17
  128. package/dist/harness/piHooks.d.ts.map +1 -1
  129. package/dist/harness/piHooks.js +177 -166
  130. package/dist/harness/piHooks.js.map +1 -1
  131. package/dist/harness/protectedPaths.d.ts +107 -25
  132. package/dist/harness/protectedPaths.d.ts.map +1 -1
  133. package/dist/harness/protectedPaths.js +342 -48
  134. package/dist/harness/protectedPaths.js.map +1 -1
  135. package/dist/harness/rulesSection.d.ts +62 -0
  136. package/dist/harness/rulesSection.d.ts.map +1 -0
  137. package/dist/harness/rulesSection.js +145 -0
  138. package/dist/harness/rulesSection.js.map +1 -0
  139. package/dist/harness/windsurfHooks.d.ts +0 -1
  140. package/dist/harness/windsurfHooks.d.ts.map +1 -1
  141. package/dist/harness/windsurfHooks.js +4 -1
  142. package/dist/harness/windsurfHooks.js.map +1 -1
  143. package/dist/index.d.ts +12 -8
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +11 -9
  146. package/dist/index.js.map +1 -1
  147. package/dist/inventory.d.ts +53 -0
  148. package/dist/inventory.d.ts.map +1 -0
  149. package/dist/inventory.js +133 -0
  150. package/dist/inventory.js.map +1 -0
  151. package/dist/lib/complianceProbes.d.ts +2 -1
  152. package/dist/lib/complianceProbes.d.ts.map +1 -1
  153. package/dist/lib/complianceProbes.js +1 -1
  154. package/dist/lib/complianceProbes.js.map +1 -1
  155. package/dist/lib/decisionsDigest.d.ts +34 -16
  156. package/dist/lib/decisionsDigest.d.ts.map +1 -1
  157. package/dist/lib/decisionsDigest.js +105 -46
  158. package/dist/lib/decisionsDigest.js.map +1 -1
  159. package/dist/lib/egressPolicy.d.ts +2 -2
  160. package/dist/lib/egressPolicy.js +2 -2
  161. package/dist/lib/mcpRegistryPy.d.ts +11 -0
  162. package/dist/lib/mcpRegistryPy.d.ts.map +1 -0
  163. package/dist/lib/mcpRegistryPy.js +173 -0
  164. package/dist/lib/mcpRegistryPy.js.map +1 -0
  165. package/dist/lib/phrasesPy.d.ts +11 -0
  166. package/dist/lib/phrasesPy.d.ts.map +1 -0
  167. package/dist/lib/phrasesPy.js +351 -0
  168. package/dist/lib/phrasesPy.js.map +1 -0
  169. package/dist/lib/policySnapshot.d.ts +46 -10
  170. package/dist/lib/policySnapshot.d.ts.map +1 -1
  171. package/dist/lib/policySnapshot.js +136 -47
  172. package/dist/lib/policySnapshot.js.map +1 -1
  173. package/dist/lib/processPoller.d.ts.map +1 -1
  174. package/dist/lib/processPoller.js +9 -2
  175. package/dist/lib/processPoller.js.map +1 -1
  176. package/dist/lib/regexLinearity.d.ts +43 -0
  177. package/dist/lib/regexLinearity.d.ts.map +1 -0
  178. package/dist/lib/regexLinearity.js +275 -0
  179. package/dist/lib/regexLinearity.js.map +1 -0
  180. package/dist/lib/runtimeEnv.d.ts +1 -3
  181. package/dist/lib/runtimeEnv.d.ts.map +1 -1
  182. package/dist/lib/runtimeEnv.js +0 -2
  183. package/dist/lib/runtimeEnv.js.map +1 -1
  184. package/dist/lib/sequencePy.d.ts +10 -0
  185. package/dist/lib/sequencePy.d.ts.map +1 -0
  186. package/dist/lib/sequencePy.js +102 -0
  187. package/dist/lib/sequencePy.js.map +1 -0
  188. package/dist/sessionReporter.d.ts +17 -11
  189. package/dist/sessionReporter.d.ts.map +1 -1
  190. package/dist/sessionReporter.js +36 -23
  191. package/dist/sessionReporter.js.map +1 -1
  192. package/dist/skillWriter.d.ts +23 -5
  193. package/dist/skillWriter.d.ts.map +1 -1
  194. package/dist/skillWriter.js +159 -16
  195. package/dist/skillWriter.js.map +1 -1
  196. package/dist/syncCycle.d.ts.map +1 -1
  197. package/dist/syncCycle.js +21 -9
  198. package/dist/syncCycle.js.map +1 -1
  199. package/dist/trajectoryMonitor.d.ts +1 -3
  200. package/dist/trajectoryMonitor.d.ts.map +1 -1
  201. package/dist/trajectoryMonitor.js +1 -4
  202. package/dist/trajectoryMonitor.js.map +1 -1
  203. package/dist/watcher/driftWatcher.d.ts +0 -1
  204. package/dist/watcher/driftWatcher.d.ts.map +1 -1
  205. package/dist/watcher/driftWatcher.js +1 -2
  206. package/dist/watcher/driftWatcher.js.map +1 -1
  207. package/dist/watcher/settingsGuard.d.ts +33 -7
  208. package/dist/watcher/settingsGuard.d.ts.map +1 -1
  209. package/dist/watcher/settingsGuard.js +373 -61
  210. package/dist/watcher/settingsGuard.js.map +1 -1
  211. package/dist/watcher/vscodeHookSettings.d.ts +30 -0
  212. package/dist/watcher/vscodeHookSettings.d.ts.map +1 -0
  213. package/dist/watcher/vscodeHookSettings.js +170 -0
  214. package/dist/watcher/vscodeHookSettings.js.map +1 -0
  215. package/dist/wsClient.d.ts +0 -1
  216. package/dist/wsClient.d.ts.map +1 -1
  217. package/dist/wsClient.js +0 -1
  218. package/dist/wsClient.js.map +1 -1
  219. package/package.json +5 -5
  220. package/dist/harness/claudeDesktopHooks.d.ts +0 -33
  221. package/dist/harness/claudeDesktopHooks.d.ts.map +0 -1
  222. package/dist/harness/claudeDesktopHooks.js +0 -256
  223. package/dist/harness/claudeDesktopHooks.js.map +0 -1
  224. package/dist/harness/continueHooks.d.ts +0 -63
  225. package/dist/harness/continueHooks.d.ts.map +0 -1
  226. package/dist/harness/continueHooks.js +0 -250
  227. package/dist/harness/continueHooks.js.map +0 -1
  228. package/dist/harness/gateKind.d.ts +0 -164
  229. package/dist/harness/gateKind.d.ts.map +0 -1
  230. package/dist/harness/gateKind.js +0 -144
  231. package/dist/harness/gateKind.js.map +0 -1
  232. package/dist/harness/rooCodeHooks.d.ts +0 -31
  233. package/dist/harness/rooCodeHooks.d.ts.map +0 -1
  234. package/dist/harness/rooCodeHooks.js +0 -281
  235. package/dist/harness/rooCodeHooks.js.map +0 -1
@@ -0,0 +1,275 @@
1
+ /**
2
+ * A static check for the regex constructs that make a backtracking engine
3
+ * super-linear, run on every pattern the gates evaluate against a command or
4
+ * a tool's arguments.
5
+ *
6
+ * The JavaScript gates run rule sources as `RegExp`s and the Python ones as
7
+ * `re` patterns; both backtrack. A rule like ` git .*(…)` was correct and took
8
+ * 9.5 s on 200 KB of crafted command, long enough for a harness that treats a
9
+ * hook timeout as "allow" to run the call. The constructs below are the ones
10
+ * that cost time growing faster than the text:
11
+ *
12
+ * - **nested quantifier**: a repeated group that contains a repeated element,
13
+ * `(a+)+` — the classic catastrophic form;
14
+ * - **unbounded wildcard before more pattern**: `.*`, `[\s\S]*`, a negated
15
+ * class or `\S`/`\W`/`\D` repeated without bound and followed by anything —
16
+ * every start rescans to the end of the text;
17
+ * - **unbounded lazy quantifier**: `*?`, `+?`, `{n,}?` — the same rescan, one
18
+ * character at a time;
19
+ * - **adjacent overlapping runs**: two unbounded repetitions whose character
20
+ * sets overlap, with nothing between them that only one can match —
21
+ * `[a-z]*R[a-z]*` tries every split of `RRRR…`.
22
+ *
23
+ * A pattern that needs one of these anyway goes on {@link BACKTRACKING_ALLOWLIST}
24
+ * by rule id, with the reason a reviewer accepted it; the test that walks
25
+ * every gate rule fails on anything else. A rule marked `sequence` is checked
26
+ * step by step, since that is how the JS and Python gates run it.
27
+ *
28
+ * It parses the JS ∩ Python `re` dialect the rules use. Character sets are
29
+ * modelled over ASCII; a class with anything beyond ASCII counts as
30
+ * overlapping everything, which errs toward a finding.
31
+ */
32
+ const range = (from, to) => {
33
+ const s = new Array(128).fill(false);
34
+ for (let c = from; c <= to; c++)
35
+ s[c] = true;
36
+ return s;
37
+ };
38
+ const union = (a, b) => a.map((v, i) => v || b[i]);
39
+ const invert = (a) => a.map((v) => !v);
40
+ const SPACE = (() => {
41
+ const s = new Array(128).fill(false);
42
+ for (const c of [9, 10, 11, 12, 13, 32])
43
+ s[c] = true;
44
+ return s;
45
+ })();
46
+ const DIGIT = range(48, 57);
47
+ const WORD = union(union(union(range(48, 57), range(65, 90)), range(97, 122)), (() => {
48
+ const s = new Array(128).fill(false);
49
+ s[95] = true;
50
+ return s;
51
+ })());
52
+ const ALL = range(0, 127);
53
+ const single = (ch) => {
54
+ const s = new Array(128).fill(false);
55
+ const c = ch.charCodeAt(0);
56
+ if (c < 128)
57
+ s[c] = true;
58
+ else
59
+ s.fill(true); // beyond ASCII: assume it overlaps everything
60
+ return s;
61
+ };
62
+ /** The set an escape names, and whether it is one of the wide negations. */
63
+ function escapeSet(ch) {
64
+ switch (ch) {
65
+ case 's': return { set: SPACE, wide: false };
66
+ case 'S': return { set: invert(SPACE), wide: true };
67
+ case 'd': return { set: DIGIT, wide: false };
68
+ case 'D': return { set: invert(DIGIT), wide: true };
69
+ case 'w': return { set: WORD, wide: false };
70
+ case 'W': return { set: invert(WORD), wide: true };
71
+ case 'n': return { set: single('\n'), wide: false };
72
+ case 't': return { set: single('\t'), wide: false };
73
+ case 'r': return { set: single('\r'), wide: false };
74
+ default: return { set: single(ch), wide: false };
75
+ }
76
+ }
77
+ /** Parses `source` into alternatives of items. Throws on syntax it does not know. */
78
+ function parse(source) {
79
+ let i = 0;
80
+ const alternatives = () => {
81
+ const alts = [[]];
82
+ while (i < source.length) {
83
+ const c = source[i];
84
+ if (c === ')')
85
+ break;
86
+ if (c === '|') {
87
+ i++;
88
+ alts.push([]);
89
+ continue;
90
+ }
91
+ const item = atom();
92
+ if (item) {
93
+ quantifier(item);
94
+ alts[alts.length - 1].push(item);
95
+ }
96
+ }
97
+ return alts;
98
+ };
99
+ const atom = () => {
100
+ const c = source[i];
101
+ const base = { group: null, min: 1, max: 1, lazy: false, wide: false };
102
+ if (c === '(') {
103
+ i++;
104
+ if (source[i] === '?') {
105
+ // (?: (?= (?! (?<= (?<! — the group's kind does not change the check.
106
+ i++;
107
+ if (source[i] === '<')
108
+ i++;
109
+ i++;
110
+ }
111
+ const group = alternatives();
112
+ if (source[i] !== ')')
113
+ throw new Error(`unclosed group in ${source}`);
114
+ i++;
115
+ return { ...base, set: null, group };
116
+ }
117
+ if (c === '[')
118
+ return { ...base, ...charClass(), group: null };
119
+ if (c === '\\') {
120
+ const e = source[i + 1] ?? '';
121
+ i += 2;
122
+ if (e === 'b' || e === 'B')
123
+ return null; // an assertion, not a character
124
+ if (/[1-9]/.test(e))
125
+ return { ...base, set: ALL }; // a backreference: counts as one wide character
126
+ const { set, wide } = escapeSet(e);
127
+ return { ...base, set, wide };
128
+ }
129
+ i++;
130
+ if (c === '^' || c === '$')
131
+ return null;
132
+ if (c === '.')
133
+ return { ...base, set: invert(single('\n')), wide: true };
134
+ return { ...base, set: single(c) };
135
+ };
136
+ const charClass = () => {
137
+ i++; // [
138
+ let negated = false;
139
+ if (source[i] === '^') {
140
+ negated = true;
141
+ i++;
142
+ }
143
+ let set = new Array(128).fill(false);
144
+ let first = true;
145
+ while (i < source.length && (source[i] !== ']' || first)) {
146
+ first = false;
147
+ let lo;
148
+ let loChar = '';
149
+ if (source[i] === '\\') {
150
+ const e = source[i + 1] ?? '';
151
+ i += 2;
152
+ lo = escapeSet(e).set;
153
+ if (!/[sSdDwW]/.test(e))
154
+ loChar = e === 'n' ? '\n' : e === 't' ? '\t' : e === 'r' ? '\r' : e;
155
+ }
156
+ else {
157
+ loChar = source[i];
158
+ lo = single(loChar);
159
+ i++;
160
+ }
161
+ if (loChar && source[i] === '-' && source[i + 1] !== ']' && i + 1 < source.length) {
162
+ i++;
163
+ let hiChar = source[i];
164
+ if (hiChar === '\\') {
165
+ hiChar = source[i + 1] ?? '';
166
+ i++;
167
+ }
168
+ i++;
169
+ set = union(set, range(loChar.charCodeAt(0), Math.min(127, hiChar.charCodeAt(0))));
170
+ if (hiChar.charCodeAt(0) > 127)
171
+ set = ALL.slice();
172
+ continue;
173
+ }
174
+ set = union(set, lo);
175
+ }
176
+ i++; // ]
177
+ const full = set.every(Boolean);
178
+ return { set: negated ? invert(set) : set, wide: negated || full };
179
+ };
180
+ const quantifier = (item) => {
181
+ const c = source[i];
182
+ if (c === '*')
183
+ [item.min, item.max] = [0, Infinity];
184
+ else if (c === '+')
185
+ [item.min, item.max] = [1, Infinity];
186
+ else if (c === '?')
187
+ [item.min, item.max] = [0, 1];
188
+ else if (c === '{') {
189
+ const m = /^\{(\d+)(,(\d*))?\}/.exec(source.slice(i));
190
+ if (!m)
191
+ return;
192
+ item.min = Number(m[1]);
193
+ item.max = m[2] === undefined ? item.min : m[3] === '' ? Infinity : Number(m[3]);
194
+ i += m[0].length - 1;
195
+ }
196
+ else
197
+ return;
198
+ i++;
199
+ if (source[i] === '?') {
200
+ item.lazy = true;
201
+ i++;
202
+ }
203
+ };
204
+ const alts = alternatives();
205
+ if (i !== source.length)
206
+ throw new Error(`unbalanced ) in ${source}`);
207
+ return alts;
208
+ }
209
+ const unbounded = (it) => it.max === Infinity;
210
+ const hasUnbounded = (alts) => alts.some((seq) => seq.some((it) => unbounded(it) || (it.group !== null && hasUnbounded(it.group))));
211
+ const overlaps = (a, b) => a.some((v, i) => v && b[i]);
212
+ /** Every construct in `source` that can make a backtracking engine super-linear. */
213
+ export function backtrackingFindings(source) {
214
+ const findings = [];
215
+ const subset = (a, b) => a.every((v, c) => !v || b[c]);
216
+ const walk = (alts, topLevel) => {
217
+ for (const seq of alts) {
218
+ seq.forEach((it, k) => {
219
+ if (it.group && unbounded(it) && hasUnbounded(it.group))
220
+ findings.push('nested quantifier');
221
+ if (it.lazy && unbounded(it))
222
+ findings.push('unbounded lazy quantifier');
223
+ // A wide run is harmless only as the very last thing a match needs.
224
+ if (it.wide && unbounded(it) && !(topLevel && k === seq.length - 1)) {
225
+ findings.push('unbounded wildcard before more pattern');
226
+ }
227
+ if (it.set && unbounded(it)) {
228
+ // Past the fixed characters this run could also have taken, is the
229
+ // next repetition one whose set overlaps it?
230
+ for (let j = k + 1; j < seq.length; j++) {
231
+ const next = seq[j];
232
+ if (!next.set)
233
+ break;
234
+ if (unbounded(next)) {
235
+ if (overlaps(it.set, next.set))
236
+ findings.push('adjacent overlapping runs');
237
+ break;
238
+ }
239
+ if (!subset(next.set, it.set))
240
+ break;
241
+ }
242
+ }
243
+ if (it.group)
244
+ walk(it.group, false);
245
+ });
246
+ }
247
+ };
248
+ walk(parse(source), true);
249
+ return [...new Set(findings)];
250
+ }
251
+ /**
252
+ * Rules allowed a construct {@link backtrackingFindings} reports, by rule id,
253
+ * with the reason a reviewer accepted it. Each entry must still trip the
254
+ * check (a stale entry fails the test), and each such rule has adversarial
255
+ * vectors that every reader must decide in linear time.
256
+ */
257
+ export const BACKTRACKING_ALLOWLIST = {
258
+ 'destructive.rm_rf_root': '`( +-[a-zA-Z-]+)+`: every repetition must begin with a space and the inner run cannot ' +
259
+ 'contain one, so there is one way to split the flags; each ` rm` start does work linear in ' +
260
+ 'its own flag list and stops at the next ` rm`.',
261
+ 'skill_content.*': 'The argPattern wrapper `"key":"(?:[^"\\\\]|\\\\.)*?(?:P)` scans one JSON string lazily and ' +
262
+ 'tries P at each position. It starts only at a key, which can appear unescaped in ' +
263
+ 'the serialized input only as a key; the two alternatives begin with different ' +
264
+ 'characters, so the scan is single-pass; and each P is bounded per position ' +
265
+ '(`{0,40}`-style windows, a lookahead-committed URL in markdown-exfil-link, a 600-character ' +
266
+ 'close check in html-comment-hidden-instruction).',
267
+ };
268
+ /** The allowlist entry that covers a rule id, if any (`prefix.*` entries match a family). */
269
+ export function backtrackingAllowance(id) {
270
+ if (BACKTRACKING_ALLOWLIST[id])
271
+ return BACKTRACKING_ALLOWLIST[id];
272
+ const family = Object.keys(BACKTRACKING_ALLOWLIST).find((k) => k.endsWith('.*') && id.startsWith(k.slice(0, -1)));
273
+ return family ? BACKTRACKING_ALLOWLIST[family] : undefined;
274
+ }
275
+ //# sourceMappingURL=regexLinearity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regexLinearity.js","sourceRoot":"","sources":["../../src/lib/regexLinearity.ts"],"names":[],"mappings":"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"}
@@ -16,8 +16,6 @@
16
16
  * INTUTIC_WORKSPACE_ID=ws_...
17
17
  * INTUTIC_VALKEY_URL=redis://127.0.0.1:6379 (only when a local Valkey is running)
18
18
  *
19
- * LLD #14 — Dual-path hook telemetry (WS-A1)
20
- *
21
19
  * @module
22
20
  */
23
21
  /** Default path to the runtime env file sourced by all generated hook scripts. */
@@ -39,7 +37,7 @@ export interface RuntimeEnvOptions {
39
37
  bypassEnforcementTier?: string;
40
38
  /**
41
39
  * The local Valkey the MCP governance proxies share their anomaly session
42
- * window through (Wave 5.3, TD-437). Written as `INTUTIC_VALKEY_URL` only
40
+ * window through (Wave 5.3). Written as `INTUTIC_VALKEY_URL` only
43
41
  * when the caller has one running; omitted otherwise, so a proxy never
44
42
  * probes a Valkey nobody started.
45
43
  */
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeEnv.d.ts","sourceRoot":"","sources":["../../src/lib/runtimeEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AASH,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,QAA4D,CAAA;AAEjG,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,eAAe,EAAE,MAAM,CAAA;IACvB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuC5E"}
1
+ {"version":3,"file":"runtimeEnv.d.ts","sourceRoot":"","sources":["../../src/lib/runtimeEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AASH,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,QAA4D,CAAA;AAEjG,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,eAAe,EAAE,MAAM,CAAA;IACvB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuC5E"}
@@ -16,8 +16,6 @@
16
16
  * INTUTIC_WORKSPACE_ID=ws_...
17
17
  * INTUTIC_VALKEY_URL=redis://127.0.0.1:6379 (only when a local Valkey is running)
18
18
  *
19
- * LLD #14 — Dual-path hook telemetry (WS-A1)
20
- *
21
19
  * @module
22
20
  */
23
21
  import * as fs from 'node:fs/promises';
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeEnv.js","sourceRoot":"","sources":["../../src/lib/runtimeEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,GAAG,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAA;AAE5C,kFAAkF;AAClF,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;AA0BjG;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAuB;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,wBAAwB,CAAA;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAEpC,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE3C,yEAAyE;IACzE,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAChE,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IACtD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAC5D,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAE/D,MAAM,OAAO,GAAG;QACd,mEAAmE;QACnE,4FAA4F;QAC5F,4GAA4G;QAC5G,sHAAsH;QACtH,gBAAgB,IAAI,EAAE;QACtB,mBAAmB,GAAG,EAAE;QACxB,wBAAwB,IAAI,EAAE;QAC9B,yBAAyB,CAAC,IAAI,CAAC,oBAAoB,IAAI,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE;QAChG,0BAA0B,IAAI,CAAC,YAAY,IAAI,aAAa,EAAE;QAC9D,uBAAuB,IAAI,CAAC,qBAAqB,IAAI,SAAS,EAAE;QAChE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,EAAE;QACF,iFAAiF;QACjF,wCAAwC;QACxC,yCAAyC;QACzC,4CAA4C;QAC5C,sCAAsC;QACtC,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEZ,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;IAChC,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3D,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC9B,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAEjC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,4BAA4B,CAAC,CAAA;AAC3F,CAAC"}
1
+ {"version":3,"file":"runtimeEnv.js","sourceRoot":"","sources":["../../src/lib/runtimeEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,GAAG,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAA;AAE5C,kFAAkF;AAClF,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;AA0BjG;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAuB;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,wBAAwB,CAAA;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAEpC,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE3C,yEAAyE;IACzE,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAChE,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IACtD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAC5D,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAE/D,MAAM,OAAO,GAAG;QACd,mEAAmE;QACnE,4FAA4F;QAC5F,4GAA4G;QAC5G,sHAAsH;QACtH,gBAAgB,IAAI,EAAE;QACtB,mBAAmB,GAAG,EAAE;QACxB,wBAAwB,IAAI,EAAE;QAC9B,yBAAyB,CAAC,IAAI,CAAC,oBAAoB,IAAI,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE;QAChG,0BAA0B,IAAI,CAAC,YAAY,IAAI,aAAa,EAAE;QAC9D,uBAAuB,IAAI,CAAC,qBAAqB,IAAI,SAAS,EAAE;QAChE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,EAAE;QACF,iFAAiF;QACjF,wCAAwC;QACxC,yCAAyC;QACzC,4CAA4C;QAC5C,sCAAsC;QACtC,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEZ,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;IAChC,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3D,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC9B,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAEjC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,4BAA4B,CAAC,CAAA;AAC3F,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The sequence-rule matcher in Python, for the Open WebUI filter.
3
+ *
4
+ * Byte-identical to `packages/intutic-clawde/intutic_clawde/gate/sequence.py`,
5
+ * which a test compares; edit that file and copy it here. It is the
6
+ * transliteration of `@intutic/shared-types`'s sequence.ts, and both run the
7
+ * shared vectors.
8
+ */
9
+ export declare const SEQUENCE_PY_SOURCE: string;
10
+ //# sourceMappingURL=sequencePy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequencePy.d.ts","sourceRoot":"","sources":["../../src/lib/sequencePy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,QA4F9B,CAAA"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The sequence-rule matcher in Python, for the Open WebUI filter.
3
+ *
4
+ * Byte-identical to `packages/intutic-clawde/intutic_clawde/gate/sequence.py`,
5
+ * which a test compares; edit that file and copy it here. It is the
6
+ * transliteration of `@intutic/shared-types`'s sequence.ts, and both run the
7
+ * shared vectors.
8
+ */
9
+ export const SEQUENCE_PY_SOURCE = String.raw `"""Sequence rules: a gate rule "A.*B" run as "find A, then find B after it".
10
+
11
+ A transliteration of packages/shared-types/src/sequence.ts; read that module's
12
+ docstring for when this means the same as the regex. A rule whose .rules
13
+ flags column contains "s" is cut into alternatives of steps at its top-level
14
+ "|" and ".*", and each step is searched for once, from where the previous one
15
+ ended: linear in the text, where re.search on the whole regex backtracks from
16
+ every start of the first step to the end of the text.
17
+
18
+ Every reader runs packages/shared-types/fixtures/gate-rule-vectors.json, and
19
+ the Open WebUI filter emits this file verbatim
20
+ (services/sync-daemon/src/lib/sequencePy.ts holds a byte-identical copy,
21
+ which a test compares). It must stay self-contained: the standard library
22
+ only, no module state, and no backtick or dollar-brace, which the TypeScript
23
+ copy cannot hold.
24
+ """
25
+
26
+ import re
27
+
28
+
29
+ def sequence_alternatives(source):
30
+ """Split a sequence rule's source into alternatives of steps."""
31
+ alternatives = []
32
+ steps = []
33
+ current = []
34
+ depth = 0
35
+ in_class = False
36
+ i = 0
37
+ n = len(source)
38
+ while i < n:
39
+ c = source[i]
40
+ if c == "\\":
41
+ current.append(source[i:i + 2])
42
+ i += 2
43
+ continue
44
+ if in_class:
45
+ current.append(c)
46
+ i += 1
47
+ if c == "]":
48
+ in_class = False
49
+ continue
50
+ if c == "[":
51
+ in_class = True
52
+ current.append(c)
53
+ i += 1
54
+ if source[i:i + 1] == "^":
55
+ current.append("^")
56
+ i += 1
57
+ if source[i:i + 1] == "]":
58
+ current.append("]")
59
+ i += 1
60
+ continue
61
+ if c == "(":
62
+ depth += 1
63
+ elif c == ")":
64
+ depth -= 1
65
+ if depth == 0 and c == "|":
66
+ steps.append("".join(current))
67
+ alternatives.append([s for s in steps if s])
68
+ steps = []
69
+ current = []
70
+ i += 1
71
+ continue
72
+ if depth == 0 and c == "." and source[i + 1:i + 2] == "*":
73
+ steps.append("".join(current))
74
+ current = []
75
+ i += 2
76
+ continue
77
+ current.append(c)
78
+ i += 1
79
+ steps.append("".join(current))
80
+ alternatives.append([s for s in steps if s])
81
+ return alternatives
82
+
83
+
84
+ def compile_sequence(source, flags=0):
85
+ """Compile a sequence rule's steps for sequence_match."""
86
+ return [[re.compile(s, flags) for s in steps] for steps in sequence_alternatives(source)]
87
+
88
+
89
+ def sequence_match(alternatives, text):
90
+ """Whether some alternative's steps occur in text in order."""
91
+ for steps in alternatives:
92
+ pos = 0
93
+ for step in steps:
94
+ m = step.search(text, pos)
95
+ if m is None:
96
+ break
97
+ pos = m.end()
98
+ else:
99
+ return True
100
+ return False
101
+ `;
102
+ //# sourceMappingURL=sequencePy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequencePy.js","sourceRoot":"","sources":["../../src/lib/sequencePy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4F3C,CAAA"}
@@ -6,10 +6,11 @@
6
6
  * capture and the Jira/Linear/GitHub task-context cascade) had no production
7
7
  * caller: sessions only ever existed as synthetic `ssp_` rows minted
8
8
  * server-side. The daemon knows everything the route wants — workspace root,
9
- * harness, git branch and commit — so it reports them once per harness per
10
- * daemon run when the harness is first detected.
9
+ * harness, git repository, branch and commit — so it reports them when the
10
+ * harness is first detected, and again whenever the repository, branch or
11
+ * HEAD commit changes.
11
12
  *
12
- * WHICH row it reports them onto (TD-231, Wave 5.6): when a local proxy is
13
+ * WHICH row it reports them onto (Wave 5.6): when a local proxy is
13
14
  * reachable, the caller passes that process's `proxyInstanceId` and the
14
15
  * control plane puts the context on the proxy's OWN row — the
15
16
  * `ssp_<ws>_<harness>_<instance>` session it derives for that process's
@@ -27,15 +28,19 @@
27
28
  *
28
29
  * Dedupe lives here (module state keyed workspace+harness+instance), so
29
30
  * callers (`reportHarnessAgents`, every `intutic connect` cycle) can call
30
- * `startHarnessSession` every iteration and only the first
31
- * one POSTs. Git context is captured once per key, not on a branch switch.
32
- * Everything is best-effort: a dead control plane costs a warning, never the
33
- * sync loop.
31
+ * `startHarnessSession` every iteration. Git is read on every call and the
32
+ * session is POSTed again only when it changed: the control plane copies the
33
+ * session's git context onto each trace as it records it, which is what makes
34
+ * cost per branch and per commit possible, so a stale commit would file a
35
+ * call under the wrong one. A re-POST updates the same row. Everything is
36
+ * best-effort: a dead control plane costs a warning, never the sync loop.
34
37
  *
35
38
  * @module
36
39
  */
37
- import type { HarnessType } from '@intutic/shared-types';
40
+ import { type HarnessType } from '@intutic/shared-types';
38
41
  export interface GitInfo {
42
+ /** The `origin` remote, normalised: host/path, never credentials. */
43
+ repoUrl?: string;
39
44
  branchName?: string;
40
45
  commitHash?: string;
41
46
  commitMessage?: string;
@@ -43,9 +48,10 @@ export interface GitInfo {
43
48
  /** Best-effort git context from the workspace root. */
44
49
  export declare function readGitInfo(workspaceRoot: string): Promise<GitInfo>;
45
50
  /**
46
- * Report a harness session once per run — onto the local proxy's own row when
47
- * `proxyInstanceId` is given (see the module doc), else as a new `ses_` row.
48
- * Returns the sessionId when one is known (new or previously created).
51
+ * Report a harness session — onto the local proxy's own row when
52
+ * `proxyInstanceId` is given (see the module doc), else as a `ses_` row — and
53
+ * report it again when its git context changes. Returns the sessionId when one
54
+ * is known (new or previously created).
49
55
  */
50
56
  export declare function startHarnessSession(opts: {
51
57
  controlPlaneUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"sessionReporter.d.ts","sourceRoot":"","sources":["../src/sessionReporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAiBxD,MAAM,WAAW,OAAO;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,uDAAuD;AACvD,wBAAsB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgBzE;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuCzB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB/F"}
1
+ {"version":3,"file":"sessionReporter.d.ts","sourceRoot":"","sources":["../src/sessionReporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAKH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAqB5E,MAAM,WAAW,OAAO;IACtB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,uDAAuD;AACvD,wBAAsB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBzE;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyCzB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB/F"}
@@ -6,10 +6,11 @@
6
6
  * capture and the Jira/Linear/GitHub task-context cascade) had no production
7
7
  * caller: sessions only ever existed as synthetic `ssp_` rows minted
8
8
  * server-side. The daemon knows everything the route wants — workspace root,
9
- * harness, git branch and commit — so it reports them once per harness per
10
- * daemon run when the harness is first detected.
9
+ * harness, git repository, branch and commit — so it reports them when the
10
+ * harness is first detected, and again whenever the repository, branch or
11
+ * HEAD commit changes.
11
12
  *
12
- * WHICH row it reports them onto (TD-231, Wave 5.6): when a local proxy is
13
+ * WHICH row it reports them onto (Wave 5.6): when a local proxy is
13
14
  * reachable, the caller passes that process's `proxyInstanceId` and the
14
15
  * control plane puts the context on the proxy's OWN row — the
15
16
  * `ssp_<ws>_<harness>_<instance>` session it derives for that process's
@@ -27,16 +28,19 @@
27
28
  *
28
29
  * Dedupe lives here (module state keyed workspace+harness+instance), so
29
30
  * callers (`reportHarnessAgents`, every `intutic connect` cycle) can call
30
- * `startHarnessSession` every iteration and only the first
31
- * one POSTs. Git context is captured once per key, not on a branch switch.
32
- * Everything is best-effort: a dead control plane costs a warning, never the
33
- * sync loop.
31
+ * `startHarnessSession` every iteration. Git is read on every call and the
32
+ * session is POSTed again only when it changed: the control plane copies the
33
+ * session's git context onto each trace as it records it, which is what makes
34
+ * cost per branch and per commit possible, so a stale commit would file a
35
+ * call under the wrong one. A re-POST updates the same row. Everything is
36
+ * best-effort: a dead control plane costs a warning, never the sync loop.
34
37
  *
35
38
  * @module
36
39
  */
37
40
  import { execFile } from 'node:child_process';
38
41
  import { promisify } from 'node:util';
39
42
  import { newIso } from '@intutic/id';
43
+ import { normalizeGitRemote } from '@intutic/shared-types';
40
44
  const execFileP = promisify(execFile);
41
45
  /**
42
46
  * sessionIds this daemon run opened AND owns the lifecycle of — the `ses_`
@@ -48,8 +52,12 @@ const openSessions = new Map();
48
52
  * `${workspaceId}:${harness}:${proxyInstanceId}`. Dedupe only — never ended.
49
53
  */
50
54
  const registeredProxyRows = new Map();
51
- /** Keys with a POST in flight or already attempted (success or not) — one try per run. */
52
- const attempted = new Set();
55
+ /**
56
+ * The git context last POSTed (or attempted, success or not) per key. One try
57
+ * per key and git state per run: a failing control plane is not retried every
58
+ * cycle, but a branch switch or a new commit is reported.
59
+ */
60
+ const attempted = new Map();
53
61
  /** Best-effort git context from the workspace root. */
54
62
  export async function readGitInfo(workspaceRoot) {
55
63
  const git = async (...args) => {
@@ -62,28 +70,31 @@ export async function readGitInfo(workspaceRoot) {
62
70
  return undefined;
63
71
  }
64
72
  };
65
- const [branchName, commitHash, commitMessage] = await Promise.all([
73
+ const [remote, branchName, commitHash, commitMessage] = await Promise.all([
74
+ git('remote', 'get-url', 'origin'),
66
75
  git('rev-parse', '--abbrev-ref', 'HEAD'),
67
76
  git('rev-parse', 'HEAD'),
68
77
  git('log', '-1', '--format=%s'),
69
78
  ]);
70
- return { branchName, commitHash, commitMessage };
79
+ // Normalised here, before it is held anywhere: a remote can embed a token.
80
+ const repoUrl = remote ? (normalizeGitRemote(remote) ?? undefined) : undefined;
81
+ return { repoUrl, branchName, commitHash, commitMessage };
71
82
  }
72
83
  /**
73
- * Report a harness session once per run — onto the local proxy's own row when
74
- * `proxyInstanceId` is given (see the module doc), else as a new `ses_` row.
75
- * Returns the sessionId when one is known (new or previously created).
84
+ * Report a harness session — onto the local proxy's own row when
85
+ * `proxyInstanceId` is given (see the module doc), else as a `ses_` row — and
86
+ * report it again when its git context changes. Returns the sessionId when one
87
+ * is known (new or previously created).
76
88
  */
77
89
  export async function startHarnessSession(opts) {
78
90
  const key = `${opts.workspaceId}:${opts.harnessType}:${opts.proxyInstanceId ?? ''}`;
79
91
  const existing = opts.proxyInstanceId ? registeredProxyRows.get(key) : openSessions.get(key);
80
- if (existing)
81
- return existing;
82
- if (attempted.has(key))
83
- return null;
84
- attempted.add(key);
92
+ const git = await readGitInfo(opts.workspaceRoot);
93
+ const gitState = JSON.stringify([git.repoUrl, git.branchName, git.commitHash]);
94
+ if (attempted.get(key) === gitState)
95
+ return existing ?? null;
96
+ attempted.set(key, gitState);
85
97
  try {
86
- const git = await readGitInfo(opts.workspaceRoot);
87
98
  const res = await fetch(`${opts.controlPlaneUrl}/api/v1/sessions`, {
88
99
  method: 'POST',
89
100
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${opts.apiKey}` },
@@ -92,6 +103,8 @@ export async function startHarnessSession(opts) {
92
103
  harnessType: opts.harnessType,
93
104
  ...(opts.agentRole ? { agentRole: opts.agentRole } : {}),
94
105
  ...(opts.proxyInstanceId ? { proxyInstanceId: opts.proxyInstanceId } : {}),
106
+ // A `ses_` row this run already opened is updated, not opened again.
107
+ ...(existing && !opts.proxyInstanceId ? { sessionId: existing } : {}),
95
108
  ...git,
96
109
  reportedAt: newIso(),
97
110
  }),
@@ -99,7 +112,7 @@ export async function startHarnessSession(opts) {
99
112
  });
100
113
  if (!res.ok) {
101
114
  console.warn(`[sync-daemon] startHarnessSession failed for ${opts.harnessType}: ${res.status}`);
102
- return null;
115
+ return existing ?? null;
103
116
  }
104
117
  const body = (await res.json());
105
118
  if (body.sessionId) {
@@ -107,11 +120,11 @@ export async function startHarnessSession(opts) {
107
120
  (opts.proxyInstanceId ? registeredProxyRows : openSessions).set(key, body.sessionId);
108
121
  return body.sessionId;
109
122
  }
110
- return null;
123
+ return existing ?? null;
111
124
  }
112
125
  catch (err) {
113
126
  console.warn(`[sync-daemon] startHarnessSession error for ${opts.harnessType}:`, err instanceof Error ? err.message : err);
114
- return null;
127
+ return existing ?? null;
115
128
  }
116
129
  }
117
130
  /**