@reticlehq/server 2.8.0 → 2.10.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 (318) hide show
  1. package/SKILL.md +130 -813
  2. package/dist/bridge/bridge.d.ts +23 -0
  3. package/dist/bridge/bridge.js +80 -2
  4. package/dist/bridge/bridge.test-harness.d.ts +5 -0
  5. package/dist/bridge/bridge.test-harness.js +6 -4
  6. package/dist/capsule/capsule-store.d.ts +2 -2
  7. package/dist/capsule/causal-summary.d.ts +16 -1
  8. package/dist/capsule/causal-summary.js +2 -1
  9. package/dist/cli/cli-doctor.js +41 -13
  10. package/dist/cli/cli-flow-commands.js +8 -8
  11. package/dist/cli/cli-launch.d.ts +0 -17
  12. package/dist/cli/cli-launch.js +0 -28
  13. package/dist/cli/cli-parse.d.ts +1 -1
  14. package/dist/cli/cli-parse.js +1 -1
  15. package/dist/cli/cli-port.d.ts +24 -0
  16. package/dist/cli/cli-port.js +66 -35
  17. package/dist/cli/cli-verify.d.ts +21 -0
  18. package/dist/cli/cli-verify.js +45 -1
  19. package/dist/cli/config-discovery.d.ts +35 -0
  20. package/dist/cli/config-discovery.js +163 -0
  21. package/dist/cli/doctor-daemon-line.js +4 -1
  22. package/dist/cli/doctor-project-line.d.ts +37 -0
  23. package/dist/cli/doctor-project-line.js +35 -0
  24. package/dist/cli/doctor-rows.d.ts +51 -0
  25. package/dist/cli/doctor-rows.js +54 -0
  26. package/dist/cli/doctor-sessions-line.js +6 -3
  27. package/dist/cli/drive-attach.d.ts +99 -0
  28. package/dist/cli/drive-attach.js +203 -0
  29. package/dist/cli/drive-command.d.ts +13 -0
  30. package/dist/cli/drive-command.js +103 -0
  31. package/dist/cli/open-note.d.ts +20 -0
  32. package/dist/cli/open-note.js +37 -0
  33. package/dist/cli/status-next-action.d.ts +37 -0
  34. package/dist/cli/status-next-action.js +61 -0
  35. package/dist/cli.js +65 -70
  36. package/dist/crawl/crawl.d.ts +7 -2
  37. package/dist/crawl/crawl.js +52 -9
  38. package/dist/ee/audit-log.js +2 -2
  39. package/dist/events/body-failures.d.ts +2 -2
  40. package/dist/events/contradiction-folds.d.ts +45 -0
  41. package/dist/events/contradiction-folds.js +42 -0
  42. package/dist/events/contradictions.d.ts +51 -1
  43. package/dist/events/contradictions.js +26 -7
  44. package/dist/events/declared.d.ts +45 -0
  45. package/dist/events/declared.js +73 -0
  46. package/dist/events/echo-mismatch.d.ts +9 -3
  47. package/dist/events/echo-mismatch.js +33 -5
  48. package/dist/events/event-filters.d.ts +1 -1
  49. package/dist/events/event-filters.js +7 -4
  50. package/dist/events/predicate-eval.d.ts +17 -91
  51. package/dist/events/predicate-eval.js +85 -326
  52. package/dist/events/predicate-parse.js +31 -3
  53. package/dist/events/predicate-route.d.ts +32 -0
  54. package/dist/events/predicate-route.js +102 -0
  55. package/dist/events/predicate-schema.d.ts +99 -0
  56. package/dist/events/predicate-schema.js +309 -0
  57. package/dist/events/predicate.d.ts +8 -0
  58. package/dist/events/predicate.js +127 -5
  59. package/dist/events/stale-response.d.ts +2 -3
  60. package/dist/events/stale-response.js +49 -2
  61. package/dist/events/unit-mismatch.d.ts +2 -2
  62. package/dist/flows/annotate-tools.js +3 -0
  63. package/dist/flows/annotate.js +18 -4
  64. package/dist/flows/coverage.d.ts +38 -5
  65. package/dist/flows/coverage.js +30 -4
  66. package/dist/flows/decision.d.ts +15 -0
  67. package/dist/flows/decision.js +1 -1
  68. package/dist/flows/flow-replay-run.js +8 -1
  69. package/dist/flows/flow-tools.js +9 -1
  70. package/dist/flows/replay.js +3 -0
  71. package/dist/flows/verify-change-tools.js +1 -1
  72. package/dist/honesty/accepted-write.d.ts +3 -0
  73. package/dist/honesty/accepted-write.js +8 -3
  74. package/dist/honesty/already-true.js +15 -0
  75. package/dist/honesty/blind-spots.js +4 -0
  76. package/dist/honesty/honesty.d.ts +11 -0
  77. package/dist/honesty/honesty.js +1 -0
  78. package/dist/honesty/uncaptured-bodies.d.ts +4 -2
  79. package/dist/honesty/uncaptured-bodies.js +9 -2
  80. package/dist/honesty/unread-outcome.d.ts +8 -0
  81. package/dist/honesty/unread-outcome.js +36 -9
  82. package/dist/honesty/unsettled.d.ts +9 -0
  83. package/dist/honesty/unsettled.js +7 -3
  84. package/dist/honesty/verified.d.ts +29 -3
  85. package/dist/honesty/verified.js +67 -8
  86. package/dist/http-server.d.ts +19 -0
  87. package/dist/http-server.js +202 -4
  88. package/dist/impact/impact-recorder.d.ts +23 -0
  89. package/dist/impact/impact-recorder.js +76 -0
  90. package/dist/impact/impact-store.d.ts +47 -0
  91. package/dist/impact/impact-store.js +171 -0
  92. package/dist/index.d.ts +25 -0
  93. package/dist/index.js +60 -10
  94. package/dist/init/agent-rules.d.ts +20 -0
  95. package/dist/init/agent-rules.js +36 -0
  96. package/dist/init/astro-patch.js +27 -1
  97. package/dist/init/confirm.d.ts +21 -3
  98. package/dist/init/confirm.js +32 -5
  99. package/dist/init/connect-steps.d.ts +2 -0
  100. package/dist/init/connect-steps.js +50 -0
  101. package/dist/init/csp-check.d.ts +38 -0
  102. package/dist/init/csp-check.js +92 -0
  103. package/dist/init/csp-doctor.d.ts +25 -0
  104. package/dist/init/csp-doctor.js +49 -0
  105. package/dist/init/cursor.js +6 -9
  106. package/dist/init/detect.d.ts +8 -0
  107. package/dist/init/detect.js +33 -3
  108. package/dist/init/mcp-clients.d.ts +1 -0
  109. package/dist/init/mcp-clients.js +51 -7
  110. package/dist/init/mcp.d.ts +16 -2
  111. package/dist/init/mcp.js +23 -1
  112. package/dist/init/plan-framework.d.ts +10 -0
  113. package/dist/init/plan-framework.js +25 -1
  114. package/dist/init/plan.d.ts +7 -2
  115. package/dist/init/plan.js +66 -30
  116. package/dist/init/run.js +11 -5
  117. package/dist/init/slash-command.d.ts +1 -1
  118. package/dist/init/slash-command.js +12 -4
  119. package/dist/init/snippets.d.ts +1 -1
  120. package/dist/init/snippets.js +35 -7
  121. package/dist/init/vite-config.js +24 -2
  122. package/dist/input/network-mock-tools.js +8 -1
  123. package/dist/input/scroll-find.d.ts +6 -0
  124. package/dist/input/scroll-find.js +17 -2
  125. package/dist/input/scroll-tools.js +4 -0
  126. package/dist/journal/attach-journal.d.ts +9 -1
  127. package/dist/journal/attach-journal.js +13 -2
  128. package/dist/journal/journal-query.d.ts +13 -0
  129. package/dist/journal/journal-query.js +28 -3
  130. package/dist/journal/workspace-gitignore.d.ts +10 -0
  131. package/dist/journal/workspace-gitignore.js +59 -0
  132. package/dist/license/license.d.ts +69 -4
  133. package/dist/license/license.js +84 -15
  134. package/dist/mcp/mcp-proxy.d.ts +7 -93
  135. package/dist/mcp/mcp-proxy.js +183 -250
  136. package/dist/mcp/mcp.d.ts +27 -2
  137. package/dist/mcp/mcp.js +77 -36
  138. package/dist/mcp/proxy-daemon-probe.d.ts +8 -0
  139. package/dist/mcp/proxy-daemon-probe.js +48 -0
  140. package/dist/mcp/proxy-handshake.d.ts +82 -1
  141. package/dist/mcp/proxy-handshake.js +110 -3
  142. package/dist/mcp/proxy-lifecycle.d.ts +10 -1
  143. package/dist/mcp/proxy-lifecycle.js +11 -2
  144. package/dist/mcp/proxy-log.d.ts +63 -0
  145. package/dist/mcp/proxy-log.js +132 -0
  146. package/dist/mcp/server-instructions.d.ts +17 -0
  147. package/dist/mcp/server-instructions.js +56 -0
  148. package/dist/mcp/shared-params.d.ts +27 -0
  149. package/dist/mcp/shared-params.js +36 -0
  150. package/dist/mcp/sse-frame-parser.d.ts +19 -0
  151. package/dist/mcp/sse-frame-parser.js +41 -0
  152. package/dist/project/reticle-dir.d.ts +2 -0
  153. package/dist/project/reticle-dir.js +1 -0
  154. package/dist/session/connection-memory.d.ts +59 -0
  155. package/dist/session/connection-memory.js +108 -0
  156. package/dist/session/dev-command.d.ts +26 -0
  157. package/dist/session/dev-command.js +89 -0
  158. package/dist/session/dev-server-probe.d.ts +68 -0
  159. package/dist/session/dev-server-probe.js +102 -7
  160. package/dist/session/last-act.d.ts +26 -0
  161. package/dist/session/last-act.js +35 -1
  162. package/dist/session/live-control-tools.js +42 -5
  163. package/dist/session/no-session-diagnosis.d.ts +55 -2
  164. package/dist/session/no-session-diagnosis.js +199 -22
  165. package/dist/session/no-session-next-action.d.ts +46 -0
  166. package/dist/session/no-session-next-action.js +85 -0
  167. package/dist/session/no-session-watch.d.ts +40 -1
  168. package/dist/session/no-session-watch.js +172 -11
  169. package/dist/session/pending-commands.d.ts +12 -0
  170. package/dist/session/pending-commands.js +13 -1
  171. package/dist/session/session-manager.d.ts +4 -0
  172. package/dist/session/session-manager.js +35 -5
  173. package/dist/session/session-readiness.js +1 -1
  174. package/dist/session/session.d.ts +10 -1
  175. package/dist/session/session.js +146 -5
  176. package/dist/telemetry/app-instrumented.d.ts +8 -0
  177. package/dist/telemetry/app-instrumented.js +10 -0
  178. package/dist/telemetry/bug-found.d.ts +18 -0
  179. package/dist/telemetry/bug-found.js +14 -0
  180. package/dist/telemetry/daemon-telemetry.js +29 -0
  181. package/dist/telemetry/install-source.d.ts +20 -0
  182. package/dist/telemetry/install-source.js +34 -0
  183. package/dist/telemetry/instrumentation-stall.d.ts +57 -0
  184. package/dist/telemetry/instrumentation-stall.js +103 -0
  185. package/dist/telemetry/license-activation.d.ts +48 -0
  186. package/dist/telemetry/license-activation.js +57 -0
  187. package/dist/telemetry/session-metrics.d.ts +8 -0
  188. package/dist/telemetry/session-metrics.js +10 -0
  189. package/dist/telemetry/telemetry.d.ts +3 -1
  190. package/dist/telemetry/telemetry.js +23 -3
  191. package/dist/tools/act-tools.js +166 -29
  192. package/dist/tools/assert-verdict.js +32 -7
  193. package/dist/tools/browser-tools.js +10 -0
  194. package/dist/tools/dynamic-tools.js +17 -8
  195. package/dist/tools/error-recovery.js +10 -6
  196. package/dist/tools/feedback-tools.js +31 -6
  197. package/dist/tools/invoke-tool.d.ts +1 -1
  198. package/dist/tools/invoke-tool.js +30 -8
  199. package/dist/tools/lease-hint.d.ts +30 -8
  200. package/dist/tools/lease-hint.js +65 -14
  201. package/dist/tools/lease-tools.d.ts +11 -0
  202. package/dist/tools/lease-tools.js +141 -96
  203. package/dist/tools/observe-tools.js +9 -3
  204. package/dist/tools/react-grace.d.ts +4 -0
  205. package/dist/tools/react-grace.js +6 -3
  206. package/dist/tools/read-tools.js +47 -1
  207. package/dist/tools/resolve-target.d.ts +33 -0
  208. package/dist/tools/resolve-target.js +53 -0
  209. package/dist/tools/settle-in-flight.d.ts +15 -0
  210. package/dist/tools/settle-in-flight.js +42 -2
  211. package/dist/tools/snapshot-delta.d.ts +3 -0
  212. package/dist/tools/snapshot-delta.js +67 -6
  213. package/dist/tools/tool-kit.d.ts +38 -3
  214. package/dist/tools/tool-names.d.ts +2 -0
  215. package/dist/tools/tool-names.js +2 -0
  216. package/dist/tools/tool-surface.d.ts +60 -0
  217. package/dist/tools/tool-surface.js +84 -19
  218. package/dist/tools/tools.js +61 -5
  219. package/dist/tools/unadvertised-help.js +9 -6
  220. package/docs/README.md +1 -2
  221. package/docs/actions.mdx +41 -8
  222. package/docs/agent-cheatsheet.md +6 -4
  223. package/docs/architecture.md +6 -0
  224. package/docs/benchmarks.md +60 -12
  225. package/docs/best-practices.mdx +11 -6
  226. package/docs/capabilities.mdx +204 -57
  227. package/docs/cli/affected.mdx +6 -4
  228. package/docs/cli/capsules.mdx +9 -8
  229. package/docs/cli/cloud.mdx +30 -4
  230. package/docs/cli/doctor.mdx +41 -8
  231. package/docs/cli/drive.mdx +34 -15
  232. package/docs/cli/gate.mdx +6 -6
  233. package/docs/cli/help.mdx +8 -6
  234. package/docs/cli/hunt.mdx +20 -4
  235. package/docs/cli/identify.mdx +16 -2
  236. package/docs/cli/init.mdx +35 -8
  237. package/docs/cli/kill.mdx +5 -5
  238. package/docs/cli/license.mdx +1 -1
  239. package/docs/cli/open.mdx +6 -0
  240. package/docs/cli/restart.mdx +6 -6
  241. package/docs/cli/serve.mdx +10 -3
  242. package/docs/cli/status.mdx +29 -5
  243. package/docs/cli/stop.mdx +8 -0
  244. package/docs/cli/verify.mdx +20 -1
  245. package/docs/cli/version.mdx +1 -1
  246. package/docs/cli/watch.mdx +9 -2
  247. package/docs/cli.mdx +44 -22
  248. package/docs/debugging.md +3 -1
  249. package/docs/deploy-checks.md +31 -2
  250. package/docs/desktop.mdx +276 -80
  251. package/docs/docs.json +13 -12
  252. package/docs/enterprise.md +9 -3
  253. package/docs/faq.mdx +49 -35
  254. package/docs/fixtures.md +2 -0
  255. package/docs/flows.md +7 -5
  256. package/docs/for-agents.md +3 -3
  257. package/docs/frameworks.mdx +46 -7
  258. package/docs/gate-plan.md +4 -2
  259. package/docs/gates.md +8 -6
  260. package/docs/getting-started.md +53 -12
  261. package/docs/human-control.md +2 -0
  262. package/docs/index.mdx +43 -1
  263. package/docs/install-agentic.mdx +13 -4
  264. package/docs/install-manual.mdx +6 -4
  265. package/docs/instrumentation.mdx +7 -1
  266. package/docs/integration-patterns.md +3 -1
  267. package/docs/local-registry.md +4 -2
  268. package/docs/multi-agent-testing.md +3 -1
  269. package/docs/packages/babel-plugin.mdx +15 -6
  270. package/docs/packages/browser.mdx +2 -2
  271. package/docs/packages/core.mdx +26 -16
  272. package/docs/packages/electron.mdx +5 -7
  273. package/docs/packages/eslint-plugin.mdx +5 -3
  274. package/docs/packages/next.mdx +3 -5
  275. package/docs/packages/react.mdx +3 -3
  276. package/docs/packages/server.mdx +2 -2
  277. package/docs/packages/tauri.mdx +6 -6
  278. package/docs/packages/test.mdx +18 -7
  279. package/docs/packages/vite-plugin.mdx +4 -6
  280. package/docs/packages.mdx +14 -13
  281. package/docs/platform-integration.md +2 -0
  282. package/docs/predicates.mdx +76 -7
  283. package/docs/quickstart.mdx +51 -5
  284. package/docs/recipes.mdx +63 -23
  285. package/docs/skill-file.mdx +3 -1
  286. package/docs/state-management.mdx +2 -0
  287. package/docs/system-map.md +3 -1
  288. package/docs/telemetry-contract.md +33 -5
  289. package/docs/telemetry-events.mdx +63 -18
  290. package/docs/telemetry.md +8 -5
  291. package/docs/testing.md +35 -1
  292. package/docs/token-efficiency.md +8 -2
  293. package/docs/tools-act-and-wait.mdx +64 -14
  294. package/docs/tools-act-sequence.mdx +34 -17
  295. package/docs/tools-act.mdx +24 -11
  296. package/docs/tools-assert.mdx +69 -10
  297. package/docs/tools-console.mdx +11 -7
  298. package/docs/tools-inspect.mdx +11 -8
  299. package/docs/tools-navigate.mdx +41 -5
  300. package/docs/tools-network.mdx +42 -9
  301. package/docs/tools-observe.mdx +66 -10
  302. package/docs/tools-overview.mdx +26 -23
  303. package/docs/tools-query.mdx +52 -8
  304. package/docs/tools-session-and-feedback.mdx +35 -9
  305. package/docs/tools-sessions.mdx +39 -10
  306. package/docs/tools-snapshot.mdx +57 -3
  307. package/docs/tools-state.mdx +24 -1
  308. package/docs/tools-tools-and-run.mdx +55 -24
  309. package/docs/tools-wait-for.mdx +34 -15
  310. package/docs/troubleshooting.mdx +80 -10
  311. package/docs/usage.md +46 -24
  312. package/docs/vs-chrome-devtools-mcp.mdx +41 -0
  313. package/docs/vs-playwright-mcp.mdx +42 -2
  314. package/docs/vs-screenshots.mdx +42 -1
  315. package/docs/what-is-reticle.mdx +1 -1
  316. package/docs/why-reticle.mdx +3 -1
  317. package/package.json +5 -5
  318. package/docs/desktop-apps.md +0 -265
