codetrellis 0.0.0-stage → 0.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 (448) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +27 -2
  3. package/bin/codetrellis.mjs +4 -0
  4. package/package.json +41 -4
  5. package/reader/material-reader.mjs +130346 -0
  6. package/resources/tree-sitter/README.md +150 -0
  7. package/resources/tree-sitter/tree-sitter-c-sharp.wasm +0 -0
  8. package/resources/tree-sitter/tree-sitter-go.wasm +0 -0
  9. package/resources/tree-sitter/tree-sitter-java.wasm +0 -0
  10. package/resources/tree-sitter/tree-sitter-javascript.wasm +0 -0
  11. package/resources/tree-sitter/tree-sitter-kotlin.wasm +0 -0
  12. package/resources/tree-sitter/tree-sitter-php.wasm +0 -0
  13. package/resources/tree-sitter/tree-sitter-python.wasm +0 -0
  14. package/resources/tree-sitter/tree-sitter-ruby.wasm +0 -0
  15. package/resources/tree-sitter/tree-sitter-rust.wasm +0 -0
  16. package/resources/tree-sitter/tree-sitter-swift.wasm +0 -0
  17. package/resources/tree-sitter/tree-sitter-tsx.wasm +0 -0
  18. package/resources/tree-sitter/tree-sitter-typescript.wasm +0 -0
  19. package/resources/tree-sitter/tree-sitter.wasm +0 -0
  20. package/src/backend/agent/claude-code-watcher.js +270 -0
  21. package/src/backend/index.js +8 -0
  22. package/src/backend/lib/sha256-file.js +39 -0
  23. package/src/backend/lib/zip-entries.js +98 -0
  24. package/src/backend/lifecycle.js +102 -0
  25. package/src/backend/mcp/binding-headers.js +54 -0
  26. package/src/backend/mcp/client-identity.js +44 -0
  27. package/src/backend/mcp/connector/command.js +110 -0
  28. package/src/backend/mcp/connector/core.js +278 -0
  29. package/src/backend/mcp/connector/files.js +121 -0
  30. package/src/backend/mcp/connector/hook.js +284 -0
  31. package/src/backend/mcp/connector/main.js +126 -0
  32. package/src/backend/mcp/connector/sse-upstream.js +152 -0
  33. package/src/backend/mcp/helpers.js +43 -0
  34. package/src/backend/mcp/prompt-builders.js +104 -0
  35. package/src/backend/mcp/resources.js +197 -0
  36. package/src/backend/mcp/server.js +813 -0
  37. package/src/backend/mcp/skill-guide.js +1528 -0
  38. package/src/backend/mcp/tools/architecture-tools.js +273 -0
  39. package/src/backend/mcp/tools/audio-tools.js +117 -0
  40. package/src/backend/mcp/tools/awareness-tools.js +623 -0
  41. package/src/backend/mcp/tools/budget-tools.js +185 -0
  42. package/src/backend/mcp/tools/channel-tools.js +205 -0
  43. package/src/backend/mcp/tools/contribution-tools.js +181 -0
  44. package/src/backend/mcp/tools/drift-tools.js +252 -0
  45. package/src/backend/mcp/tools/git-tools.js +316 -0
  46. package/src/backend/mcp/tools/governance-tools.js +175 -0
  47. package/src/backend/mcp/tools/graph-tools.js +181 -0
  48. package/src/backend/mcp/tools/intake-tools.js +244 -0
  49. package/src/backend/mcp/tools/mobile-tools.js +118 -0
  50. package/src/backend/mcp/tools/peer-tools.js +330 -0
  51. package/src/backend/mcp/tools/plan-item-tools.js +1601 -0
  52. package/src/backend/mcp/tools/plan-tools.js +680 -0
  53. package/src/backend/mcp/tools/presence-tools.js +185 -0
  54. package/src/backend/mcp/tools/project-config-tools.js +150 -0
  55. package/src/backend/mcp/tools/review-tools.js +172 -0
  56. package/src/backend/mcp/tools/session-tools.js +554 -0
  57. package/src/backend/mcp/tools/system-docs-tools.js +168 -0
  58. package/src/backend/mcp/tools/terminal-tools.js +186 -0
  59. package/src/backend/mcp/tools/test-tools.js +117 -0
  60. package/src/backend/mcp/tools/ui-tools.js +356 -0
  61. package/src/backend/mcp/types.js +15 -0
  62. package/src/backend/middleware/local-auth.js +116 -0
  63. package/src/backend/server.js +5951 -0
  64. package/src/backend/services/agent-event-log.js +350 -0
  65. package/src/backend/services/architecture-rule.js +584 -0
  66. package/src/backend/services/architecture-rules.js +275 -0
  67. package/src/backend/services/artefact-content-service.js +181 -0
  68. package/src/backend/services/artefact-service.js +305 -0
  69. package/src/backend/services/artefact-watcher.js +167 -0
  70. package/src/backend/services/ast-parser.js +327 -0
  71. package/src/backend/services/audio-buffer-service.js +152 -0
  72. package/src/backend/services/awareness-notices.js +139 -0
  73. package/src/backend/services/awareness-replies.js +194 -0
  74. package/src/backend/services/awareness-service.js +344 -0
  75. package/src/backend/services/awareness-signals.js +267 -0
  76. package/src/backend/services/baseline-store.js +83 -0
  77. package/src/backend/services/branch-workstreams.js +341 -0
  78. package/src/backend/services/breakpoint-service.js +584 -0
  79. package/src/backend/services/brief-service.js +299 -0
  80. package/src/backend/services/budget-service.js +528 -0
  81. package/src/backend/services/callsites/base.js +15 -0
  82. package/src/backend/services/callsites/csharp.js +148 -0
  83. package/src/backend/services/callsites/go.js +163 -0
  84. package/src/backend/services/callsites/index.js +53 -0
  85. package/src/backend/services/callsites/kotlin.js +123 -0
  86. package/src/backend/services/callsites/process-env.js +125 -0
  87. package/src/backend/services/callsites/python.js +91 -0
  88. package/src/backend/services/callsites/ruby.js +164 -0
  89. package/src/backend/services/callsites/shared.js +198 -0
  90. package/src/backend/services/callsites/swift.js +115 -0
  91. package/src/backend/services/callsites/typescript.js +87 -0
  92. package/src/backend/services/capability-token.js +108 -0
  93. package/src/backend/services/change-check.js +156 -0
  94. package/src/backend/services/channel-dispatcher-service.js +235 -0
  95. package/src/backend/services/channel-event-file-service.js +125 -0
  96. package/src/backend/services/channel-event-service.js +297 -0
  97. package/src/backend/services/check-runs.js +177 -0
  98. package/src/backend/services/checkout-identity.js +73 -0
  99. package/src/backend/services/claude-code-parallel.js +221 -0
  100. package/src/backend/services/claude-desktop-config.js +186 -0
  101. package/src/backend/services/cli-install.js +249 -0
  102. package/src/backend/services/clock.js +57 -0
  103. package/src/backend/services/cloud-files.js +191 -0
  104. package/src/backend/services/coalesce.js +50 -0
  105. package/src/backend/services/code-breakpoints.js +291 -0
  106. package/src/backend/services/comment-service.js +168 -0
  107. package/src/backend/services/commit-attribution.js +134 -0
  108. package/src/backend/services/commit-edges.js +99 -0
  109. package/src/backend/services/confined-fs.js +241 -0
  110. package/src/backend/services/conformity-gate.js +132 -0
  111. package/src/backend/services/contribution-service.js +409 -0
  112. package/src/backend/services/coverage-service.js +173 -0
  113. package/src/backend/services/criteria-service.js +721 -0
  114. package/src/backend/services/criterion-checks.js +387 -0
  115. package/src/backend/services/criterion-loop-service.js +411 -0
  116. package/src/backend/services/cross-system-service.js +231 -0
  117. package/src/backend/services/database.js +916 -0
  118. package/src/backend/services/db-schema.js +1358 -0
  119. package/src/backend/services/deviation-service.js +344 -0
  120. package/src/backend/services/diff-engine.js +168 -0
  121. package/src/backend/services/evidence.js +363 -0
  122. package/src/backend/services/external-intake-service.js +269 -0
  123. package/src/backend/services/external-pointer-service.js +195 -0
  124. package/src/backend/services/external-refs-service.js +214 -0
  125. package/src/backend/services/file-history.js +158 -0
  126. package/src/backend/services/file-watcher.js +193 -0
  127. package/src/backend/services/folder-requests.js +118 -0
  128. package/src/backend/services/freeze-service.js +189 -0
  129. package/src/backend/services/gemini-cli-hook.js +172 -0
  130. package/src/backend/services/git-activity-service.js +325 -0
  131. package/src/backend/services/git-blobs.js +70 -0
  132. package/src/backend/services/git-branches.js +357 -0
  133. package/src/backend/services/git-checkout.js +153 -0
  134. package/src/backend/services/git-commit-service.js +91 -0
  135. package/src/backend/services/git-env.js +121 -0
  136. package/src/backend/services/git-identity.js +84 -0
  137. package/src/backend/services/git-refs.js +386 -0
  138. package/src/backend/services/git-safety.js +58 -0
  139. package/src/backend/services/grant-guard.js +67 -0
  140. package/src/backend/services/html-view-policy.js +135 -0
  141. package/src/backend/services/human-decision.js +55 -0
  142. package/src/backend/services/importers.js +159 -0
  143. package/src/backend/services/intent-service.js +105 -0
  144. package/src/backend/services/ipc-dispatcher.js +164 -0
  145. package/src/backend/services/item-git-state.js +208 -0
  146. package/src/backend/services/line-changes.js +232 -0
  147. package/src/backend/services/line-history.js +174 -0
  148. package/src/backend/services/local-api-changes.js +40 -0
  149. package/src/backend/services/logger.js +217 -0
  150. package/src/backend/services/material-footprints.js +203 -0
  151. package/src/backend/services/material-place.js +82 -0
  152. package/src/backend/services/material-reader/docx-markdown.js +198 -0
  153. package/src/backend/services/material-reader/fixtures.test-helper.js +169 -0
  154. package/src/backend/services/material-reader/quote.js +44 -0
  155. package/src/backend/services/material-reader/reader-host.js +202 -0
  156. package/src/backend/services/material-signals.js +141 -0
  157. package/src/backend/services/mcp-capabilities.js +390 -0
  158. package/src/backend/services/mdns-service.js +304 -0
  159. package/src/backend/services/mobile-api-server.js +281 -0
  160. package/src/backend/services/mobile-approvals.js +306 -0
  161. package/src/backend/services/mobile-awareness.js +180 -0
  162. package/src/backend/services/mobile-breakpoints.js +115 -0
  163. package/src/backend/services/mobile-budget.js +98 -0
  164. package/src/backend/services/mobile-freeze.js +106 -0
  165. package/src/backend/services/mobile-proposals.js +100 -0
  166. package/src/backend/services/mobile-rpc-service.js +1289 -0
  167. package/src/backend/services/mobile-workstreams.js +160 -0
  168. package/src/backend/services/monorepo-detector.js +131 -0
  169. package/src/backend/services/other-work.js +45 -0
  170. package/src/backend/services/pack-seal.js +129 -0
  171. package/src/backend/services/paired-device-service.js +192 -0
  172. package/src/backend/services/pairing-server.js +323 -0
  173. package/src/backend/services/pairing-service.js +200 -0
  174. package/src/backend/services/pantry-resolution-service.js +166 -0
  175. package/src/backend/services/parsers/base.js +124 -0
  176. package/src/backend/services/parsers/csharp.js +224 -0
  177. package/src/backend/services/parsers/go.js +180 -0
  178. package/src/backend/services/parsers/index.js +101 -0
  179. package/src/backend/services/parsers/java.js +103 -0
  180. package/src/backend/services/parsers/kotlin.js +252 -0
  181. package/src/backend/services/parsers/php.js +113 -0
  182. package/src/backend/services/parsers/python.js +189 -0
  183. package/src/backend/services/parsers/ruby.js +212 -0
  184. package/src/backend/services/parsers/rust.js +97 -0
  185. package/src/backend/services/parsers/swift.js +221 -0
  186. package/src/backend/services/parsers/typescript.js +235 -0
  187. package/src/backend/services/pattern-scan.js +65 -0
  188. package/src/backend/services/patterns.js +281 -0
  189. package/src/backend/services/peer-audit-service.js +123 -0
  190. package/src/backend/services/peer-auth.js +153 -0
  191. package/src/backend/services/peer-capabilities.js +209 -0
  192. package/src/backend/services/peer-connection-service.js +450 -0
  193. package/src/backend/services/peer-grants.js +33 -0
  194. package/src/backend/services/persistence.js +116 -0
  195. package/src/backend/services/personal-sync-service.js +232 -0
  196. package/src/backend/services/pipeline-approvals.js +72 -0
  197. package/src/backend/services/pipeline.js +232 -0
  198. package/src/backend/services/plan-arrivals.js +68 -0
  199. package/src/backend/services/plan-changes-service.js +273 -0
  200. package/src/backend/services/plan-conflict-service.js +288 -0
  201. package/src/backend/services/plan-dependencies.js +91 -0
  202. package/src/backend/services/plan-doc-guard.js +90 -0
  203. package/src/backend/services/plan-documents-service.js +275 -0
  204. package/src/backend/services/plan-event-service.js +154 -0
  205. package/src/backend/services/plan-file-refs.js +106 -0
  206. package/src/backend/services/plan-file-service.js +1564 -0
  207. package/src/backend/services/plan-history-service.js +156 -0
  208. package/src/backend/services/plan-import-service.js +261 -0
  209. package/src/backend/services/plan-item-service.js +1223 -0
  210. package/src/backend/services/plan-migrate-service.js +395 -0
  211. package/src/backend/services/plan-overlay-service.js +173 -0
  212. package/src/backend/services/plan-phases-service.js +182 -0
  213. package/src/backend/services/plan-progress-service.js +134 -0
  214. package/src/backend/services/plan-review-service.js +262 -0
  215. package/src/backend/services/plan-service.js +682 -0
  216. package/src/backend/services/plan-status.js +113 -0
  217. package/src/backend/services/plan-template-publish-service.js +217 -0
  218. package/src/backend/services/plan-templates-service.js +242 -0
  219. package/src/backend/services/plan-templates.js +1164 -0
  220. package/src/backend/services/planned-overlap-actions.js +200 -0
  221. package/src/backend/services/plans-home.js +224 -0
  222. package/src/backend/services/play-forward.js +256 -0
  223. package/src/backend/services/playback-service.js +117 -0
  224. package/src/backend/services/power-service.js +167 -0
  225. package/src/backend/services/power-signals.js +81 -0
  226. package/src/backend/services/pr-draft-service.js +142 -0
  227. package/src/backend/services/presence-service.js +122 -0
  228. package/src/backend/services/pricing.js +60 -0
  229. package/src/backend/services/project-config-service.js +474 -0
  230. package/src/backend/services/project-scanner.js +184 -0
  231. package/src/backend/services/projection-service.js +69 -0
  232. package/src/backend/services/push-notification-service.js +288 -0
  233. package/src/backend/services/recent-projects-service.js +204 -0
  234. package/src/backend/services/record-chain.js +162 -0
  235. package/src/backend/services/recurrence-rule.js +63 -0
  236. package/src/backend/services/recurring-agent.js +89 -0
  237. package/src/backend/services/recurring-scheduler.js +77 -0
  238. package/src/backend/services/recurring-service.js +276 -0
  239. package/src/backend/services/reference-service.js +169 -0
  240. package/src/backend/services/release-public-key.js +30 -0
  241. package/src/backend/services/release-signature.js +64 -0
  242. package/src/backend/services/remote-audio-service.js +224 -0
  243. package/src/backend/services/remote-interaction-service.js +275 -0
  244. package/src/backend/services/remote-terminal-service.js +324 -0
  245. package/src/backend/services/rendition/child/main.js +76 -0
  246. package/src/backend/services/rendition/engine-host.js +249 -0
  247. package/src/backend/services/rendition/engine-lock.js +39 -0
  248. package/src/backend/services/rendition/engine-lock.json +14 -0
  249. package/src/backend/services/rendition/engine-manifest.js +97 -0
  250. package/src/backend/services/rendition/network-proof.js +73 -0
  251. package/src/backend/services/rendition/rendition-service.js +175 -0
  252. package/src/backend/services/replay-frames.js +387 -0
  253. package/src/backend/services/replay-state.js +201 -0
  254. package/src/backend/services/resolvers/base.js +108 -0
  255. package/src/backend/services/resolvers/csharp.js +109 -0
  256. package/src/backend/services/resolvers/go.js +105 -0
  257. package/src/backend/services/resolvers/index.js +60 -0
  258. package/src/backend/services/resolvers/java.js +63 -0
  259. package/src/backend/services/resolvers/kotlin.js +109 -0
  260. package/src/backend/services/resolvers/php.js +89 -0
  261. package/src/backend/services/resolvers/python.js +154 -0
  262. package/src/backend/services/resolvers/ruby.js +103 -0
  263. package/src/backend/services/resolvers/rust.js +77 -0
  264. package/src/backend/services/resolvers/swift.js +98 -0
  265. package/src/backend/services/resolvers/typescript.js +71 -0
  266. package/src/backend/services/retention.js +54 -0
  267. package/src/backend/services/review-architecture.js +236 -0
  268. package/src/backend/services/review-attest.js +68 -0
  269. package/src/backend/services/review-bundle.js +226 -0
  270. package/src/backend/services/review-graduation.js +93 -0
  271. package/src/backend/services/review-host/bitbucket.js +79 -0
  272. package/src/backend/services/review-host/detect.js +80 -0
  273. package/src/backend/services/review-host/github.js +97 -0
  274. package/src/backend/services/review-host/gitlab.js +77 -0
  275. package/src/backend/services/review-host/host-state.js +103 -0
  276. package/src/backend/services/review-host/http.js +58 -0
  277. package/src/backend/services/review-host/switch.js +120 -0
  278. package/src/backend/services/review-marks.js +72 -0
  279. package/src/backend/services/review-notes.js +169 -0
  280. package/src/backend/services/review-other-work.js +90 -0
  281. package/src/backend/services/review-queue-service.js +147 -0
  282. package/src/backend/services/review-risk.js +127 -0
  283. package/src/backend/services/review-task.js +121 -0
  284. package/src/backend/services/rule-approvals.js +275 -0
  285. package/src/backend/services/rule-baseline.js +134 -0
  286. package/src/backend/services/rule-changes.js +162 -0
  287. package/src/backend/services/rule-preview.js +47 -0
  288. package/src/backend/services/rule-proposals.js +104 -0
  289. package/src/backend/services/rule-scope.js +76 -0
  290. package/src/backend/services/rulebook.js +286 -0
  291. package/src/backend/services/rules-at.js +56 -0
  292. package/src/backend/services/rules-overview.js +88 -0
  293. package/src/backend/services/schema-reconciler.js +250 -0
  294. package/src/backend/services/secret-store.js +119 -0
  295. package/src/backend/services/section-workstreams.js +95 -0
  296. package/src/backend/services/self-write-tracker.js +75 -0
  297. package/src/backend/services/sensor-bridge-service.js +285 -0
  298. package/src/backend/services/session-service.js +192 -0
  299. package/src/backend/services/settings-service.js +324 -0
  300. package/src/backend/services/signal-breakpoints.js +172 -0
  301. package/src/backend/services/signed-approval-record.js +86 -0
  302. package/src/backend/services/signed-approvals.js +244 -0
  303. package/src/backend/services/signoff-pack.js +328 -0
  304. package/src/backend/services/signoff-rows.js +114 -0
  305. package/src/backend/services/skill-arrival-service.js +138 -0
  306. package/src/backend/services/skill-model.js +193 -0
  307. package/src/backend/services/skill-use-service.js +140 -0
  308. package/src/backend/services/skills-service.js +150 -0
  309. package/src/backend/services/snapshot-compare-service.js +397 -0
  310. package/src/backend/services/source-control.js +189 -0
  311. package/src/backend/services/spec-links-service.js +123 -0
  312. package/src/backend/services/spec-proposal-withdraw.js +50 -0
  313. package/src/backend/services/spec-proposals-service.js +470 -0
  314. package/src/backend/services/sql/embedded.js +95 -0
  315. package/src/backend/services/sql/index.js +63 -0
  316. package/src/backend/services/sql/refs.js +357 -0
  317. package/src/backend/services/sql/schema.js +312 -0
  318. package/src/backend/services/sql/tokenizer.js +195 -0
  319. package/src/backend/services/stack-overlaps.js +117 -0
  320. package/src/backend/services/stack-service.js +225 -0
  321. package/src/backend/services/state-sync-service.js +384 -0
  322. package/src/backend/services/stuck-sensor-service.js +200 -0
  323. package/src/backend/services/system-discovery.js +481 -0
  324. package/src/backend/services/system-docs-service.js +701 -0
  325. package/src/backend/services/task-attachments-service.js +340 -0
  326. package/src/backend/services/task-grounding.js +60 -0
  327. package/src/backend/services/task-records/check-run-record.js +146 -0
  328. package/src/backend/services/task-records/check-runs.js +115 -0
  329. package/src/backend/services/task-records/heads.js +88 -0
  330. package/src/backend/services/task-records/material-reads.js +225 -0
  331. package/src/backend/services/task-records/read-record.js +100 -0
  332. package/src/backend/services/task-records/record.js +173 -0
  333. package/src/backend/services/task-records/run-record.js +136 -0
  334. package/src/backend/services/task-records/shared-state.js +548 -0
  335. package/src/backend/services/task-records/signing.js +140 -0
  336. package/src/backend/services/task-records/split-signals.js +52 -0
  337. package/src/backend/services/task-records/test-runs.js +164 -0
  338. package/src/backend/services/task-records/trust.js +243 -0
  339. package/src/backend/services/task-rules.js +81 -0
  340. package/src/backend/services/task-workstreams.js +90 -0
  341. package/src/backend/services/terminal-history-service.js +266 -0
  342. package/src/backend/services/terminal-service.js +267 -0
  343. package/src/backend/services/test-clock.js +53 -0
  344. package/src/backend/services/tests/grounding.js +266 -0
  345. package/src/backend/services/tests/junit.js +104 -0
  346. package/src/backend/services/tests/teammate-runs.js +134 -0
  347. package/src/backend/services/tests/test-results.js +234 -0
  348. package/src/backend/services/tree-watcher.js +204 -0
  349. package/src/backend/services/trellis-service.js +204 -0
  350. package/src/backend/services/trusted-roots.js +186 -0
  351. package/src/backend/services/update-download-service.js +342 -0
  352. package/src/backend/services/update-service.js +266 -0
  353. package/src/backend/services/watch-ignore.js +76 -0
  354. package/src/backend/services/webhook-egress.js +206 -0
  355. package/src/backend/services/webrtc-service.js +413 -0
  356. package/src/backend/services/work-changes.js +59 -0
  357. package/src/backend/services/workstream-binding.js +82 -0
  358. package/src/backend/services/workstream-commits.js +116 -0
  359. package/src/backend/services/workstream-imports.js +177 -0
  360. package/src/backend/services/workstream-service.js +335 -0
  361. package/src/backend/services/workstream-symbols.js +223 -0
  362. package/src/backend/services/workstream-watch-service.js +391 -0
  363. package/src/backend/services/worktree-service.js +235 -0
  364. package/src/cli/agent.js +100 -0
  365. package/src/cli/args.js +209 -0
  366. package/src/cli/conformity.js +106 -0
  367. package/src/cli/desktop.js +91 -0
  368. package/src/cli/main.js +411 -0
  369. package/src/cli/pipeline.js +109 -0
  370. package/src/cli/plan-verbs.js +259 -0
  371. package/src/cli/review-adapters.js +220 -0
  372. package/src/cli/review-output.js +93 -0
  373. package/src/cli/review-post.js +89 -0
  374. package/src/cli/review-sink.js +183 -0
  375. package/src/cli/review-skill.js +44 -0
  376. package/src/cli/review-verify.js +104 -0
  377. package/src/cli/review.js +386 -0
  378. package/src/cli/sarif.js +148 -0
  379. package/src/cli/verbs.js +360 -0
  380. package/src/shared/build-info.js +39 -0
  381. package/src/shared/lib/agent-review.js +199 -0
  382. package/src/shared/lib/agent-turns.js +140 -0
  383. package/src/shared/lib/awareness-digest.js +120 -0
  384. package/src/shared/lib/branch-name.js +45 -0
  385. package/src/shared/lib/breakpoint-words.js +118 -0
  386. package/src/shared/lib/budget-words.js +56 -0
  387. package/src/shared/lib/burst.js +69 -0
  388. package/src/shared/lib/call-entry.js +105 -0
  389. package/src/shared/lib/check-compare.js +42 -0
  390. package/src/shared/lib/check-words.js +216 -0
  391. package/src/shared/lib/csv.js +62 -0
  392. package/src/shared/lib/folder-entry.js +87 -0
  393. package/src/shared/lib/freeze-words.js +60 -0
  394. package/src/shared/lib/fuzzy.js +84 -0
  395. package/src/shared/lib/git-state-words.js +80 -0
  396. package/src/shared/lib/grep-entry.js +86 -0
  397. package/src/shared/lib/grounding-line.js +61 -0
  398. package/src/shared/lib/import-line.js +71 -0
  399. package/src/shared/lib/item-status.js +249 -0
  400. package/src/shared/lib/line-changes.js +53 -0
  401. package/src/shared/lib/locator.js +71 -0
  402. package/src/shared/lib/matcher.js +120 -0
  403. package/src/shared/lib/merge-order.js +65 -0
  404. package/src/shared/lib/open-findings.js +68 -0
  405. package/src/shared/lib/other-work.js +108 -0
  406. package/src/shared/lib/package-entry.js +142 -0
  407. package/src/shared/lib/plan-vocab.js +40 -0
  408. package/src/shared/lib/pptx-xml.js +86 -0
  409. package/src/shared/lib/proposal-words.js +61 -0
  410. package/src/shared/lib/recurrence.js +183 -0
  411. package/src/shared/lib/references.js +72 -0
  412. package/src/shared/lib/rule-pattern.js +56 -0
  413. package/src/shared/lib/sdp-fingerprint.js +115 -0
  414. package/src/shared/lib/sdp-minimal.js +123 -0
  415. package/src/shared/lib/secret-paths.js +34 -0
  416. package/src/shared/lib/signal-words.js +172 -0
  417. package/src/shared/lib/signoff.js +56 -0
  418. package/src/shared/lib/skills-note.js +60 -0
  419. package/src/shared/lib/spec-sections.js +78 -0
  420. package/src/shared/lib/symbol-entry.js +60 -0
  421. package/src/shared/lib/tool-phrasing.js +338 -0
  422. package/src/shared/lib/turn-gap.js +27 -0
  423. package/src/shared/lib/workstream-words.js +38 -0
  424. package/src/shared/lib/xlsx-xml.js +144 -0
  425. package/src/shared/lib/zip-reader.js +99 -0
  426. package/src/shared/types/agent.js +27 -0
  427. package/src/shared/types/architecture-rules.js +30 -0
  428. package/src/shared/types/ast.js +15 -0
  429. package/src/shared/types/breakpoint.js +33 -0
  430. package/src/shared/types/channel.js +41 -0
  431. package/src/shared/types/criteria.js +15 -0
  432. package/src/shared/types/graph.js +15 -0
  433. package/src/shared/types/index.js +53 -0
  434. package/src/shared/types/peer.js +36 -0
  435. package/src/shared/types/pipeline.js +15 -0
  436. package/src/shared/types/plan.js +15 -0
  437. package/src/shared/types/play-forward.js +15 -0
  438. package/src/shared/types/power.js +15 -0
  439. package/src/shared/types/presence.js +15 -0
  440. package/src/shared/types/project-config.js +35 -0
  441. package/src/shared/types/project.js +15 -0
  442. package/src/shared/types/record.js +15 -0
  443. package/src/shared/types/recurring.js +15 -0
  444. package/src/shared/types/review.js +15 -0
  445. package/src/shared/types/settings.js +114 -0
  446. package/src/shared/types/stack.js +15 -0
  447. package/src/shared/types/system-doc.js +15 -0
  448. package/src/shared/types/system.js +15 -0
