@atmem/architect 0.0.0-stage → 0.2.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 (512) hide show
  1. package/CONSTITUTION.md +144 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +6 -0
  4. package/README.md +31 -2
  5. package/agent-skills/discovery/SKILL.md +48 -0
  6. package/agent-skills/discovery/topics.json +139 -0
  7. package/agent-skills/generation-capabilities.json +760 -0
  8. package/agent-skills/service-lifecycle.json +21 -0
  9. package/agent-skills/specialists/architecture/SKILL.md +15 -0
  10. package/agent-skills/specialists/aws/SKILL.md +17 -0
  11. package/agent-skills/specialists/azure/SKILL.md +7 -0
  12. package/agent-skills/specialists/composition/SKILL.md +17 -0
  13. package/agent-skills/specialists/devops/SKILL.md +10 -0
  14. package/agent-skills/specialists/finops/SKILL.md +7 -0
  15. package/agent-skills/specialists/gcp/SKILL.md +7 -0
  16. package/agent-skills/specialists/ibm/SKILL.md +7 -0
  17. package/agent-skills/specialists/manifest.json +13 -0
  18. package/agent-skills/specialists/pulumi/SKILL.md +7 -0
  19. package/agent-skills/specialists/security/SKILL.md +7 -0
  20. package/agent-skills/specialists/terraform/SKILL.md +14 -0
  21. package/agent-skills/upstream/aws/LICENSE +175 -0
  22. package/agent-skills/upstream/aws/SKILL.md +125 -0
  23. package/agent-skills/upstream/azure/LICENSE +21 -0
  24. package/agent-skills/upstream/azure/SKILL.md +387 -0
  25. package/agent-skills/upstream/gcp/LICENSE +202 -0
  26. package/agent-skills/upstream/gcp/SKILL.md +159 -0
  27. package/agent-skills/upstream/manifest.json +57 -0
  28. package/agent-skills/upstream/pulumi/LICENSE +201 -0
  29. package/agent-skills/upstream/pulumi/SKILL.md +501 -0
  30. package/agent-skills/upstream/terraform/LICENSE +373 -0
  31. package/agent-skills/upstream/terraform/SKILL.md +316 -0
  32. package/agent-skills/workflows/workflows.json +601 -0
  33. package/cli/architect.mjs +3 -0
  34. package/cli/runtime.mjs +115 -0
  35. package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
  36. package/dist/assets/index-BTLHaa3J.css +1 -0
  37. package/dist/assets/index-CQ2C1gKF.js +21338 -0
  38. package/dist/assets/index.es-DTyqIdaO.js +18 -0
  39. package/dist/assets/purify.es-CAi2OJFm.js +3 -0
  40. package/dist/assets/visual-export-Ox0j5xj5.js +173 -0
  41. package/dist/favicon.svg +2 -0
  42. package/dist/fonts/DM-Sans-OFL.txt +93 -0
  43. package/dist/fonts/Manrope-OFL.txt +93 -0
  44. package/dist/fonts/NotoSans-Regular.ttf +0 -0
  45. package/dist/fonts/OFL.txt +94 -0
  46. package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
  47. package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
  48. package/dist/index.html +4 -0
  49. package/dist/licenses/atmem-APACHE-2.0.txt +202 -0
  50. package/dist/licenses/aws-architect-LICENSE.txt +17 -0
  51. package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
  52. package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
  53. package/dist/licenses/cloud-emulation-notices.txt +36619 -0
  54. package/dist/licenses/dependency-notices.txt +6379 -0
  55. package/dist/licenses/emulation-notices.txt +717 -0
  56. package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  57. package/dist/licenses/memory-dependency-notices.txt +3319 -0
  58. package/dist/licenses/openarchflow-LICENSE.txt +21 -0
  59. package/dist/licenses/python-dependency-notices.txt +11951 -0
  60. package/docs/upstream-lock.json +26 -0
  61. package/knowledge/engineering-practices.json +536 -0
  62. package/package.json +30 -4
  63. package/packaging/uv-release-lock.json +54 -0
  64. package/public/licenses/atmem-APACHE-2.0.txt +202 -0
  65. package/public/licenses/aws-architect-LICENSE.txt +17 -0
  66. package/public/licenses/aws-mcp-LICENSE.txt +175 -0
  67. package/public/licenses/aws-mcp-NOTICE.txt +1 -0
  68. package/public/licenses/cloud-emulation-notices.txt +36619 -0
  69. package/public/licenses/dependency-notices.txt +6379 -0
  70. package/public/licenses/emulation-notices.txt +717 -0
  71. package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  72. package/public/licenses/memory-dependency-notices.txt +3319 -0
  73. package/public/licenses/openarchflow-LICENSE.txt +21 -0
  74. package/public/licenses/python-dependency-notices.txt +11951 -0
  75. package/scripts/terraform-render.mjs +8 -0
  76. package/server/__init__.py +0 -0
  77. package/server/abstraction.py +28 -0
  78. package/server/agent_runtime.py +153 -0
  79. package/server/app.py +257 -0
  80. package/server/architect.py +112 -0
  81. package/server/architecture_activity.py +67 -0
  82. package/server/attachments.py +101 -0
  83. package/server/cloud-emulation-requirements.txt +29 -0
  84. package/server/codex_app_server.py +219 -0
  85. package/server/codex_engineering_backend.py +266 -0
  86. package/server/codex_worker_policy.py +193 -0
  87. package/server/contracts.py +315 -0
  88. package/server/copilot_worker.py +16 -0
  89. package/server/dependency_mirror.py +54 -0
  90. package/server/design_patch.py +43 -0
  91. package/server/emulation-requirements.txt +8 -0
  92. package/server/emulation_adapters.py +20 -0
  93. package/server/emulation_routes.py +98 -0
  94. package/server/emulation_worker.py +351 -0
  95. package/server/engineering_artifacts.py +221 -0
  96. package/server/engineering_assets/application-runtime.json +4372 -0
  97. package/server/engineering_assets/architecture_work.schema.json +79 -0
  98. package/server/engineering_assets/materialize_runtime.py +62 -0
  99. package/server/engineering_assets/prepare_tools.py +96 -0
  100. package/server/engineering_assets/sql_validation.py +59 -0
  101. package/server/engineering_contexts.py +120 -0
  102. package/server/engineering_delivery.py +210 -0
  103. package/server/engineering_finalization.py +210 -0
  104. package/server/engineering_handoff.py +172 -0
  105. package/server/engineering_jobs.py +206 -0
  106. package/server/engineering_knowledge.py +75 -0
  107. package/server/engineering_reconciliation.py +159 -0
  108. package/server/engineering_routes.py +483 -0
  109. package/server/engineering_sessions.py +273 -0
  110. package/server/engineering_tools.py +166 -0
  111. package/server/generation_lint.py +18 -0
  112. package/server/local_providers.py +185 -0
  113. package/server/local_validation.py +236 -0
  114. package/server/memory-requirements.lock +181 -0
  115. package/server/memory-requirements.txt +6 -0
  116. package/server/memory_context.py +58 -0
  117. package/server/memory_routes.py +141 -0
  118. package/server/memory_service.py +306 -0
  119. package/server/memory_worker.py +129 -0
  120. package/server/openai_voice.py +80 -0
  121. package/server/orca_handoff.py +90 -0
  122. package/server/packaged_app.py +9 -0
  123. package/server/pricing.py +190 -0
  124. package/server/process_control_observation.py +31 -0
  125. package/server/prompts.py +136 -0
  126. package/server/public_faq_emulation_worker.py +44 -0
  127. package/server/requirements.lock +1618 -0
  128. package/server/requirements.txt +68 -0
  129. package/server/runtime_dependency_locks.py +27 -0
  130. package/server/runtime_evidence.py +93 -0
  131. package/server/settings.py +14 -0
  132. package/server/skills.py +103 -0
  133. package/server/storage_emulation_worker.py +160 -0
  134. package/server/structured_emulation_worker.py +48 -0
  135. package/server/terraform_routes.py +118 -0
  136. package/server/upstream/__init__.py +0 -0
  137. package/server/upstream/agent/__init__.py +1 -0
  138. package/server/upstream/agent/aws_architect.py +600 -0
  139. package/server/upstream/agent/config/__init__.py +1 -0
  140. package/server/upstream/agent/config/agent_config.py +82 -0
  141. package/server/upstream/agent/config/prompts.py +138 -0
  142. package/server/upstream/agent/rate_limiter.py +62 -0
  143. package/server/upstream/utils/__init__.py +1 -0
  144. package/server/upstream/utils/file_writer.py +43 -0
  145. package/server/validation_applicability.py +85 -0
  146. package/server/validation_jobs.py +252 -0
  147. package/server/validation_workers.py +48 -0
  148. package/server/voice_routes.py +91 -0
  149. package/server/voice_worker.py +26 -0
  150. package/server/windows_snapshot.py +119 -0
  151. package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
  152. package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
  153. package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
  154. package/src/ArchitectureActivity.jsx +54 -0
  155. package/src/ArchitectureWorkPanel.jsx +39 -0
  156. package/src/ArtifactChecks.jsx +49 -0
  157. package/src/BlockRequirements.jsx +13 -0
  158. package/src/BrainActivity.jsx +11 -0
  159. package/src/ChatMessage.jsx +29 -0
  160. package/src/ChatResizeHandle.jsx +6 -0
  161. package/src/CodePreview.jsx +8 -0
  162. package/src/ComponentReview.jsx +31 -0
  163. package/src/DeliveryViews.jsx +59 -0
  164. package/src/EmulationPanel.jsx +44 -0
  165. package/src/EngagementViews.jsx +387 -0
  166. package/src/EngineeringPanel.jsx +151 -0
  167. package/src/FileTree.jsx +7 -0
  168. package/src/FinalChecklist.jsx +15 -0
  169. package/src/HelpNote.jsx +3 -0
  170. package/src/ImplementationDelivery.jsx +91 -0
  171. package/src/JourneyViews.jsx +403 -0
  172. package/src/LocationChoices.jsx +12 -0
  173. package/src/MemoryPanel.jsx +58 -0
  174. package/src/OpenAIVoicePanel.jsx +66 -0
  175. package/src/OutputsView.jsx +71 -0
  176. package/src/ProjectChecks.jsx +44 -0
  177. package/src/TerraformView.jsx +57 -0
  178. package/src/WorkbenchDialogs.jsx +532 -0
  179. package/src/activity-presentation.mjs +28 -0
  180. package/src/ai-qualification-scenarios.mjs +31 -0
  181. package/src/approval-application-files.mjs +32 -0
  182. package/src/approval-applications.mjs +82 -0
  183. package/src/approval-spec-kit.mjs +30 -0
  184. package/src/architecture-activity.css +28 -0
  185. package/src/architecture-activity.mjs +63 -0
  186. package/src/architecture-work.mjs +176 -0
  187. package/src/artifact-work.mjs +103 -0
  188. package/src/async-documents.mjs +191 -0
  189. package/src/atflow.css +1140 -0
  190. package/src/atforma.css +748 -0
  191. package/src/atmem-tokens.css +79 -0
  192. package/src/aws-preflight.mjs +60 -0
  193. package/src/aws-public-faq.mjs +102 -0
  194. package/src/aws-validation-scenarios.mjs +10 -0
  195. package/src/aws-validation.mjs +48 -0
  196. package/src/azure-plan-mocks.mjs +25 -0
  197. package/src/azure-structured.mjs +133 -0
  198. package/src/bedrock-summary.mjs +152 -0
  199. package/src/bounded-rag.mjs +60 -0
  200. package/src/brand.css +9 -0
  201. package/src/canvas-view.mjs +28 -0
  202. package/src/capability-composition.mjs +44 -0
  203. package/src/chat-attachments.mjs +13 -0
  204. package/src/cloud-provider-locks.mjs +9 -0
  205. package/src/cloud-storage-scenarios.mjs +2 -0
  206. package/src/component-review.mjs +55 -0
  207. package/src/composition-spec-kit.mjs +30 -0
  208. package/src/consultation-context.mjs +15 -0
  209. package/src/consultation.mjs +52 -0
  210. package/src/container-contracts.mjs +5 -0
  211. package/src/container-inspection.mjs +75 -0
  212. package/src/container-runtime.mjs +30 -0
  213. package/src/container-security-inspection.mjs +49 -0
  214. package/src/container-terraform.mjs +109 -0
  215. package/src/conversation.mjs +18 -0
  216. package/src/core.mjs +863 -0
  217. package/src/cross-cloud-storage.mjs +100 -0
  218. package/src/delivery-capabilities.mjs +25 -0
  219. package/src/delivery.mjs +58 -0
  220. package/src/deployment-ownership.mjs +10 -0
  221. package/src/deployment-region.mjs +11 -0
  222. package/src/derive.mjs +31 -0
  223. package/src/design-build-flow.mjs +10 -0
  224. package/src/diagram-attention.mjs +24 -0
  225. package/src/discovery.mjs +359 -0
  226. package/src/document.mjs +170 -0
  227. package/src/emulation-feedback.mjs +61 -0
  228. package/src/emulation-guidance.mjs +37 -0
  229. package/src/engagement.mjs +274 -0
  230. package/src/engineering-client.mjs +73 -0
  231. package/src/engineering-defaults.mjs +17 -0
  232. package/src/engineering.css +31 -0
  233. package/src/enterprise-application.mjs +76 -0
  234. package/src/enterprise-aws-host-infrastructure.mjs +62 -0
  235. package/src/enterprise-aws-host-tests.mjs +15 -0
  236. package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
  237. package/src/enterprise-aws-infrastructure.mjs +138 -0
  238. package/src/enterprise-google-infrastructure.mjs +84 -0
  239. package/src/enterprise-google-tests.mjs +25 -0
  240. package/src/estimate.mjs +190 -0
  241. package/src/existing-references.mjs +18 -0
  242. package/src/external-identities.mjs +17 -0
  243. package/src/faq-lambda-build.mjs +28 -0
  244. package/src/final-package.mjs +34 -0
  245. package/src/gcp-structured.mjs +104 -0
  246. package/src/generation-provider.mjs +7 -0
  247. package/src/generation-quality.mjs +69 -0
  248. package/src/handoff.mjs +141 -0
  249. package/src/ibm-registry.mjs +12 -0
  250. package/src/ibm-structured.mjs +103 -0
  251. package/src/identifiers.mjs +15 -0
  252. package/src/implementation-map.mjs +14 -0
  253. package/src/implementation-outcome.mjs +8 -0
  254. package/src/incomplete-terraform.mjs +9 -0
  255. package/src/integrations.ts +137 -0
  256. package/src/interface-contracts.mjs +30 -0
  257. package/src/invoice-application.mjs +66 -0
  258. package/src/invoice-aws-operations.mjs +136 -0
  259. package/src/invoice-aws.mjs +143 -0
  260. package/src/invoice-azure-operations.mjs +147 -0
  261. package/src/invoice-azure.mjs +134 -0
  262. package/src/invoice-builder.mjs +35 -0
  263. package/src/invoice-dependencies.mjs +43 -0
  264. package/src/invoice-gcp-operations.mjs +178 -0
  265. package/src/invoice-gcp.mjs +104 -0
  266. package/src/invoice-ibm-operations.mjs +278 -0
  267. package/src/invoice-ibm.mjs +142 -0
  268. package/src/invoice-operations.mjs +143 -0
  269. package/src/invoice-plan.mjs +45 -0
  270. package/src/invoice-region.mjs +6 -0
  271. package/src/invoice-spec-kit.mjs +61 -0
  272. package/src/invoice-state.mjs +7 -0
  273. package/src/invoice-terraform.mjs +29 -0
  274. package/src/invoice-tests.mjs +122 -0
  275. package/src/journey-workspace.css +106 -0
  276. package/src/journey.mjs +97 -0
  277. package/src/levels.mjs +145 -0
  278. package/src/local-agent.mjs +184 -0
  279. package/src/main.jsx +2481 -0
  280. package/src/model-policy-preflight.mjs +34 -0
  281. package/src/openai-voice.mjs +102 -0
  282. package/src/output-bundle.mjs +13 -0
  283. package/src/ownership-inspection.mjs +28 -0
  284. package/src/pack.mjs +201 -0
  285. package/src/pricing.mjs +85 -0
  286. package/src/production-state.mjs +8 -0
  287. package/src/project-copy.mjs +10 -0
  288. package/src/project-file.mjs +160 -0
  289. package/src/project-links.mjs +3 -0
  290. package/src/protected-state.mjs +43 -0
  291. package/src/public-faq-builder.mjs +19 -0
  292. package/src/public-faq-clouds.mjs +251 -0
  293. package/src/public-faq.mjs +122 -0
  294. package/src/pulumi.mjs +123 -0
  295. package/src/python-container.mjs +25 -0
  296. package/src/python-runtime.mjs +50 -0
  297. package/src/rag-handler.mjs +43 -0
  298. package/src/rag-web-assets.mjs +52 -0
  299. package/src/rag-web.mjs +68 -0
  300. package/src/records.mjs +177 -0
  301. package/src/resources.mjs +163 -0
  302. package/src/review.mjs +252 -0
  303. package/src/runtime/EnterpriseDocument.java +89 -0
  304. package/src/runtime/approved_http_destination.py +72 -0
  305. package/src/runtime/aws-lambda-qualification-lock.json +7 -0
  306. package/src/runtime/aws-python-lock.json +82 -0
  307. package/src/runtime/aws-web-python-lock.json +887 -0
  308. package/src/runtime/aws_json_model.py +42 -0
  309. package/src/runtime/azure-python-lock.json +863 -0
  310. package/src/runtime/azure-sources.json +8 -0
  311. package/src/runtime/azure-test-lock.json +23 -0
  312. package/src/runtime/azure_acceptance.py +192 -0
  313. package/src/runtime/azure_structured.py +153 -0
  314. package/src/runtime/bounded_rag.py +126 -0
  315. package/src/runtime/catalog_admin.py +70 -0
  316. package/src/runtime/cloud-sql-proxy-lock.json +8 -0
  317. package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
  318. package/src/runtime/connector_cli.py +50 -0
  319. package/src/runtime/connector_sync.py +70 -0
  320. package/src/runtime/durable-python-lock.json +247 -0
  321. package/src/runtime/durable_workflow.py +325 -0
  322. package/src/runtime/enterprise-application-manifest.json +110 -0
  323. package/src/runtime/enterprise-application-sources.json +266 -0
  324. package/src/runtime/enterprise-auth-python-lock.json +592 -0
  325. package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
  326. package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
  327. package/src/runtime/enterprise-aws-task-template.json +235 -0
  328. package/src/runtime/enterprise-document-lock.json +18 -0
  329. package/src/runtime/enterprise-document.Dockerfile +15 -0
  330. package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
  331. package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
  332. package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
  333. package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
  334. package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
  335. package/src/runtime/enterprise-runtime.Dockerfile +15 -0
  336. package/src/runtime/enterprise-web-app.js +1 -0
  337. package/src/runtime/enterprise-web-entry.js +105 -0
  338. package/src/runtime/enterprise-web-lock.json +29 -0
  339. package/src/runtime/enterprise-web-notices.txt +229 -0
  340. package/src/runtime/enterprise-web.css +4 -0
  341. package/src/runtime/enterprise-web.html +8 -0
  342. package/src/runtime/enterprise_audit.py +91 -0
  343. package/src/runtime/enterprise_audit_schema.sql +25 -0
  344. package/src/runtime/enterprise_auth_schema.sql +67 -0
  345. package/src/runtime/enterprise_auth_store.py +241 -0
  346. package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
  347. package/src/runtime/enterprise_aws_host_probe.py +105 -0
  348. package/src/runtime/enterprise_aws_hosts.py +136 -0
  349. package/src/runtime/enterprise_aws_jobs.py +300 -0
  350. package/src/runtime/enterprise_aws_objects.py +93 -0
  351. package/src/runtime/enterprise_aws_transfer.py +83 -0
  352. package/src/runtime/enterprise_azure_batch_api.py +128 -0
  353. package/src/runtime/enterprise_azure_host.py +599 -0
  354. package/src/runtime/enterprise_azure_jobs.py +294 -0
  355. package/src/runtime/enterprise_azure_objects.py +234 -0
  356. package/src/runtime/enterprise_azure_pool.py +161 -0
  357. package/src/runtime/enterprise_azure_transfer.py +122 -0
  358. package/src/runtime/enterprise_configuration.py +136 -0
  359. package/src/runtime/enterprise_document.py +116 -0
  360. package/src/runtime/enterprise_document_cli.py +32 -0
  361. package/src/runtime/enterprise_document_contract.py +54 -0
  362. package/src/runtime/enterprise_document_job.py +109 -0
  363. package/src/runtime/enterprise_document_jobs.py +122 -0
  364. package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
  365. package/src/runtime/enterprise_extraction_cache.py +186 -0
  366. package/src/runtime/enterprise_extraction_schema.sql +17 -0
  367. package/src/runtime/enterprise_google_jobs.py +275 -0
  368. package/src/runtime/enterprise_google_transfer.py +85 -0
  369. package/src/runtime/enterprise_http.py +212 -0
  370. package/src/runtime/enterprise_identity.py +52 -0
  371. package/src/runtime/enterprise_knowledge.py +152 -0
  372. package/src/runtime/enterprise_models.py +94 -0
  373. package/src/runtime/enterprise_native.py +181 -0
  374. package/src/runtime/enterprise_oauth.py +132 -0
  375. package/src/runtime/enterprise_operator.py +160 -0
  376. package/src/runtime/enterprise_parser_service.py +136 -0
  377. package/src/runtime/enterprise_parser_transport.py +68 -0
  378. package/src/runtime/enterprise_queries.py +255 -0
  379. package/src/runtime/enterprise_queries_schema.sql +27 -0
  380. package/src/runtime/enterprise_query_service.py +111 -0
  381. package/src/runtime/enterprise_secrets.py +110 -0
  382. package/src/runtime/enterprise_sessions.py +95 -0
  383. package/src/runtime/enterprise_sources.py +260 -0
  384. package/src/runtime/enterprise_web.py +39 -0
  385. package/src/runtime/enterprise_worker.py +68 -0
  386. package/src/runtime/export_contract_acceptance.py +18 -0
  387. package/src/runtime/faq-output-contract.json +40 -0
  388. package/src/runtime/faq-web-app.js +41 -0
  389. package/src/runtime/faq-web.css +1 -0
  390. package/src/runtime/faq-web.html +6 -0
  391. package/src/runtime/gcp-sources.json +9 -0
  392. package/src/runtime/gcp_acceptance.py +110 -0
  393. package/src/runtime/gcp_structured.py +150 -0
  394. package/src/runtime/http_acceptance.py +39 -0
  395. package/src/runtime/iap-web-app.js +36 -0
  396. package/src/runtime/iap-web-source.json +1 -0
  397. package/src/runtime/ibm-python-lock.json +856 -0
  398. package/src/runtime/ibm-sources.json +9 -0
  399. package/src/runtime/ibm_acceptance.py +133 -0
  400. package/src/runtime/ibm_structured.py +113 -0
  401. package/src/runtime/invoice-azapi-provider-lock.json +7 -0
  402. package/src/runtime/invoice-ibm-provider-lock.json +7 -0
  403. package/src/runtime/invoice-parser-lock.json +1199 -0
  404. package/src/runtime/invoice-parser-python-lock.json +56 -0
  405. package/src/runtime/invoice-parser.Dockerfile +190 -0
  406. package/src/runtime/invoice-sources.json +58 -0
  407. package/src/runtime/invoice-test-sources.json +24 -0
  408. package/src/runtime/invoice-web.css +2 -0
  409. package/src/runtime/invoice-web.html +12 -0
  410. package/src/runtime/invoice-web.js +112 -0
  411. package/src/runtime/invoice_configuration.py +17 -0
  412. package/src/runtime/invoice_document.py +95 -0
  413. package/src/runtime/invoice_extraction.py +96 -0
  414. package/src/runtime/invoice_http.py +129 -0
  415. package/src/runtime/invoice_identity.py +5 -0
  416. package/src/runtime/invoice_maintenance.py +40 -0
  417. package/src/runtime/invoice_native.py +77 -0
  418. package/src/runtime/invoice_object_store.py +153 -0
  419. package/src/runtime/invoice_operations.py +108 -0
  420. package/src/runtime/invoice_operator.py +79 -0
  421. package/src/runtime/invoice_operator_core.py +39 -0
  422. package/src/runtime/invoice_operator_job.py +147 -0
  423. package/src/runtime/invoice_parser_cli.py +17 -0
  424. package/src/runtime/invoice_parser_identity.py +39 -0
  425. package/src/runtime/invoice_parser_service.py +89 -0
  426. package/src/runtime/invoice_parser_transport.py +89 -0
  427. package/src/runtime/invoice_processing.py +24 -0
  428. package/src/runtime/invoice_retention.py +16 -0
  429. package/src/runtime/invoice_runtime_secrets.py +43 -0
  430. package/src/runtime/invoice_stage.py +80 -0
  431. package/src/runtime/invoice_web.py +39 -0
  432. package/src/runtime/invoice_worker.py +50 -0
  433. package/src/runtime/lambda_faq.py +5 -0
  434. package/src/runtime/parser-requirements.lock +4 -0
  435. package/src/runtime/policy_review.py +18 -0
  436. package/src/runtime/postgres-qualification-lock.json +21 -0
  437. package/src/runtime/public-faq-sources.json +22 -0
  438. package/src/runtime/public_catalog_admin.py +122 -0
  439. package/src/runtime/public_catalog_store.py +125 -0
  440. package/src/runtime/public_faq.py +144 -0
  441. package/src/runtime/public_faq_app.py +49 -0
  442. package/src/runtime/python-container-lock.json +18 -0
  443. package/src/runtime/rag-sources.json +10 -0
  444. package/src/runtime/rag_acceptance.py +60 -0
  445. package/src/runtime/rag_aws_adapter.py +95 -0
  446. package/src/runtime/rds-ca-source.json +9 -0
  447. package/src/runtime/rds-global-ca.pem +2888 -0
  448. package/src/runtime/state-contract-check-source.json +4 -0
  449. package/src/runtime/state_contract_check.py +338 -0
  450. package/src/runtime/structured-source.json +1 -0
  451. package/src/runtime/structured-web-app.js +71 -0
  452. package/src/runtime/structured-web-source.json +1 -0
  453. package/src/runtime/structured_http.py +105 -0
  454. package/src/runtime/structured_output.py +48 -0
  455. package/src/runtime/web-python-lock.json +802 -0
  456. package/src/runtime/web_user_admin.py +44 -0
  457. package/src/runtime/workflow_application.py +62 -0
  458. package/src/runtime/workflow_bindings.py +128 -0
  459. package/src/runtime/workflow_configuration.py +40 -0
  460. package/src/runtime/workflow_connection.py +105 -0
  461. package/src/runtime/workflow_contract.py +54 -0
  462. package/src/runtime/workflow_database.py +94 -0
  463. package/src/runtime/workflow_errors.py +6 -0
  464. package/src/runtime/workflow_http.py +178 -0
  465. package/src/runtime/workflow_identity.py +69 -0
  466. package/src/runtime/workflow_payload.py +118 -0
  467. package/src/runtime/workflow_process_stage.py +88 -0
  468. package/src/runtime/workflow_profile.py +91 -0
  469. package/src/runtime/workflow_review.py +106 -0
  470. package/src/runtime/workflow_role_bootstrap.py +104 -0
  471. package/src/runtime/workflow_schema.sql +72 -0
  472. package/src/runtime/workflow_worker.py +142 -0
  473. package/src/runtime-settings.mjs +15 -0
  474. package/src/s3-access.mjs +14 -0
  475. package/src/source-contracts.mjs +17 -0
  476. package/src/spec-kit.mjs +186 -0
  477. package/src/state-custody.mjs +32 -0
  478. package/src/state-inspection.mjs +45 -0
  479. package/src/state-spec-kit.mjs +13 -0
  480. package/src/storage-tests.mjs +25 -0
  481. package/src/structured-api.mjs +133 -0
  482. package/src/structured-contract.mjs +19 -0
  483. package/src/structured-operation-policy.mjs +2 -0
  484. package/src/structured-web.mjs +17 -0
  485. package/src/style.css +1 -0
  486. package/src/system-description.mjs +26 -0
  487. package/src/terraform-admission.mjs +66 -0
  488. package/src/terraform-composition.mjs +83 -0
  489. package/src/terraform-expressions.mjs +46 -0
  490. package/src/terraform-validation-coverage.mjs +11 -0
  491. package/src/terraform.mjs +128 -0
  492. package/src/text-statistics.mjs +51 -0
  493. package/src/upstream/openarchflow/format-payload.ts +26 -0
  494. package/src/upstream/openarchflow/import/mermaid.ts +205 -0
  495. package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
  496. package/src/upstream/openarchflow/layout.ts +54 -0
  497. package/src/upstream/openarchflow/providers/aws.ts +196 -0
  498. package/src/upstream/openarchflow/providers/azure.ts +126 -0
  499. package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
  500. package/src/upstream/openarchflow/providers/generic.ts +227 -0
  501. package/src/upstream/openarchflow/providers/integration.ts +22 -0
  502. package/src/upstream/openarchflow/providers/types.ts +2 -0
  503. package/src/upstream/openarchflow/templates.ts +937 -0
  504. package/src/upstream-catalog.mjs +10 -0
  505. package/src/useChatScroll.jsx +37 -0
  506. package/src/useEmulation.jsx +36 -0
  507. package/src/useEngineeringStatus.mjs +17 -0
  508. package/src/validation-receipt.mjs +10 -0
  509. package/src/visual-export.mjs +92 -0
  510. package/src/voice-conversation.mjs +117 -0
  511. package/src/work-intake.mjs +24 -0
  512. package/src/workflow-runtime-inventory.mjs +3 -0