@@ -0,0 +1,73 @@
1
+ /**
2
+ * What the caller DECLARED before acting — the oracle, read back.
3
+ *
4
+ * `act_and_wait` asks the agent to name the expected consequence BEFORE the action, and that
5
+ * declaration was then thrown away: everything downstream judged the window as if nobody had said
6
+ * what they were expecting. Two measured consequences, one bug:
7
+ *
8
+ * - An error path can be declared exactly — `{ net, POST, /api/login, status: 500 }` — and the
9
+ * contradiction hunter still reported `ui-advanced-request-failed`, because a failed request plus
10
+ * a moved DOM is all it can see. The failure was EXPECTED and said so in the predicate, so a
11
+ * verdict of "channels disagree" is a statement about a disagreement nobody had.
12
+ * - A destination whose content was asserted and FOUND was still reported as
13
+ * `route-rendered-nothing`, a clause the element evidence beside it disproves.
14
+ *
15
+ * Deliberately conservative about what counts as declared. Only what the caller REQUIRED is read:
16
+ * the top level and `allOf` chains. An `anyOf` branch may never have held, and a `not` declares the
17
+ * opposite of a consequence — honouring either would suppress a real finding on the strength of
18
+ * something that never happened. Pure: a predicate in, a description out.
19
+ */
20
+ import { PredicateKind } from '@reticlehq/core';
21
+ /** Below this, a status is a success or a redirect: not a declared failure. */
22
+ const FAILURE_STATUS_MIN = 400;
23
+ export function declaredExpectations(predicate) {
24
+ const netFailures = [];
25
+ let rendersContent = false;
26
+ const walk = (p) => {
27
+ switch (p.kind) {
28
+ case PredicateKind.ALL_OF:
29
+ for (const child of p.predicates)
30
+ walk(child);
31
+ return;
32
+ case PredicateKind.NET: {
33
+ const declaredFailure = false === p.ok || (p.status !== undefined && p.status >= FAILURE_STATUS_MIN);
34
+ if (!declaredFailure)
35
+ return;
36
+ netFailures.push({
37
+ ...(p.method === undefined ? {} : { method: p.method }),
38
+ ...(p.urlContains === undefined ? {} : { urlContains: p.urlContains }),
39
+ ...(p.status === undefined ? {} : { status: p.status }),
40
+ });
41
+ return;
42
+ }
43
+ case PredicateKind.ELEMENT:
44
+ case PredicateKind.TEXT:
45
+ if (true !== p.absent)
46
+ rendersContent = true;
47
+ return;
48
+ default:
49
+ return;
50
+ }
51
+ };
52
+ if (predicate !== undefined)
53
+ walk(predicate);
54
+ return { netFailures, rendersContent };
55
+ }
56
+ /**
57
+ * Does a call the window recorded match a failure the caller declared?
58
+ *
59
+ * Every field the caller named must agree — a declaration about `POST /api/login → 500` says nothing
60
+ * about `POST /api/orders`, and treating it as a blanket amnesty for failed requests would remove
61
+ * the check rather than inform it. Fields the caller left out are not constraints.
62
+ */
63
+ export function matchesDeclaredFailure(call, declared) {
64
+ return declared.some((d) => {
65
+ if (d.method !== undefined && d.method.toUpperCase() !== call.method.toUpperCase())
66
+ return false;
67
+ if (d.urlContains !== undefined && !call.url.includes(d.urlContains))
68
+ return false;
69
+ if (d.status !== undefined && d.status !== call.status)
70
+ return false;
71
+ return true;
72
+ });
73
+ }
@@ -1,4 +1,10 @@
1
1
  import { type ReticleEvent } from '@reticlehq/core';