@@ -0,0 +1,281 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var mobile_api_server_exports = {};
29
+ __export(mobile_api_server_exports, {
30
+ getMobileApiPort: () => getMobileApiPort,
31
+ isMobileApiRunning: () => isMobileApiRunning,
32
+ resetMobileApiRateLimit: () => resetMobileApiRateLimit,
33
+ startMobileApiServer: () => startMobileApiServer,
34
+ stopMobileApiServer: () => stopMobileApiServer
35
+ });
36
+ module.exports = __toCommonJS(mobile_api_server_exports);
37
+ var import_node_http = __toESM(require("node:http"));
38
+ var import_settings_service = require("./settings-service");
39
+ var import_peer_connection_service = require("./peer-connection-service");
40
+ const MAX_BODY_BYTES = 256 * 1024;
41
+ const SOCKET_TIMEOUT_MS = 15e3;
42
+ const RATE_WINDOW_MS = 6e4;
43
+ const RATE_MAX_REQUESTS = 60;
44
+ const RATE_MAX_SOURCES = 1024;
45
+ let server = null;
46
+ let boundPort = 0;
47
+ async function startMobileApiServer() {
48
+ if (server) {
49
+ return boundPort;
50
+ }
51
+ const settings = (0, import_settings_service.getSettings)();
52
+ const requestedPort = settings.device.mobileApiPort || 19480;
53
+ return new Promise((resolve, reject) => {
54
+ const tryPort = (candidate, attemptsLeft) => {
55
+ const srv = import_node_http.default.createServer(handleRequest);
56
+ srv.on("error", (err) => {
57
+ if (err.code === "EADDRINUSE" && attemptsLeft > 0) {
58
+ console.log(`[MobileAPI] Port ${candidate} in use, trying ${candidate + 1}\u2026`);
59
+ tryPort(candidate + 1, attemptsLeft - 1);
60
+ } else {
61
+ reject(err);
62
+ }
63
+ });
64
+ srv.setTimeout(SOCKET_TIMEOUT_MS, (socket) => socket.destroy());
65
+ srv.headersTimeout = SOCKET_TIMEOUT_MS;
66
+ srv.requestTimeout = SOCKET_TIMEOUT_MS;
67
+ srv.listen(candidate, "0.0.0.0", () => {
68
+ server = srv;
69
+ boundPort = candidate;
70
+ console.log(`[MobileAPI] Listening on 0.0.0.0:${candidate}`);
71
+ resolve(candidate);
72
+ });
73
+ };
74
+ tryPort(requestedPort, 10);
75
+ });
76
+ }
77
+ function stopMobileApiServer() {
78
+ if (server) {
79
+ try {
80
+ server.closeAllConnections?.();
81
+ server.close();
82
+ } catch {
83
+ }
84
+ server = null;
85
+ boundPort = 0;
86
+ hits.clear();
87
+ console.log("[MobileAPI] Stopped");
88
+ }
89
+ }
90
+ function getMobileApiPort() {
91
+ return boundPort;
92
+ }
93
+ function isMobileApiRunning() {
94
+ return server !== null;
95
+ }
96
+ function handleRequest(req, res) {
97
+ res.setHeader("Access-Control-Allow-Origin", "*");
98
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
99
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
100
+ if (req.method === "OPTIONS") {
101
+ res.writeHead(204);
102
+ res.end();
103
+ return;
104
+ }
105
+ if (rateLimited(req)) {
106
+ sendJson(res, 429, { error: "Too many requests" });
107
+ return;
108
+ }
109
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
110
+ if (req.method === "POST" && url.pathname === "/api/mobile/auth/challenge") {
111
+ handleChallenge(req, res);
112
+ return;
113
+ }
114
+ if (req.method === "POST" && url.pathname === "/api/mobile/reconnect") {
115
+ handleReconnect(req, res);
116
+ return;
117
+ }
118
+ if (req.method === "POST" && url.pathname === "/api/mobile/reconnect/answer") {
119
+ handleReconnectAnswer(req, res);
120
+ return;
121
+ }
122
+ if (req.method === "GET" && url.pathname === "/api/mobile/status") {
123
+ handleStatus(res);
124
+ return;
125
+ }
126
+ res.writeHead(404, { "Content-Type": "application/json" });
127
+ res.end(JSON.stringify({ error: "Not found" }));
128
+ }
129
+ function handleChallenge(req, res) {
130
+ readJsonBody(req).then((data) => {
131
+ const pairingId = typeof data.pairingId === "string" ? data.pairingId : "";
132
+ if (!pairingId) {
133
+ sendJson(res, 400, { error: "Missing pairingId" });
134
+ return;
135
+ }
136
+ sendJson(res, 200, (0, import_peer_connection_service.issueReconnectChallenge)(pairingId));
137
+ }).catch((err) => failBody(res, err));
138
+ }
139
+ function handleReconnect(req, res) {
140
+ readJsonBody(req).then(async (data) => {
141
+ const pairingId = typeof data.pairingId === "string" ? data.pairingId : "";
142
+ const nonce = typeof data.nonce === "string" ? data.nonce : "";
143
+ const mac = typeof data.mac === "string" ? data.mac : "";
144
+ const expiresAt = typeof data.expiresAt === "number" ? data.expiresAt : 0;
145
+ if (!pairingId || !nonce || !mac || !expiresAt) {
146
+ sendJson(res, 400, { error: "Missing pairingId or challenge response" });
147
+ return;
148
+ }
149
+ const result = await (0, import_peer_connection_service.startReconnection)(pairingId, { nonce, expiresAt, mac });
150
+ if (!result) {
151
+ sendJson(res, 403, { error: "Reconnection refused" });
152
+ return;
153
+ }
154
+ sendJson(res, 200, {
155
+ offer: result.offer,
156
+ ice: result.iceCandidates,
157
+ fingerprint: result.fingerprint,
158
+ pairingId: result.pairingId,
159
+ // Consumed with the answer, so a captured proof is worth one use.
160
+ answerNonce: result.answerNonce,
161
+ // Lets the phone verify it is talking to the desktop it paired with,
162
+ // without depending on a certificate that changes every restart.
163
+ offerMac: result.offerMac
164
+ });
165
+ }).catch((err) => failBody(res, err));
166
+ }
167
+ function handleReconnectAnswer(req, res) {
168
+ readJsonBody(req).then(async (data) => {
169
+ const pairingId = typeof data.pairingId === "string" ? data.pairingId : "";
170
+ const answer = typeof data.answer === "string" ? data.answer : "";
171
+ const fingerprint = typeof data.fingerprint === "string" ? data.fingerprint : "";
172
+ const ice = Array.isArray(data.ice) ? data.ice : [];
173
+ const mac = typeof data.mac === "string" ? data.mac : "";
174
+ if (!pairingId || !answer) {
175
+ sendJson(res, 400, { error: "Missing pairingId or answer" });
176
+ return;
177
+ }
178
+ const success = await (0, import_peer_connection_service.completeReconnection)(pairingId, answer, ice, fingerprint, mac);
179
+ if (!success) {
180
+ sendJson(res, 403, { error: "Reconnection refused" });
181
+ return;
182
+ }
183
+ sendJson(res, 200, { connected: true });
184
+ }).catch((err) => failBody(res, err));
185
+ }
186
+ function handleStatus(res) {
187
+ try {
188
+ const status = (0, import_peer_connection_service.getPeerManagerStatus)();
189
+ const settings = (0, import_settings_service.getSettings)();
190
+ res.writeHead(200, { "Content-Type": "application/json" });
191
+ res.end(JSON.stringify({
192
+ ok: true,
193
+ deviceName: settings.device.deviceName || require("node:os").hostname(),
194
+ port: boundPort,
195
+ ...status
196
+ }));
197
+ } catch (err) {
198
+ res.writeHead(500, { "Content-Type": "application/json" });
199
+ res.end(JSON.stringify({ error: String(err) }));
200
+ }
201
+ }
202
+ function readJsonBody(req) {
203
+ return new Promise((resolve, reject) => {
204
+ let body = "";
205
+ let size = 0;
206
+ req.on("data", (chunk) => {
207
+ size += Buffer.byteLength(chunk);
208
+ if (size > MAX_BODY_BYTES) {
209
+ reject(new BodyTooLarge());
210
+ req.destroy();
211
+ return;
212
+ }
213
+ body += chunk;
214
+ });
215
+ req.on("end", () => {
216
+ try {
217
+ const parsed = JSON.parse(body || "{}");
218
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
219
+ reject(new BadJson());
220
+ return;
221
+ }
222
+ resolve(parsed);
223
+ } catch {
224
+ reject(new BadJson());
225
+ }
226
+ });
227
+ req.on("error", reject);
228
+ });
229
+ }
230
+ class BodyTooLarge extends Error {
231
+ status = 413;
232
+ }
233
+ class BadJson extends Error {
234
+ status = 400;
235
+ }
236
+ function sendJson(res, status, payload) {
237
+ if (res.headersSent) return;
238
+ res.writeHead(status, { "Content-Type": "application/json" });
239
+ res.end(JSON.stringify(payload));
240
+ }
241
+ function failBody(res, err) {
242
+ if (err instanceof BodyTooLarge) return sendJson(res, 413, { error: "Request body too large" });
243
+ if (err instanceof BadJson) return sendJson(res, 400, { error: "Invalid JSON" });
244
+ console.warn("[MobileAPI] Request failed:", err);
245
+ sendJson(res, 500, { error: "Request failed" });
246
+ }
247
+ const hits = /* @__PURE__ */ new Map();
248
+ function rateLimited(req, now = Date.now()) {
249
+ const source = req.socket.remoteAddress ?? "unknown";
250
+ const entry = hits.get(source);
251
+ if (!entry || entry.resetAt <= now) {
252
+ if (hits.size >= RATE_MAX_SOURCES) {
253
+ for (const [key, value] of hits) if (value.resetAt <= now) hits.delete(key);
254
+ if (hits.size >= RATE_MAX_SOURCES) {
255
+ const oldest = hits.keys().next();
256
+ if (!oldest.done) hits.delete(oldest.value);
257
+ }
258
+ }
259
+ hits.set(source, { count: 1, resetAt: now + RATE_WINDOW_MS });
260
+ return false;
261
+ }
262
+ entry.count += 1;
263
+ if (entry.count > RATE_MAX_REQUESTS) {
264
+ if (entry.count === RATE_MAX_REQUESTS + 1) {
265
+ console.warn(`[MobileAPI] Rate limiting ${source} \u2014 ${entry.count} requests in the window`);
266
+ }
267
+ return true;
268
+ }
269
+ return false;
270
+ }
271
+ function resetMobileApiRateLimit() {
272
+ hits.clear();
273
+ }
274
+ // Annotate the CommonJS export names for ESM import in node:
275
+ 0 && (module.exports = {
276
+ getMobileApiPort,
277
+ isMobileApiRunning,
278
+ resetMobileApiRateLimit,
279
+ startMobileApiServer,
280
+ stopMobileApiServer
281
+ });
@@ -0,0 +1,306 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var mobile_approvals_exports = {};
29
+ __export(mobile_approvals_exports, {
30
+ APPROVAL_METHODS: () => APPROVAL_METHODS,
31
+ PREVIEW_CHUNK_CHARS: () => PREVIEW_CHUNK_CHARS,
32
+ PREVIEW_IMAGE_BYTES: () => PREVIEW_IMAGE_BYTES,
33
+ PREVIEW_TEXT_CHARS: () => PREVIEW_TEXT_CHARS,
34
+ SHEET_CONTEXT: () => SHEET_CONTEXT,
35
+ buildPreview: () => buildPreview,
36
+ handleApprovalMethod: () => handleApprovalMethod,
37
+ sanitiseLocator: () => sanitiseLocator,
38
+ setPreviewImageScaler: () => setPreviewImageScaler,
39
+ toPhone: () => toPhone
40
+ });
41
+ module.exports = __toCommonJS(mobile_approvals_exports);
42
+ var import_node_path = __toESM(require("node:path"));
43
+ var criteriaService = __toESM(require("./criteria-service"));
44
+ var import_artefact_service = require("./artefact-service");
45
+ var import_plan_item_service = require("./plan-item-service");
46
+ var import_task_grounding = require("./task-grounding");
47
+ var import_human_decision = require("./human-decision");
48
+ var import_settings_service = require("./settings-service");
49
+ var import_paired_device_service = require("./paired-device-service");
50
+ var import_peer_capabilities = require("./peer-capabilities");
51
+ var import_peer_audit_service = require("./peer-audit-service");
52
+ var import_sensor_bridge_service = require("./sensor-bridge-service");
53
+ var import_reader_host = require("./material-reader/reader-host");
54
+ var import_locator = require("../../shared/lib/locator");
55
+ var import_xlsx_xml = require("../../shared/lib/xlsx-xml");
56
+ const PREVIEW_TEXT_CHARS = 4e4;
57
+ const PREVIEW_IMAGE_BYTES = 3 * 1024 * 1024;
58
+ const PREVIEW_CHUNK_CHARS = 16e3;
59
+ const SHEET_CONTEXT = { rows: 6, cols: 3 };
60
+ const TRANSFER_ID = /^[A-Za-z0-9-]{8,64}$/;
61
+ let scaler = null;
62
+ function setPreviewImageScaler(fn) {
63
+ scaler = fn;
64
+ }
65
+ const APPROVAL_METHODS = ["criteria.awaiting", "criteria.list", "criterion.decide", "artefact.preview"];
66
+ async function handleApprovalMethod(method, params, peer) {
67
+ switch (method) {
68
+ case "criteria.awaiting":
69
+ return awaiting(params);
70
+ case "criteria.list":
71
+ return criteriaForItem(requireUid(params, "itemUid"));
72
+ case "criterion.decide":
73
+ return decide(requireUid(params, "criterionUid"), params, peer);
74
+ case "artefact.preview":
75
+ return preview(params, peer);
76
+ default:
77
+ throw new Error(`Unknown approval method: ${method}`);
78
+ }
79
+ }
80
+ function awaiting(params) {
81
+ const limit = typeof params.limit === "number" && params.limit > 0 ? Math.min(params.limit, 200) : 100;
82
+ return {
83
+ entries: criteriaService.listAwaitingPerson(limit).map((a) => ({
84
+ ...toPhone(a.criterion),
85
+ itemTitle: a.itemTitle,
86
+ planUid: a.planUid,
87
+ planTitle: a.planTitle
88
+ }))
89
+ };
90
+ }
91
+ async function criteriaForItem(itemUid) {
92
+ const item = (0, import_plan_item_service.getItem)(itemUid);
93
+ if (!item) throw new Error("Item not found");
94
+ await (0, import_artefact_service.refreshArtefactHashes)(itemUid).catch(() => []);
95
+ const g = await (0, import_task_grounding.taskGrounding)(itemUid);
96
+ return {
97
+ item: { uid: item.uid, title: item.title, planUid: item.planUid },
98
+ criteria: criteriaService.listCriteria(itemUid).map(toPhone),
99
+ grounding: g && g.total ? { words: g.words, grounded: g.grounded, grades: Object.fromEntries(g.criteria.map((c) => [c.uid, { grade: c.grade, why: c.why }])) } : null
100
+ };
101
+ }
102
+ async function decide(criterionUid, params, peer) {
103
+ const device = (0, import_paired_device_service.getPairedDevice)(peer.fingerprint);
104
+ if (!device?.confirmedAt) {
105
+ throw new import_peer_capabilities.PeerAuthorizationError("Approving needs a pairing confirmed on the desktop");
106
+ }
107
+ const before = criteriaService.getCriterion(criterionUid);
108
+ if (!before) throw new criteriaService.CriterionError("Criterion not found", 404);
109
+ await (0, import_artefact_service.refreshArtefactHashes)(before.itemUid).catch(() => []);
110
+ const criterion = criteriaService.decideCriterion(
111
+ criterionUid,
112
+ { decision: params.decision, note: params.note, anchor: params.anchor },
113
+ (0, import_human_decision.issueHumanDecision)("phone", (0, import_settings_service.getAuthorKey)("human"), device.alias)
114
+ );
115
+ (0, import_peer_audit_service.recordPeerAudit)({
116
+ kind: "decision",
117
+ fingerprint: peer.fingerprint,
118
+ alias: device.alias,
119
+ method: "criterion.decide",
120
+ detail: `${params.decision === "approved" ? "approved" : "sent back"} criterion ${criterionUid}`
121
+ });
122
+ const planUid = (0, import_plan_item_service.getItem)(criterion.itemUid)?.planUid ?? null;
123
+ if (planUid) (0, import_sensor_bridge_service.resolveCriterionNotices)(planUid, criterionUid);
124
+ peer.broadcast?.("plan-item-criteria-changed", { planUid, itemUid: criterion.itemUid });
125
+ return { criterion: toPhone(criterion) };
126
+ }
127
+ async function preview(params, peer) {
128
+ const transferId = params.transferId;
129
+ if (typeof transferId !== "string" || !TRANSFER_ID.test(transferId)) {
130
+ throw new Error("transferId must be 8\u201364 letters, digits or dashes");
131
+ }
132
+ const attachmentUid = requireUid(params, "attachmentUid");
133
+ const artefact = (0, import_artefact_service.getArtefact)(attachmentUid);
134
+ if (!artefact) throw new Error("No evidence file with that uid");
135
+ const built = await buildPreview(attachmentUid, sanitiseLocator(params.locator));
136
+ const body = JSON.stringify(built);
137
+ const total = Math.max(1, Math.ceil(body.length / PREVIEW_CHUNK_CHARS));
138
+ for (let seq = 0; seq < total; seq++) {
139
+ peer.send({
140
+ mcp: true,
141
+ cmd: "preview.chunk",
142
+ id: transferId,
143
+ seq,
144
+ total,
145
+ data: body.slice(seq * PREVIEW_CHUNK_CHARS, (seq + 1) * PREVIEW_CHUNK_CHARS)
146
+ });
147
+ }
148
+ return { transferId, total, chars: body.length, kind: built.kind };
149
+ }
150
+ async function buildPreview(attachmentUid, locator, read = import_reader_host.readMaterial) {
151
+ const artefact = (0, import_artefact_service.getArtefact)(attachmentUid);
152
+ const ext = artefact ? import_node_path.default.extname(artefact.path).slice(1).toLowerCase() : "";
153
+ const isImage = ["png", "jpg", "jpeg", "gif", "webp"].includes(ext);
154
+ let readAt = isImage ? null : locator;
155
+ let grid;
156
+ if ((ext === "xlsx" || ext === "xlsm") && locator) {
157
+ const cited = locator.range ? (0, import_locator.parseRange)(String(locator.range)) : null;
158
+ if (cited) {
159
+ const firstRow = Math.max(1, cited.from.row - SHEET_CONTEXT.rows);
160
+ const firstCol = Math.max(1, cited.from.col - SHEET_CONTEXT.cols);
161
+ const lastRow = cited.to.row + SHEET_CONTEXT.rows;
162
+ const lastCol = cited.to.col + SHEET_CONTEXT.cols;
163
+ readAt = { ...locator, range: `${(0, import_xlsx_xml.colLetters)(firstCol)}${firstRow}:${(0, import_xlsx_xml.colLetters)(lastCol)}${lastRow}` };
164
+ grid = { firstRow, firstCol, cited };
165
+ } else if (!locator.text) {
166
+ grid = { firstRow: 1, firstCol: 1, cited: null };
167
+ }
168
+ }
169
+ const result = await read(attachmentUid, readAt);
170
+ if (!result.ok) return { kind: "unavailable", name: result.name ?? null, reason: result.reason };
171
+ if (result.kind === "image") {
172
+ const original = Buffer.from(result.base64, "base64");
173
+ const scaled = scaler ? safeScale(original, result.mimeType) : null;
174
+ if (scaled) return { kind: "image", name: result.name, mime: scaled.mime, base64: scaled.bytes.toString("base64"), scaled: true };
175
+ if (original.length > PREVIEW_IMAGE_BYTES) {
176
+ return {
177
+ kind: "unavailable",
178
+ name: result.name,
179
+ reason: `${result.name} is larger than the ${PREVIEW_IMAGE_BYTES / 1024 / 1024} MB the phone is sent \u2014 open it on the desktop`
180
+ };
181
+ }
182
+ return { kind: "image", name: result.name, mime: result.mimeType, base64: result.base64, scaled: false };
183
+ }
184
+ const { reply } = result;
185
+ const notes = [...reply.notes];
186
+ let budget = PREVIEW_TEXT_CHARS;
187
+ const sections = [];
188
+ for (const s of reply.sections) {
189
+ if (budget <= 0) break;
190
+ if (s.body.length > budget) {
191
+ sections.push({ heading: s.heading, body: s.body.slice(0, budget) });
192
+ notes.push("Cut short for the phone \u2014 the whole of it is on the desktop.");
193
+ budget = 0;
194
+ break;
195
+ }
196
+ sections.push(s);
197
+ budget -= s.body.length;
198
+ }
199
+ if (sections.length < reply.sections.length && !notes.some((n) => n.startsWith("Cut short"))) {
200
+ notes.push("Cut short for the phone \u2014 the whole of it is on the desktop.");
201
+ }
202
+ if (grid) {
203
+ return {
204
+ kind: "text",
205
+ name: result.name,
206
+ format: reply.format,
207
+ // The place that was cited, not the wider area read around it.
208
+ where: (0, import_locator.describeLocator)(locator) || reply.where,
209
+ outline: reply.outline,
210
+ sections: sections.map((sec) => ({ ...sec, heading: sec.heading.replace(/,\s*[A-Z]+\d+:[A-Z]+\d+$/, "").replace(/ from A1$/, "") })),
211
+ notes,
212
+ grid
213
+ };
214
+ }
215
+ return {
216
+ kind: "text",
217
+ name: result.name,
218
+ format: reply.format,
219
+ where: reply.where,
220
+ outline: reply.outline,
221
+ sections,
222
+ notes
223
+ };
224
+ }
225
+ function safeScale(bytes, mime) {
226
+ try {
227
+ return scaler?.(bytes, mime) ?? null;
228
+ } catch (err) {
229
+ console.warn("[Approvals] Could not scale an image for the phone:", err);
230
+ return null;
231
+ }
232
+ }
233
+ function requireUid(params, key) {
234
+ const v = params[key];
235
+ if (typeof v !== "string" || !/^[A-Za-z0-9-]{8,64}$/.test(v)) throw new Error(`${key} is required`);
236
+ return v;
237
+ }
238
+ function sanitiseLocator(raw) {
239
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
240
+ const l = raw;
241
+ const out = {};
242
+ const str = (v) => typeof v === "string" && v.length > 0 && v.length <= 200 ? v : void 0;
243
+ const span = (v) => {
244
+ if (typeof v === "number" && Number.isFinite(v)) return v;
245
+ if (Array.isArray(v) && v.length === 2 && v.every((n) => typeof n === "number")) return `${v[0]}-${v[1]}`;
246
+ return str(v);
247
+ };
248
+ out.sheet = str(l.sheet);
249
+ out.range = str(l.range);
250
+ out.page = span(l.page);
251
+ out.lines = span(l.lines);
252
+ out.text = str(l.text);
253
+ for (const k of Object.keys(out)) if (out[k] === void 0) delete out[k];
254
+ return Object.keys(out).length > 0 ? out : null;
255
+ }
256
+ function toEvidence(e) {
257
+ const artefact = e.attachmentUid ? (0, import_artefact_service.getArtefact)(e.attachmentUid) : null;
258
+ const name = artefact ? import_node_path.default.basename(artefact.path) : null;
259
+ return {
260
+ attachmentUid: e.attachmentUid,
261
+ name,
262
+ ext: name ? import_node_path.default.extname(name).slice(1).toLowerCase() || null : null,
263
+ locator: e.locator,
264
+ where: (0, import_locator.describeLocator)(e.locator),
265
+ note: e.note,
266
+ submittedBy: e.submittedBy,
267
+ submittedAt: e.submittedAt
268
+ };
269
+ }
270
+ function toPhone(c) {
271
+ const signoff = c.latestSignoff;
272
+ let changedFiles = [];
273
+ if (c.state === "stale" && signoff?.evidenceHashes) {
274
+ const recorded = signoff.evidenceHashes;
275
+ changedFiles = Object.keys(recorded).filter((uid) => ((0, import_artefact_service.getArtefact)(uid)?.sha256 ?? null) !== recorded[uid]).map((uid) => (0, import_artefact_service.getArtefact)(uid)?.path ?? uid);
276
+ }
277
+ return {
278
+ uid: c.uid,
279
+ itemUid: c.itemUid,
280
+ text: c.text,
281
+ kind: c.kind,
282
+ policy: c.policy,
283
+ state: c.state,
284
+ evidence: c.latestSubmission.map(toEvidence),
285
+ lastDecision: signoff ? { decision: signoff.decision, actor: signoff.actor, channel: signoff.channel, note: signoff.note, at: signoff.createdAt } : null,
286
+ changedFiles,
287
+ // The desktop's rule, the same here (CriteriaBlock): approve anything
288
+ // not already met; send back what was submitted, or take back an
289
+ // approval.
290
+ canApprove: c.state !== "met",
291
+ canSendBack: c.state === "submitted" || c.state === "met"
292
+ };
293
+ }
294
+ // Annotate the CommonJS export names for ESM import in node:
295
+ 0 && (module.exports = {
296
+ APPROVAL_METHODS,
297
+ PREVIEW_CHUNK_CHARS,
298
+ PREVIEW_IMAGE_BYTES,
299
+ PREVIEW_TEXT_CHARS,
300
+ SHEET_CONTEXT,
301
+ buildPreview,
302
+ handleApprovalMethod,
303
+ sanitiseLocator,
304
+ setPreviewImageScaler,
305
+ toPhone
306
+ });