@@ -0,0 +1,403 @@
1
+ // Review, Estimate and Deploy steps of the AtMem AI Architect journey.
2
+ import React from "react";
3
+ import {Check} from "lucide-react";
4
+ import { ATTRIBUTES, acceptRisk, blockers } from "./review.mjs";
5
+ import { DISCLAIMER, INPUTS, PROJECT_SIZES, setProjectSize, setPrice, setUsage } from "./estimate.mjs";
6
+ import { deploymentSteps } from "./document.mjs";
7
+ import { GATE } from "./engagement.mjs";
8
+ import { DetailedCharges, ConsultingView, ResourcesView } from "./DeliveryViews.jsx";
9
+ import { resourcePlan } from "./resources.mjs";
10
+
11
+ const money = (v, c) => (v == null ? "—" : `${c || ""} ${v.toLocaleString("en", { maximumFractionDigits: 2 })}`.trim());
12
+
13
+ function Accept({ onAccept }) {
14
+ const [open, setOpen] = React.useState(false),
15
+ [reason, setReason] = React.useState(""),
16
+ [owner, setOwner] = React.useState("");
17
+ if (!open)
18
+ return (
19
+ <button className="link small" onClick={() => setOpen(true)}>
20
+ Accept risk
21
+ </button>
22
+ );
23
+ return (
24
+ <div className="revise">
25
+ <input aria-label="Why accept" placeholder="Why is this acceptable?" value={reason} onChange={(e) => setReason(e.target.value)} />
26
+ <input aria-label="Risk owner" placeholder="Owner" value={owner} onChange={(e) => setOwner(e.target.value)} />
27
+ <button className="btn" disabled={!reason.trim() || !owner.trim()} onClick={() => onAccept(reason, owner)}>
28
+ Accept
29
+ </button>
30
+ </div>
31
+ );
32
+ }
33
+
34
+ export function ReviewView({ project, findings, onChange, onOpen, onAddRequirement,expanded=false }) {
35
+ const ChoicesContainer=expanded?'section':'details',ChoicesHeading=expanded?'h3':'summary';
36
+ const names = Object.fromEntries(project.nodes.map((n) => [n.id, n.data.label]));
37
+ const block = blockers(findings);
38
+ const open = findings.filter((f) => f.status === "open");
39
+ return (
40
+ <section className="doc" aria-label="Review">
41
+ <header className="doc-head">
42
+ <h2>Review</h2>
43
+ <p className="muted">
44
+ {block.length
45
+ ? `${block.length} blocker${block.length > 1 ? "s" : ""} to fix before deployment.`
46
+ : open.length
47
+ ? `No blockers. ${open.length} thing${open.length > 1 ? "s" : ""} worth a look.`
48
+ : "Nothing open. Rules run on every change."}
49
+ </p>
50
+ </header>
51
+ <ChoicesContainer className="extra"><ChoicesHeading>Agreed choices and consequences</ChoicesHeading>{(project.decisions||[]).filter(d=>d.status==='Accepted').map(d=><article key={d.id||d.subject}><strong>{d.title||d.subject}</strong><p>{d.decision}</p>{d.context&&<p>{d.context}</p>}{d.consequences&&<p>{d.consequences}</p>}</article>)}</ChoicesContainer>
52
+ {ATTRIBUTES.map(([id, label]) => {
53
+ const fs = findings.filter((f) => f.attribute === id);
54
+ if (!fs.length) return null;
55
+ return (
56
+ <div key={id} className="finding-group">
57
+ <h3>{label}</h3>
58
+ {fs.map((f) => (
59
+ <article key={f.key} className={`finding sev-${f.severity} ${f.status}`}>
60
+ <div className="finding-head">
61
+ <span className={`sev ${f.severity}`}>{f.severity}</span>
62
+ <strong>{f.statement}</strong>
63
+ </div>
64
+ <p className="muted">{f.rationale}</p>
65
+ <p>{f.action}</p>
66
+ <div className="finding-foot">
67
+ {f.components.map((c) => (
68
+ <button key={c} className="chip" onClick={() => onOpen(c)}>
69
+ {names[c] || c}
70
+ </button>
71
+ ))}
72
+ <span className="muted small">{f.source}</span>
73
+ {["AVL-001", "PRF-001"].includes(f.rule) && f.status === "open" && (
74
+ <button className="link small" onClick={() => onAddRequirement(f.rule === "AVL-001" ? "availability" : "performance")}>
75
+ Add target
76
+ </button>
77
+ )}
78
+ {f.status === "accepted" ? (
79
+ <span className="small">
80
+ Accepted by {f.acceptance.owner}: {f.acceptance.reason}
81
+ </span>
82
+ ) : (
83
+ <Accept onAccept={(reason, owner) => onChange(acceptRisk(project, f, reason, owner))} />
84
+ )}
85
+ </div>
86
+ </article>
87
+ ))}
88
+ </div>
89
+ );
90
+ })}
91
+ </section>
92
+ );
93
+ }
94
+
95
+ export function EstimateView({ project, result, onChange, onEstimate, busy=false, pricing, onNext }) {
96
+ const e = result;
97
+ return (
98
+ <section className="doc" aria-label="Estimate">
99
+ <header className="doc-head">
100
+ <div className="cost-heading"><h2>1. Cost estimate</h2>{onEstimate&&<button type="button" className="btn" disabled={!!pricing?.busy} onClick={()=>onEstimate(project)}>{pricing?.busy?'Calculating…':'Recalculate'}</button>}</div>
101
+ <p className="muted">Monthly planning estimate. {DISCLAIMER}</p>
102
+ </header>
103
+ <fieldset className="cost-size-selector"><legend>How large do you expect this project to be?</legend>
104
+ <div className="row">{PROJECT_SIZES.map(size=><button type="button" className={project.estimate?.projectSize===size.id?"btn primary":"btn"} key={size.id} aria-pressed={project.estimate?.projectSize===size.id} onClick={()=>{if(project.estimate?.projectSize===size.id)return;const next=setProjectSize(project,size.id);onChange(next);onEstimate?.(next);}}>{project.estimate?.projectSize===size.id&&<Check size={16} aria-hidden="true"/>}{size.label}</button>)}</div>
105
+ <details className="cost-assumptions"><summary>Planning assumptions</summary><p className="muted small">Small: 10,000 requests / 10 GB; Medium: 100,000 / 100 GB; Large: 1 million / 1 TB; Extra Large: 10 million / 10 TB per month. Existing usage figures take priority. Model assumptions: 3,000 tokens per request and 730 endpoint hours; training usage needs separate agreement.</p></details>
106
+ </fieldset>
107
+ {project.estimate?.projectSize&&<p className="cost-selection-status" role="status">{PROJECT_SIZES.find(size=>size.id===project.estimate.projectSize)?.label} · {pricing?.busy?'Updating prices…':e.complete?'Estimate ready':e.lines.some(line=>line.cost!=null)?'Partial estimate':'Prices unavailable'}</p>}{pricing?.errors?.map((error,i)=><p className="warn-text" key={i}>{error.component&&`${error.component}: `}{error.reason} <button className="link" disabled={busy||pricing?.busy} onClick={()=>onEstimate?.(project)}>Retry pricing</button></p>)}
108
+
109
+ <div className="totals">
110
+ <div>
111
+ <small className="muted">Estimated subtotal / month</small>
112
+ <strong>{e.lines.some((l) => l.cost != null) ? e.expected===0&&e.preciseExpected>0?`< ${money(0.01,e.currency)}`:money(e.expected, e.currency) : "—"}</strong>
113
+ </div>
114
+ <div>
115
+ <small className="muted">Range</small>
116
+ <span>
117
+ {e.lines.some((l) => l.cost != null) ? `${money(e.low, e.currency)} – ${money(e.high, e.currency)}` : "—"}
118
+ </span>
119
+ </div>
120
+ <div>
121
+ <small className="muted">Status</small>
122
+ <span className={e.complete ? "ok-text" : "warn-text"}>
123
+ {e.complete ? "All entered charges estimated" : e.mixedCurrencies ? "Currency needs review" : "Partial estimate · see exclusions"}
124
+ </span>
125
+ </div>
126
+ {e.budget && (
127
+ <div>
128
+ <small className="muted">Budget</small>
129
+ <span className={e.budget.comparable && e.expected > e.budget.amount ? "bad-text" : ""}>
130
+ {e.budget.currency} {e.budget.amount.toLocaleString("en")}
131
+ {e.budget.comparable ? (e.expected > e.budget.amount ? " · over" : " · within") : " · not compared"}
132
+ </span>
133
+ </div>
134
+ )}
135
+ </div>
136
+ <p className="muted small">{e.lines.some(line=>line.price?.planningReference)?'US reference rates · cloud region undecided. Unpriced charges excluded.':'Unpriced charges are excluded.'}</p>
137
+ {e.drivers.length > 0 && (
138
+ <p className="small">
139
+ Biggest drivers: {e.drivers.map((d) => `${d.component} (${money(d.cost, d.price.currency)})`).join(", ")}
140
+ </p>
141
+ )}
142
+ {e.lines.some(l=>l.cost==null)&&<p role="status">{e.lines.filter(l=>l.cost==null).length} charges unpriced.</p>}
143
+ {onNext&&<button type="button" className="btn" onClick={onNext}>Project files ↓</button>}
144
+ <details className="extra"><summary>Cost breakdown and price sources</summary><div className="resource-schedule" aria-label="Estimate breakdown">{e.lines.map(l=><article key={l.id}><strong>{l.component} · {l.driver}</strong><span>{l.cost==null?'Not yet estimated':money(l.cost,l.price.currency)+'/month'}</span><small>{l.note||l.price?.note}</small><small>{l.quantity??'Usage unresolved'} {l.unit}{l.price&&<> · {l.price.source}{l.price.url&&<> · <a href={l.price.url} target="_blank" rel="noreferrer">price source</a></>}</>}</small></article>)}</div></details>
145
+ <details className="extra"><summary>Adjust usage assumptions and prices</summary>
146
+ {e.lines.some((l) => !l.detailed) && <>
147
+ <div className="doc-sub">
148
+ <h3>Usage</h3>
149
+ </div>
150
+ <div className="usage">
151
+ {INPUTS.filter((i) => e.lines.some((l) => l.inputs.includes(i.id))).map((i) => {
152
+ const u = e.usage[i.id];
153
+ return (
154
+ <label key={i.id} className="field">
155
+ {i.label}
156
+ <input
157
+ aria-label={i.label}
158
+ inputMode="decimal"
159
+ placeholder={u.value != null ? String(u.value) : "unknown"}
160
+ value={project.estimate?.usage?.[i.id] ?? ""}
161
+ onChange={(ev) => onChange(setUsage(project, i.id, ev.target.value))}
162
+ />
163
+ <small className={u.source === "needed" ? "warn-text" : "muted"}>
164
+ {u.source === "needed" ? "needed by a line below" : `${u.value?.toLocaleString("en")} ${i.unit} · ${u.source}`}
165
+ </small>
166
+ {i.hint && <small className="muted">{i.hint}</small>}
167
+ </label>
168
+ );
169
+ })}
170
+ </div>
171
+ </>}
172
+ {e.lines.some((l) => !l.detailed) &&
173
+ <div className="doc-sub">
174
+ <h3>Lines</h3>
175
+ <span className="muted small">Price per unit, with where it came from.</span>
176
+ </div>
177
+ }
178
+ {e.lines.some((l) => !l.detailed) ? (
179
+ <div className="lines">
180
+ {e.lines.filter((l) => !l.detailed).map((l) => {
181
+ const p = project.estimate?.prices?.[l.id] || {};
182
+ const set = (patch) => onChange(setPrice(project, l.id, patch));
183
+ return (
184
+ <div key={l.id} className={`line ${l.status.replace(" ", "-")}`}>
185
+ <span>
186
+ <strong>{l.component}</strong>
187
+ <small className="muted">
188
+ {l.driver} · {l.quantity == null ? `needs ${l.missing.map((m) => INPUTS.find((i) => i.id === m).label.toLowerCase()).join(", ")}` : `${l.quantity.toLocaleString("en")} ${l.unit}`}
189
+ </small>
190
+ </span>
191
+ <input
192
+ aria-label={`${l.component} unit price`}
193
+ inputMode="decimal"
194
+ placeholder={`price / ${l.unit}`}
195
+ value={p.price ?? ""}
196
+ onChange={(ev) => set({ price: ev.target.value === "" ? undefined : Number(ev.target.value) })}
197
+ />
198
+ <input aria-label={`${l.component} currency`} className="cur" value={p.currency || "USD"} onChange={(ev) => set({ currency: ev.target.value.toUpperCase().slice(0, 3) })} />
199
+ <input aria-label={`${l.component} price source`} placeholder="source or URL" value={p.url || (p.source && p.source !== "entered by you" ? p.source : "")} onChange={(ev) => set({ url: ev.target.value })} />
200
+ <span className="cost">{l.cost == null ? <em className="muted">{l.status}</em> : money(l.cost, l.price.currency)}</span>
201
+ </div>
202
+ );
203
+ })}
204
+ </div>
205
+ ) : (
206
+ !project.nodes.length && <p className="muted">Sketch a design first; lines appear for each component that has a running cost.</p>
207
+ )}
208
+ {e.notCosted.length > 0 && <p className="muted small">Not costed in this release: {e.notCosted.join(", ")}.</p>}
209
+ <div className="row small">
210
+ <label className="inline">
211
+ Low ×
212
+ <input aria-label="Low factor" value={e.factors.low} onChange={(ev) => onChange({ ...project, estimate: { ...(project.estimate || {}), low: Number(ev.target.value) || 0 } })} />
213
+ </label>
214
+ <label className="inline">
215
+ High ×
216
+ <input aria-label="High factor" value={e.factors.high} onChange={(ev) => onChange({ ...project, estimate: { ...(project.estimate || {}), high: Number(ev.target.value) || 0 } })} />
217
+ </label>
218
+ </div>
219
+ </details>
220
+ <details className="extra"><summary>Advanced charge worksheet</summary><DetailedCharges project={project} onChange={onChange} /></details>
221
+ {e.lines.some((l) => l.detailed) && <div className="resource-schedule" aria-label="Detailed cost breakdown">{e.lines.filter((l) => l.detailed).map((l) => <article key={l.id}><strong>{l.component} · {l.driver}</strong><span>{l.quantity ?? "?"} {l.unit} × {l.price ? money(l.price.price, l.price.currency) : "price needed"} = {l.cost == null ? l.status : money(l.cost, l.price.currency)}/month</span></article>)}</div>}
222
+ <details className="extra"><summary>What we still need for a full estimate · {e.gaps.length} worksheet gaps</summary><ul>{e.gaps.map((g,i) => <li key={i}>{g}</li>)}</ul><p className="muted small">Check networking, security, logging, backups, support, licences, taxes and delivery effort. A detailed worksheet still needs validation against the selected services.</p></details>
223
+ <details className="extra"><summary>Optional delivery effort estimate</summary><ConsultingView project={project} onChange={onChange} /></details>
224
+ </section>
225
+ );
226
+ }
227
+
228
+ export function DeployView({ project, findings, environment, onEnvironment, onPlan, onPulumi, onDocument, onPack, onTerraform, onChange }) {
229
+ const block = blockers(findings);
230
+ const infrastructure = resourcePlan(project);
231
+ const gate = project.gate || {};
232
+ return (
233
+ <section className="doc" aria-label="Deploy">
234
+ <header className="doc-head">
235
+ <h2>Deploy</h2>
236
+ <p className="muted">A reviewable plan and document. AtMem AI Architect never provisions anything on its own.</p>
237
+ </header>
238
+ <div className="ready no">
239
+ <strong>{block.length ? "Not ready for deployment" : "No automated review blockers"}</strong>
240
+ {!block.length && <p className="small">{infrastructure.gaps.length ? `${infrastructure.gaps.length} infrastructure and integration tasks remain.` : "Infrastructure and account validation are still required."}</p>}
241
+ {block.length > 0 && (
242
+ <ul>
243
+ {block.map((f) => (
244
+ <li key={f.key}>{f.statement}</li>
245
+ ))}
246
+ </ul>
247
+ )}
248
+ </div>
249
+ <div className="row">
250
+ <div className="seg">
251
+ {["staging", "production"].map((env) => (
252
+ <button key={env} className={environment === env ? "on" : ""} onClick={() => onEnvironment(env)}>
253
+ {env[0].toUpperCase() + env.slice(1)}
254
+ </button>
255
+ ))}
256
+ </div>
257
+ <button className="btn primary" onClick={onDocument}>
258
+ Deployment document
259
+ </button>
260
+ <button className="btn primary" onClick={onPack}>Download delivery pack</button>
261
+ <button className="btn" onClick={onTerraform}>Terraform sketch</button>
262
+ <button className="btn" onClick={onPlan}>
263
+ Export {environment} plan
264
+ </button>
265
+ <button className="btn ghost" onClick={onPulumi}>
266
+ Pulumi
267
+ </button>
268
+ </div>
269
+ <p className="muted small">The delivery pack includes the specification, diagram, decisions, estimates, resource schedule, editable project and Terraform sketch, all from the current snapshot. Change the project and regenerate it.</p>
270
+ <p className="warn-text small">Deployment access and execution are not connected. Resource coverage and implementation work are listed below; automated review alone cannot establish deployment readiness.</p>
271
+ <ResourcesView project={project} />
272
+ <div className="doc-sub">
273
+ <h3>Draft steps</h3>
274
+ <span className="muted small">Validate against the provider's documentation before use.</span>
275
+ </div>
276
+ <ol className="steps-list">
277
+ {deploymentSteps(project).map((s) => (
278
+ <li key={s}>{s}</li>
279
+ ))}
280
+ </ol>
281
+ <details className="extra">
282
+ <summary>Scale gate — scale only when value exceeds cost and risk</summary>
283
+ {GATE.map((g) => (
284
+ <label key={g.id} className="field">
285
+ {g.label}
286
+ <textarea
287
+ rows={2}
288
+ aria-label={g.label}
289
+ placeholder={g.hint}
290
+ value={gate[g.id] || ""}
291
+ onChange={(e) => onChange({ ...project, gate: { ...gate, [g.id]: e.target.value } })}
292
+ />
293
+ </label>
294
+ ))}
295
+ </details>
296
+ </section>
297
+ );
298
+ }
299
+
300
+ // Test run companion card: what's in scope and whether the success measures passed.
301
+ export function TestCard({ project, run, onOpen }) {
302
+ const tested = run && run.revision === project.revision && run.status !== "running";
303
+ const results = Object.fromEntries((tested ? run.steps : []).flatMap((s) => (s.output?.measures || []).map((m) => [m.label, m])));
304
+ const outputs = project.nodes.filter((n) => n.data.measures?.length);
305
+ return (
306
+ <aside className="deploy build" aria-label="Test">
307
+ <h3>Test run</h3>
308
+ <p className="small">Runs each block in order with built-in sample data, on this computer. No AI model is called, nothing touches a cloud account, and it costs nothing.</p>
309
+ <p className="muted small">Faded blocks are outside the prototype scope.</p>
310
+ {outputs.length ? (
311
+ <ul className="checks">
312
+ {outputs.flatMap((n) =>
313
+ n.data.measures.map((m) => {
314
+ const r = results[m.label];
315
+ return (
316
+ <li key={n.id + m.label} className={r ? (r.pass ? "ok" : "bad") : ""}>
317
+ <button className="link-plain" onClick={() => onOpen(n.id)}>
318
+ {m.label} {m.op} {String(m.target)}
319
+ {r && <span className="muted"> · {String(r.value)}</span>}
320
+ </button>
321
+ </li>
322
+ );
323
+ }),
324
+ )}
325
+ </ul>
326
+ ) : (
327
+ <p className="muted small">Success measures live on a final check block.</p>
328
+ )}
329
+ </aside>
330
+ );
331
+ }
332
+
333
+ const when = (iso) => {
334
+ if (!iso) return "";
335
+ const d = new Date(iso);
336
+ const mins = Math.round((Date.now() - d) / 60000);
337
+ if (mins < 1) return "just now";
338
+ if (mins < 60) return `${mins} min ago`;
339
+ if (mins < 60 * 24) return `${Math.round(mins / 60)} h ago`;
340
+ return d.toLocaleDateString();
341
+ };
342
+
343
+ // Projects saved under your account on this computer, with a rubbish bin.
344
+ export function ProjectsDialog({ list, currentId, onOpen, onNew, onBin, onRestore, onPurge, folder }) {
345
+ const [confirm, setConfirm] = React.useState(null);
346
+ return (
347
+ <div className="projects">
348
+ <div className="row-between">
349
+ <h2>Projects</h2>
350
+ <button className="button run-button" onClick={onNew}>
351
+ New project
352
+ </button>
353
+ </div>
354
+ {list.projects.length ? (
355
+ <ul className="project-list">
356
+ {list.projects.map((p) => (
357
+ <li key={p.id} className={p.id === currentId ? "current" : ""}>
358
+ <button className="open" onClick={() => onOpen(p.id)} disabled={p.id === currentId}>
359
+ <strong>{p.name}</strong>
360
+ <small>
361
+ {p.id === currentId ? "open now" : when(p.updatedAt)} · {p.blocks} block{p.blocks === 1 ? "" : "s"}
362
+ </small>
363
+ </button>
364
+ <button className="icon bin" aria-label={`Move ${p.name} to the bin`} title="Move to bin" onClick={() => onBin(p.id)}>
365
+ 🗑
366
+ </button>
367
+ </li>
368
+ ))}
369
+ </ul>
370
+ ) : (
371
+ <p className="muted">No saved projects yet. Your current one saves itself as you work.</p>
372
+ )}
373
+ {list.bin.length > 0 && (
374
+ <details className="bin-list">
375
+ <summary>Rubbish bin · {list.bin.length}</summary>
376
+ <ul className="project-list">
377
+ {list.bin.map((p) => (
378
+ <li key={p.id}>
379
+ <span className="open">
380
+ <strong>{p.name}</strong>
381
+ <small>binned {when(p.binnedAt)}</small>
382
+ </span>
383
+ <button className="btn" onClick={() => onRestore(p.id)}>
384
+ Restore
385
+ </button>
386
+ {confirm === p.id ? (
387
+ <button className="btn danger" onClick={() => (onPurge(p.id), setConfirm(null))}>
388
+ Delete forever?
389
+ </button>
390
+ ) : (
391
+ <button className="btn ghost danger" onClick={() => setConfirm(p.id)}>
392
+ Delete
393
+ </button>
394
+ )}
395
+ </li>
396
+ ))}
397
+ </ul>
398
+ </details>
399
+ )}
400
+ <p className="modal-fine">{folder || "~/.atforma"}</p>
401
+ </div>
402
+ );
403
+ }
@@ -0,0 +1,12 @@
1
+ import React,{useState,useEffect}from'react';
2
+ import{newDecision}from'./engagement.mjs';import{reviseDecision}from'./records.mjs';
3
+ const latest=(project,subject)=>[...(project.decisions||[])].reverse().find(d=>d.subject===subject&&d.status==='Accepted');
4
+ export function LocationChoices({project,onChange,onDiscuss}){
5
+ const cloud=latest(project,'platform.cloud')?.decision,country=latest(project,'data.country')?.decision,current=latest(project,'cloud.region');
6
+ const [region,setRegion]=useState(current?.decision||''),[error,setError]=useState('');
7
+ useEffect(()=>setRegion(current?.decision||''),[project.id,current?.decision]);
8
+ function apply(event){event.preventDefault();const value=region.trim();if(!cloud||!value)return;if(!/^[a-z][a-z0-9-]{2,39}$/.test(value)){setError('Enter a region code.');return;}if(cloud==='gcp'&&/^(usa|us|united states)$/i.test(country||'')&&!value.startsWith('us-')){setError('Choose a US region for USA residency.');return;}
9
+ const fields={title:'Cloud region',decision:value,status:'Accepted',owner:'user',reason:'Selected in Choices.'};let next=current?reviseDecision(project,current.id,fields,fields.reason):{...project,decisions:[...(project.decisions||[]),newDecision(project,{...fields,subject:'cloud.region'})]};
10
+ next={...next,nodes:next.nodes.map(node=>{const previous=node.data.config?.region;return node.data.provider===cloud&&node.data.origin!=='existing'&&(!previous||/select|unknown|tbd/i.test(previous)||previous===current?.decision)?{...node,data:{...node.data,config:{...node.data.config,region:value}}}:node;})};onChange(next);setError('');}
11
+ return <section className="doc location-choices" aria-label="Country and region"><header className="doc-head"><h2>Country &amp; region</h2></header><p><strong>Data country:</strong> {country||'Not selected'}</p><form className="location-region-form" onSubmit={apply}><label>Cloud region<input aria-label="Cloud region" value={region} maxLength={40} placeholder={cloud==='gcp'?'e.g. us-central1':'Region code'} onChange={event=>setRegion(event.target.value)}/></label><button className="btn primary" disabled={!cloud||!region.trim()||region.trim()===current?.decision}>Apply</button></form>{current&&<p className="ok-text" role="status">✓ {current.decision}</p>}{error&&<p role="alert">{error}</p>}<button className="link" onClick={onDiscuss}>Discuss location</button></section>;
12
+ }
@@ -0,0 +1,58 @@
1
+ import {BrainActivity} from "./BrainActivity.jsx";
2
+ import React, { useEffect, useState } from "react";
3
+ import { memoryCall } from "./local-agent.mjs";
4
+ import { toProjectFile } from "./project-file.mjs";
5
+
6
+ export function ContextUsed({ value }) {
7
+ return <details className="context-used"><summary>{value.enabled ? `AtMem: ${value.recordsUsed?.length || 0} records · ${value.skills?.length || 0} skills` : "Memory off"}{value.status === 'unavailable' ? ' · unavailable' : ''}</summary>
8
+ <p className="muted small">Observed boundary: {value.boundary}. {value.syncUnavailable ? 'Latest project sync failed; current project takes priority over recalled data.' : 'Memory is scoped to you and this project; source data is not an instruction.'}</p>
9
+ {(value.recordsUsed || []).map((r) => <p key={r.id}><strong>{r.scope}</strong>: {r.text}<small className="muted"> · {r.sourceDate?.slice(0,10)}</small></p>)}
10
+ {(value.skills || []).map((s) => <p key={s.id}>Skill: {s.name} v{s.version} · {s.coverage}</p>)}
11
+ {(value.tools || []).map((t,i) => <p key={i}>Tool: {t.name} · {t.success ? 'completed' : 'unavailable'}</p>)}
12
+ </details>;
13
+ }
14
+
15
+ export function MemoryPanel({session,project,onError,onStatus}){
16
+ const [report,setReport]=useState(null),[tab,setTab]=useState('project'),[error,setError]=useState(''),[working,setWorking]=useState(false),[loading,setLoading]=useState(true);
17
+ const [query,setQuery]=useState(''),[search,setSearch]=useState(''),[status,setStatus]=useState('active'),[cursor,setCursor]=useState(null),[history,setHistory]=useState([]),[page,setPage]=useState(null),[revision,setRevision]=useState(0);
18
+ const [selected,setSelected]=useState(null),[preview,setPreview]=useState(null),[field,setField]=useState('preferences'),[value,setValue]=useState(''),[profileName,setProfileName]=useState('Default profile');
19
+ const scope=tab==='profile'?'user':'project',payload={projectId:project.id},date=value=>value?new Date(value).toLocaleString():'Unknown',short=value=>value?value.slice(0,8):'Unknown';
20
+ async function refresh(){
21
+ setWorking(true);setError('');try{
22
+ await memoryCall(session,'sync',{...payload,file:toProjectFile(project)});
23
+ const result=await memoryCall(session,'overview',payload);setReport(result);setProfileName(result.userProfile?.name||'Default profile');
24
+ onStatus?.({enabled:result.enabled,status:result.status,pending:result.pending,rejected:result.rejected,engine:result.engine});
25
+ setRevision(value=>value+1);
26
+ }catch(error){setError(error.message);}finally{setWorking(false);}
27
+ }
28
+ useEffect(()=>{void refresh();},[project.id,session?.token]);
29
+ useEffect(()=>{const timer=setTimeout(()=>{setSearch(query);setCursor(null);setHistory([]);},250);return()=>clearTimeout(timer);},[query]);
30
+ useEffect(()=>{
31
+ if(!['profile','project'].includes(tab))return;
32
+ const controller=new AbortController();setLoading(true);setError('');
33
+ memoryCall(session,'records',{...payload,scope,status,query:search,cursor,limit:50},controller.signal).then(value=>{if(!controller.signal.aborted)setPage(value);}).catch(error=>{if(!controller.signal.aborted)setError(error.message);}).finally(()=>{if(!controller.signal.aborted)setLoading(false);});
34
+ return()=>controller.abort();
35
+ },[project.id,session?.token,scope,tab,status,search,cursor,revision]);
36
+ async function act(operation){setWorking(true);setError('');try{await operation();setCursor(null);setHistory([]);await refresh();}catch(error){setError(error.message);onError?.(error.message);}finally{setWorking(false);}}
37
+ async function openRecord(record){setSelected({record,audit:[]});try{setSelected(await memoryCall(session,'record',{...payload,scope,recordId:record.id}));}catch(error){setError(error.message);}}
38
+ function chooseTab(value){setTab(value);setSelected(null);setPreview(null);setCursor(null);setHistory([]);setQuery('');setSearch('');}
39
+ return <div className="memory-panel memory-browser">
40
+ <header className="memory-heading"><h2>AtMem memory</h2><button className="btn" disabled={working} onClick={refresh}>Refresh</button></header>
41
+ <div className="memory-profile-bar"><label>Profile <input aria-label="Profile name" value={profileName} maxLength={80} onChange={event=>setProfileName(event.target.value)}/></label><button className="btn" disabled={working||!profileName.trim()||profileName===report?.userProfile?.name} onClick={()=>act(async()=>{await memoryCall(session,'user-profile',{name:profileName});})}>Save</button><code title="Local profile user ID">{report?.userProfile?.id||'Loading profile…'}</code><span className="memory-state">{report?.enabled?'On':'Off'}{report?.status==='unavailable'?' · Unavailable':''}</span><button className="link" disabled={working||!report} onClick={()=>act(()=>memoryCall(session,'settings',{...payload,enabled:!report.enabled}))}>{report?.enabled?'Turn off':'Turn on'}</button></div>
42
+ <div className="seg" role="tablist" aria-label="Memory views">{[['project','This project'],['profile','About you'],['skills','Skills'],['activity','Activity'],['providers','Context providers']].map(([id,label])=><button key={id} role="tab" aria-selected={tab===id} className={tab===id?'on':''} onClick={()=>chooseTab(id)}>{label}</button>)}</div>
43
+ {error&&<p className="error-box" role="alert">{error}</p>}
44
+ {(working||loading&&['project','profile'].includes(tab))&&<p className="memory-feedback" role="status"><BrainActivity/> Updating memory…</p>}
45
+ {report&&(report.pending>0||report.rejected>0)&&<p className="warn-text">{report.pending} pending · {report.rejected} rejected</p>}
46
+ {['profile','project'].includes(tab)&&<>
47
+ <div className="memory-toolbar"><input type="search" aria-label="Search memory" placeholder="Search memory or record ID" value={query} maxLength={200} onChange={event=>setQuery(event.target.value)}/><select aria-label="Memory status" value={status} onChange={event=>{setStatus(event.target.value);setCursor(null);setHistory([]);}}><option value="active">Current</option><option value="superseded">Earlier</option><option value="quarantined">Quarantined</option><option value="all">All</option></select><strong>{page?.total?.toLocaleString()??'—'} {page?.total===1?'record':'records'}</strong></div>
48
+ <div className="memory-table-wrap" aria-busy={loading}><table className="memory-table"><thead><tr><th>Key</th><th>Memory</th><th>Status</th><th>User</th><th>Updated</th><th><span className="sr-only">Actions</span></th></tr></thead><tbody>{page?.records?.map(record=><tr key={record.id}><td><button className="link" onClick={()=>openRecord(record)}>{record.fact_key||record.id}</button></td><td><button className="memory-content" onClick={()=>openRecord(record)}>{record.content}</button></td><td><span className={'memory-badge '+record.status}>{record.status==='active'?'Current':record.status==='superseded'?'Earlier':record.status}</span></td><td title={record.governance?.userId||'Creator was not recorded'}>{short(record.governance?.userId)}</td><td>{date(record.updated_at||record.created_at)}</td><td><button className="link" disabled={working} onClick={()=>act(async()=>{setPreview({...await memoryCall(session,'forget-preview',{...payload,scope,recordId:record.id}),scope,recordId:record.id});})}>Forget</button></td></tr>)}</tbody></table>{!loading&&!page?.records?.length&&<p className="memory-empty">{query?'No matching memories.':'No memories yet.'}</p>}</div>
49
+ <footer className="memory-pagination"><span>Page {history.length+1} · 50 per page</span><button className="btn" disabled={loading||!history.length} onClick={()=>{setCursor(history.at(-1));setHistory(value=>value.slice(0,-1));}}>Previous</button><button className="btn" disabled={loading||!page?.hasMore} onClick={()=>{setHistory(value=>[...value,cursor]);setCursor(page.nextCursor);}}>Next</button></footer>
50
+ </>}
51
+ {tab==='profile'&&<form className="memory-preference-form" onSubmit={event=>{event.preventDefault();act(async()=>{await memoryCall(session,'profile',{...payload,field,value});setValue('');});}}><select aria-label="Preference type" value={field} onChange={event=>setField(event.target.value)}><option value="preferences">Preference</option><option value="explanation">Explanation style</option><option value="experience">Role</option><option value="iac">Infrastructure tool</option></select><input aria-label="Remember about me" placeholder="Add a preference" value={value} maxLength={1000} onChange={event=>setValue(event.target.value)}/><button className="btn primary" disabled={working||!report?.enabled||!value.trim()}>Remember</button></form>}
52
+ {selected&&<section className="memory-record-detail" aria-label="Memory details"><header className="memory-heading"><h3>{selected.record.fact_key||'Memory'}</h3><button className="icon" aria-label="Close memory details" onClick={()=>setSelected(null)}>×</button></header><p>{selected.record.content}</p><dl>{[['Record',selected.record.id],['Creator user ID',selected.record.governance?.userId||'Unknown · legacy record'],['Owner user ID',selected.record.governance?.ownerUserId],['Actor',selected.record.governance?.actor||'Unknown'],['Source',selected.record.governance?.source||'Unknown'],['Created',date(selected.record.created_at)],['Updated',date(selected.record.updated_at)]].map(([label,value])=><React.Fragment key={label}><dt>{label}</dt><dd>{value||'Unknown'}</dd></React.Fragment>)}</dl>{selected.audit?.length>0&&<table className="memory-table"><thead><tr><th>Time</th><th>Actor</th><th>Action</th></tr></thead><tbody>{selected.audit.map((event,index)=><tr key={index}><td>{date(event.created_at)}</td><td>{event.actor}</td><td>{event.event_type}</td></tr>)}</tbody></table>}</section>}
53
+ {preview&&<section className="memory-preview" aria-label="Confirm memory removal"><h3>Forget memory?</h3><p>{preview.total??preview.records.length} records, including history. Project files stay.</p><button className="btn danger" disabled={working} onClick={()=>act(async()=>{await memoryCall(session,'forget',{...payload,scope:preview.scope,recordId:preview.recordId});setPreview(null);setSelected(null);})}>Forget</button><button className="btn" onClick={()=>setPreview(null)}>Cancel</button></section>}
54
+ {tab==='skills'&&<table className="memory-table"><thead><tr><th>Skill</th><th>Version</th><th>Integrity</th><th>Source</th><th>Status</th></tr></thead><tbody>{report?.skills?.map(skill=><tr key={skill.id}><td>{skill.name}</td><td>{skill.version}</td><td>{skill.integrity?'Verified':'Failed'}</td><td>{skill.source.startsWith('https:')?<a href={skill.source} target="_blank" rel="noreferrer">Source ↗</a>:'Local'}</td><td><button className="link" disabled={working||!skill.integrity} onClick={()=>act(()=>memoryCall(session,'skill',{...payload,skillId:skill.id,enabled:!skill.enabled}))}>{skill.enabled?'Disable':'Enable'}</button></td></tr>)}</tbody></table>}
55
+ {tab==='providers'&&<section aria-label="Context providers"><table className="memory-table"><thead><tr><th>Provider</th><th>Type</th><th>Status</th></tr></thead><tbody>{report?.contextProviders?.map(provider=><tr key={provider.id}><td>{provider.label}</td><td>{provider.kind}</td><td>{provider.enabled?'Enabled':'Disabled'}</td></tr>)}</tbody></table>{!report?.contextProviders?.length&&<p>No external providers configured.</p>}<p className="muted small">Connect RAG or memory through the server’s MEMORY_CONTEXT_PROVIDERS setting. Retrieved context is never saved automatically.</p></section>}
56
+ {tab==='activity'&&<table className="memory-table"><thead><tr><th>Time</th><th>Result</th><th>Records</th><th>Skills</th></tr></thead><tbody>{report?.activity?.map(activity=><tr key={activity.id}><td>{date(activity.createdAt)}</td><td>{activity.success?'Completed':'Incomplete'}</td><td>{activity.recordIds.length}</td><td>{activity.skillIds.join(', ')||'—'}</td></tr>)}</tbody></table>}
57
+ </div>;
58
+ }
@@ -0,0 +1,66 @@
1
+ import {InlineNote} from './HelpNote.jsx';
2
+ import {X,Volume2} from 'lucide-react';
3
+ import {BrainActivity} from './BrainActivity.jsx';
4
+ import React,{useEffect,useRef,useState} from 'react';
5
+ import {OpenAIVoice} from './openai-voice.mjs';
6
+
7
+ export function OpenAIVoicePanel({session,connection,transportRef,onTranscript,onStart,onConnections}){
8
+ const [status,setStatus]=useState(null),[phase,setPhase]=useState('off'),[error,setError]=useState(''),[key,setKey]=useState(''),[saving,setSaving]=useState(false),[automatic,setAutomatic]=useState(false),[readReplies,setReadReplies]=useState(true),[open,setOpen]=useState(false);
9
+ const props=useRef({onTranscript,onStart,automatic});props.current={onTranscript,onStart,automatic};
10
+ const engine=useRef(null),live=useRef(true);
11
+ const active=!['off','error'].includes(phase);
12
+ async function refresh(){
13
+ const response=await fetch('/api/voice/openai/status');
14
+ if(!response.ok)throw Error('The voice service is unavailable. Check the Architect server.');
15
+ const body=await response.json();if(live.current)setStatus(body);
16
+ }
17
+ useEffect(()=>{
18
+ live.current=true;setPhase('off');void refresh().catch(e=>{if(live.current)setError(e.message)});
19
+ return()=>{live.current=false;engine.current?.stop();if(transportRef.current===engine.current)transportRef.current=null;};
20
+ },[session.token]);
21
+ async function configure(){
22
+ setSaving(true);setError('');
23
+ try{
24
+ const response=await fetch('/api/voice/openai/credentials',{method:'POST',headers:{Authorization:`Bearer ${session.token}`,'Content-Type':'application/json'},body:JSON.stringify({key})});
25
+ if(!response.ok)throw Error('The voice key could not be configured. Check the server connection.');
26
+ setKey('');await refresh();
27
+ }catch(e){setError(e.message)}finally{setSaving(false)}
28
+ }
29
+ function start(){
30
+ if(active||!status?.available||!connection)return;
31
+ props.current.onStart?.();setError('');engine.current?.stop();
32
+ const current=new OpenAIVoice({session,
33
+ onState:value=>{if(live.current)setPhase(value)},onError:value=>{if(live.current)setError(value)},
34
+ onTranscript:(text,meta)=>{
35
+ if(props.current.automatic&&!meta.ordered){setAutomatic(false);setError('Automatic sending paused. Review the retained transcript in chat.');}
36
+ props.current.onTranscript(text,props.current.automatic&&meta.ordered);
37
+ }});
38
+ current.readReplies=readReplies;engine.current=current;transportRef.current=current;void current.start().then(()=>{if(live.current&&current.channel?.readyState==='open')setOpen(false)});
39
+ }
40
+ const caption={off:'Off',connecting:'Connecting OpenAI audio…',listening:automatic?'Listening · auto-send':'Listening',speaking:'Speaking',error:'Stopped · retry'}[phase];
41
+ return <>
42
+ <div className="voice-status"><button type="button" className="icon" aria-label="Voice settings" title="Voice settings" onClick={()=>setOpen(true)}><Volume2 size={18}/></button>{active&&<button type="button" onClick={()=>engine.current?.stop({preservePending:true})}>Stop voice</button>}{active&&error.includes('playback')&&<button type="button" onClick={()=>void engine.current?.resumeAudio()}>Enable voice playback</button>}{(active||error)&&<span role="status">{error||caption}</span>}</div>
43
+ {open&&<div className="backdrop" onMouseDown={event=>{if(event.target===event.currentTarget)setOpen(false)}}><section className="modal" role="dialog" aria-modal="true" aria-label="OpenAI voice" onKeyDown={event=>{if(event.key==='Escape'){event.stopPropagation();setOpen(false)}}}>
44
+ <h2>Voice</h2><button type="button" className="icon modal-close" aria-label="Close voice settings" onClick={()=>setOpen(false)}><X size={18}/></button>
45
+ <section aria-label="OpenAI voice settings">
46
+ <small>AI audio · OpenAI</small><InlineNote>Audio and spoken replies go to OpenAI. Audio is not saved. Live verification pending.</InlineNote>
47
+ {!connection&&<button type="button" className="btn" onClick={()=>{setOpen(false);onConnections()}}>Choose AI provider</button>}
48
+ {status&&<details open={!status.available}><summary>API key</summary>
49
+
50
+ <label className="dialog-field">OpenAI voice API key<input aria-label="OpenAI voice API key" type="password" autoComplete="off" value={key} onChange={event=>setKey(event.target.value)}/></label>
51
+ <button type="button" className="btn" disabled={saving||active||!key.trim()} onClick={configure}>Use key</button>
52
+ </details>}
53
+ <button type="button" className="link" disabled={saving} onClick={()=>void refresh().catch(e=>setError(e.message))}>Refresh</button>
54
+ <div className="record-row">
55
+ <button type="button" className="btn" disabled={!status?.available||!connection} onClick={active?()=>engine.current?.stop({preservePending:true}):start}>{active?'Stop voice':'Start voice'}</button>
56
+ {active&&<button type="button" className="btn" onClick={()=>engine.current?.interrupt()}>Stop speaking</button>}
57
+ {active&&error.includes('playback')&&<button type="button" className="btn" onClick={()=>void engine.current?.resumeAudio()}>Enable voice playback</button>}
58
+ </div>
59
+ <p><label><input type="checkbox" checked={automatic} onChange={event=>setAutomatic(event.target.checked)}/> Auto-send</label></p>
60
+ <p><label><input type="checkbox" checked={readReplies} onChange={event=>{setReadReplies(event.target.checked);if(engine.current){engine.current.readReplies=event.target.checked;if(!event.target.checked)engine.current.interrupt();}}}/> Read replies</label></p>
61
+ <p role="status">{caption}</p>
62
+ {error&&<p role="alert">{error}</p>}
63
+ </section>
64
+ </section></div>}
65
+ </>;
66
+ }