2
- import type { Contradiction } from './contradictions.js';
3
- /** Contradictions for writes whose response echoes a different value than the request asked for. */
4
- export declare function findEchoMismatches(events: readonly ReticleEvent[]): Contradiction[];
2
+ import type { OwnContradiction } from './contradictions.js';
3
+ /**
4
+ * Contradictions for writes whose response echoes a different value than the request asked for.
5
+ *
6
+ * `actionSince` is the attribution floor `findContradictions` already keeps — the event time the
7
+ * action that opened this window was dispatched. A finding about a request that had already settled
8
+ * before the agent acted otherwise reads as a verdict on the change it just made.
9
+ */
10
+ export declare function findEchoMismatches(events: readonly ReticleEvent[], actionSince?: number): OwnContradiction[];
@@ -1,4 +1,4 @@
1
- import { ContradictionKind, EventType } from '@reticlehq/core';
1
+ import { ContradictionKind, EventType, MUTATING_METHODS } from '@reticlehq/core';
2
2
  /**
3
3
  * A write that returned OK and quietly did not apply part of what it was asked to.
4
4
  *
@@ -18,6 +18,11 @@ import { ContradictionKind, EventType } from '@reticlehq/core';
18
18
  * would fire on healthy APIs constantly, and a contradiction kind that cries wolf gets filtered out
19
19
  * by the agents reading it, taking the true positives with it. So:
20
20
  *
21
+ * - only requests that are WRITES are considered. Reported from the field: a lookup that sends its
22
+ * key in the body (`POST /get-branding` with `{workspace_id}`) and gets a record back was graded
23
+ * as a half-applied write. The gate was on the RESPONSE being a success and never on the REQUEST
24
+ * being a write, so every successful call carrying a body read as a save — and the same key name
25
+ * legitimately means different things on the two sides of a request/response pair;
21
26
  * - only keys the request actually SENT are considered;
22
27
  * - only scalars, since deep structural diffing is where the false positives live;
23
28
  * - values are compared NORMALISED (trimmed, case-folded, numbers as numbers), so `FR` vs `fr` and
@@ -89,12 +94,33 @@ function scalarsByKey(body, into = new Map(), depth = 0) {
89
94
  }
90
95
  const OK_MIN = 200;
91
96
  const OK_MAX = 300;
92
- /** Contradictions for writes whose response echoes a different value than the request asked for. */
93
- export function findEchoMismatches(events) {
97
+ /**
98
+ * Contradictions for writes whose response echoes a different value than the request asked for.
99
+ *
100
+ * `actionSince` is the attribution floor `findContradictions` already keeps — the event time the
101
+ * action that opened this window was dispatched. A finding about a request that had already settled
102
+ * before the agent acted otherwise reads as a verdict on the change it just made.
103
+ */
104
+ export function findEchoMismatches(events, actionSince) {
94
105
  const found = [];
95
106
  for (const event of events) {
96
107
  if (event.type !== EventType.NET_REQUEST)
97
108
  continue;
109
+ // `MUTATING_METHODS` is the one definition of "is this a write" in the codebase — the same list
110
+ // `contradictions.ts`'s `isMutating` reads. A read has no fields to half-apply.
111
+ //
112
+ // KNOWN LIMIT, stated because it is easy to read this fix as bigger than it is: POST is in that
113
+ // list, so a lookup that POSTs its key and gets a record back — the shape actually reported —
114
+ // still reaches the comparison below. Method is the only signal available here, and a read-shaped
115
+ // POST is genuinely indistinguishable from a write by method alone.
116
+ //
117
+ // The tempting next step is to skip a pair whose JSON types disagree (a string key sent, a number
118
+ // echoed). Deliberately not taken: that also silences `{"qty":"5"}` answered with `3`, which is a
119
+ // real dropped write. A false negative here is a missed bug in someone's app, and that is not a
120
+ // trade this file makes to quieten a false positive.
121
+ const method = 'string' === typeof event.data['method'] ? event.data['method'] : '';
122
+ if (!MUTATING_METHODS.includes(method.toUpperCase()))
123
+ continue;
98
124
  // IPC reports `ok` rather than a status; HTTP reports a status. Either must say success — a write
99
125
  // that already failed is a different (and louder) finding than one that half-applied.
100
126
  const status = event.data['status'];
@@ -126,12 +152,14 @@ export function findEchoMismatches(events) {
126
152
  if (0 === dropped.length)
127
153
  continue;
128
154
  const url = 'string' === typeof event.data['url'] ? event.data['url'] : '';
129
- const method = 'string' === typeof event.data['method'] ? event.data['method'] : '';
155
+ const attribution = actionSince !== undefined && event.t < actionSince
156
+ ? ' — this request completed before the action being verified, so the finding is not about that change'
157
+ : '';
130
158
  found.push({
131
159
  kind: ContradictionKind.WRITE_FIELD_IGNORED,
132
160
  claim: 'the write returned success and the page treated it as saved',
133
161
  counter: `its own echo shows ${String(dropped.length)} field(s) NOT applied — ${dropped.join('; ')}`,
134
- detail: `${method} ${url} — the server answered OK and returned a different value than it was asked to set, so the write half-applied; the UI has no way to know and will show the value the user typed`,
162
+ detail: `${method} ${url} — the server answered OK and returned a different value than it was asked to set, so the write half-applied; the UI has no way to know and will show the value the user typed${attribution}`,
135
163
  });
136
164
  }
137
165
  return found;
@@ -57,7 +57,7 @@ interface NetCallView {
57
57
  */
58
58
  oneWay?: boolean;
59
59
  }
60
- export declare function projectNetCall(e: ReticleEvent): NetCallView;
60
+ export declare function projectNetCall(e: ReticleEvent, includeBodies?: boolean): NetCallView;
61
61
  /**
62
62
  * Compact console-log summary for reticle_console output.
63
63
  *
@@ -86,7 +86,7 @@ export function reconcileNet(events) {
86
86
  .map((e) => ({ ...e, data: { ...e.data, status: 'pending', pending: true } }));
87
87
  return [...completed, ...unresolved].sort((a, b) => a.t - b.t);
88
88
  }
89
- export function projectNetCall(e) {
89
+ export function projectNetCall(e, includeBodies = true) {
90
90
  const status = e.data['status'];
91
91
  const ms = asNumber(e.data['durationMs']);
92
92
  const statusText = asString(e.data['statusText']);
@@ -106,11 +106,14 @@ export function projectNetCall(e) {
106
106
  view.contentType = contentType;
107
107
  if (responseSize !== undefined)
108
108
  view.responseSize = responseSize;
109
- if (requestBody !== undefined)
109
+ // Bodies dominate the payload of the most-called read tool, and are only occasionally the part
110
+ // anyone wanted — `bodies: false` on reticle_network drops them for a body-free listing (#401).
111
+ if (includeBodies && requestBody !== undefined)
110
112
  view.requestBody = requestBody;
111
- if (responseBody !== undefined)
113
+ if (includeBodies && responseBody !== undefined)
112
114
  view.responseBody = responseBody;
113
- if (true === e.data['requestBodyTruncated'] || true === e.data['responseBodyTruncated']) {
115
+ if (includeBodies &&
116
+ (true === e.data['requestBodyTruncated'] || true === e.data['responseBodyTruncated'])) {
114
117
  view.bodyTruncated = true;
115
118
  }
116
119
  if (ms !== undefined)
@@ -1,91 +1,6 @@
1
- import { ElementState, PredicateKind, type ElementDescriptor, type ElementQuery, type ReticleEvent } from '@reticlehq/core';
2
- import { z } from 'zod';
3
- export type Predicate = {
4
- kind: typeof PredicateKind.ELEMENT;
5
- query: ElementQuery;
6
- state?: ElementState;
7
- absent?: boolean;
8
- } | {
9
- kind: typeof PredicateKind.TEXT;
10
- contains: string;
11
- visible?: boolean;
12
- absent?: boolean;
13
- } | {
14
- kind: typeof PredicateKind.NET;
15
- method?: string;
16
- urlContains?: string;
17
- status?: number;
18
- /** Did the call succeed? The honest field for IPC, which has no status code. */
19
- ok?: boolean;
20
- since?: number;
21
- count?: number;
22
- } | {
23
- kind: typeof PredicateKind.ROUTE;
24
- pathname?: string;
25
- contains?: string;
26
- since?: number;
27
- } | {
28
- kind: typeof PredicateKind.CONSOLE;
29
- level?: string;
30
- absent?: boolean;
31
- since?: number;
32
- } | {
33
- kind: typeof PredicateKind.ANIMATION;
34
- name?: string;
35
- target?: string;
36
- completed?: boolean;
37
- since?: number;
38
- } | {
39
- kind: typeof PredicateKind.SIGNAL;
40
- name?: string;
41
- dataMatches?: Record<string, unknown>;
42
- since?: number;
43
- } | {
44
- kind: typeof PredicateKind.STATE;
45
- store?: string;
46
- path: string;
47
- equals?: unknown;
48
- } | {
49
- kind: typeof PredicateKind.SETTLED;
50
- quietMs?: number;
51
- } | {
52
- kind: typeof PredicateKind.ALL_OF;
53
- predicates: Predicate[];
54
- } | {
55
- kind: typeof PredicateKind.ANY_OF;
56
- predicates: Predicate[];
57
- } | {
58
- kind: typeof PredicateKind.NOT;
59
- predicate: Predicate;
60
- };
61
- export interface ResidualQueryChecks {
62
- /** Dropped fields this side CAN check, as [field, wanted value] pairs. */
63
- checks: [string, string][];
64
- /** Dropped fields with no descriptor to check them against — refuse rather than ignore. */
65
- unusable: string[];
66
- }
67
- /**
68
- * Split a query's dropped fields into the ones the server can still enforce and the ones it cannot.
69
- *
70
- * The alternative — refuse every dropped field — breaks calls that work today and that our own
71
- * cheatsheet advertises (`{ role: "button", text: "Save" }`), and breaks them into no verdict at all.
72
- * Enforcing what we can and refusing only the rest keeps those calls working AS WRITTEN, which is the
73
- * outcome the caller was already assuming.
74
- */
75
- export declare function residualQueryChecks(query: ElementQuery): ResidualQueryChecks;
76
- /** Does this element satisfy every field the locator dropped? */
77
- export declare function satisfiesResiduals(element: ElementDescriptor, checks: readonly [string, string][]): boolean;
78
- /** How the element's own reading of a dropped field should be REPORTED back on a failure. */
79
- export declare function describeResidual(element: ElementDescriptor, field: string): string;
80
- export declare const PredicateSchema: z.ZodType<Predicate>;
81
- /**
82
- * The fields a given predicate kind accepts, read off the schema itself.
83
- *
84
- * Derived rather than listed so the two can never disagree: a rejection message that names a stale
85
- * field set is worse than one that names none, because the agent trusts it and retries into the same
86
- * wall. Empty for a kind that is not in the union.
87
- */
88
- export declare function predicateFieldsFor(kind: string): readonly string[];
1
+ import { PredicateKind, type ReticleEvent } from '@reticlehq/core';
2
+ import type { Predicate } from './predicate-schema.js';
3
+ export * from './predicate-schema.js';
89
4
  export interface EvalResult {
90
5
  pass: boolean;
91
6
  /**
@@ -104,6 +19,19 @@ export interface EvalResult {
104
19
  * not a defect in the user's app, and reporting it as one puts agent mistakes into the bug count.
105
20
  */
106
21
  inconclusive?: string;
22
+ /**
23
+ * No later event in this window can change this answer, so waiting out the rest of the budget
24
+ * buys nothing.
25
+ *
26
+ * Set ONLY where that is provable, which is rarer than it looks. "The toast never appeared" is not
27
+ * decided — it is only knowable when the budget ends, and ending early there would manufacture the
28
+ * false negative the budget exists to prevent. Exact cardinality IS decided once exceeded, because
29
+ * a window only accumulates matches and a count cannot come back down.
30
+ *
31
+ * A scheduling fact, never a verdict: `pass` already says what the answer is, and this only says
32
+ * that it is final.
33
+ */
34
+ decided?: boolean;
107
35
  evidence?: unknown;
108
36
  failureReason?: string;
109
37
  /**
@@ -132,6 +60,7 @@ export interface EvalResult {
132
60
  expected?: string;
133
61
  assertion?: string;
134
62
  }
63
+ export declare function str(value: unknown): string | undefined;
135
64
  /**
136
65
  * Match one value against a pattern. Supports `*` (present), strict equality, and operators:
137
66
  * `{$gte,$lte,$gt,$lt}` (numbers), `{$contains}` (array membership or substring), `{$length}`.
@@ -140,9 +69,6 @@ export declare function matchValue(got: unknown, want: unknown): boolean;
140
69
  export declare function evalNet(events: ReticleEvent[], p: Extract<Predicate, {
141
70
  kind: typeof PredicateKind.NET;
142
71
  }>): EvalResult;
143
- export declare function evalRoute(events: ReticleEvent[], p: Extract<Predicate, {
144
- kind: typeof PredicateKind.ROUTE;
145
- }>): EvalResult;
146
72
  export declare function evalConsole(events: ReticleEvent[], p: Extract<Predicate, {
147
73
  kind: typeof PredicateKind.CONSOLE;
148
74
  }>): EvalResult;