@atmem/architect 0.0.0-stage → 0.2.0-alpha.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/CONSTITUTION.md +144 -0
  2. package/README.md +33 -2
  3. package/agent-skills/discovery/SKILL.md +48 -0
  4. package/agent-skills/discovery/topics.json +139 -0
  5. package/agent-skills/generation-capabilities.json +760 -0
  6. package/agent-skills/service-lifecycle.json +21 -0
  7. package/agent-skills/specialists/architecture/SKILL.md +15 -0
  8. package/agent-skills/specialists/aws/SKILL.md +17 -0
  9. package/agent-skills/specialists/azure/SKILL.md +7 -0
  10. package/agent-skills/specialists/composition/SKILL.md +17 -0
  11. package/agent-skills/specialists/devops/SKILL.md +10 -0
  12. package/agent-skills/specialists/finops/SKILL.md +7 -0
  13. package/agent-skills/specialists/gcp/SKILL.md +7 -0
  14. package/agent-skills/specialists/ibm/SKILL.md +7 -0
  15. package/agent-skills/specialists/manifest.json +13 -0
  16. package/agent-skills/specialists/pulumi/SKILL.md +7 -0
  17. package/agent-skills/specialists/security/SKILL.md +7 -0
  18. package/agent-skills/specialists/terraform/SKILL.md +14 -0
  19. package/agent-skills/upstream/aws/LICENSE +175 -0
  20. package/agent-skills/upstream/aws/SKILL.md +125 -0
  21. package/agent-skills/upstream/azure/LICENSE +21 -0
  22. package/agent-skills/upstream/azure/SKILL.md +387 -0
  23. package/agent-skills/upstream/gcp/LICENSE +202 -0
  24. package/agent-skills/upstream/gcp/SKILL.md +159 -0
  25. package/agent-skills/upstream/manifest.json +57 -0
  26. package/agent-skills/upstream/pulumi/LICENSE +201 -0
  27. package/agent-skills/upstream/pulumi/SKILL.md +501 -0
  28. package/agent-skills/upstream/terraform/LICENSE +373 -0
  29. package/agent-skills/upstream/terraform/SKILL.md +316 -0
  30. package/agent-skills/workflows/workflows.json +601 -0
  31. package/cli/architect.mjs +3 -0
  32. package/cli/runtime.mjs +115 -0
  33. package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
  34. package/dist/assets/index-B97MkVMs.css +1 -0
  35. package/dist/assets/index-qtCM2D7O.js +21271 -0
  36. package/dist/assets/index.es-C6C0sneB.js +18 -0
  37. package/dist/assets/purify.es-CAi2OJFm.js +3 -0
  38. package/dist/assets/visual-export-DjbjgQbA.js +173 -0
  39. package/dist/favicon.svg +2 -0
  40. package/dist/fonts/DM-Sans-OFL.txt +93 -0
  41. package/dist/fonts/Manrope-OFL.txt +93 -0
  42. package/dist/fonts/NotoSans-Regular.ttf +0 -0
  43. package/dist/fonts/OFL.txt +94 -0
  44. package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
  45. package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
  46. package/dist/index.html +4 -0
  47. package/dist/licenses/aws-architect-LICENSE.txt +17 -0
  48. package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
  49. package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
  50. package/dist/licenses/cloud-emulation-notices.txt +36619 -0
  51. package/dist/licenses/dependency-notices.txt +6379 -0
  52. package/dist/licenses/emulation-notices.txt +717 -0
  53. package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  54. package/dist/licenses/memory-dependency-notices.txt +3319 -0
  55. package/dist/licenses/openarchflow-LICENSE.txt +21 -0
  56. package/dist/licenses/python-dependency-notices.txt +11951 -0
  57. package/docs/upstream-lock.json +26 -0
  58. package/knowledge/engineering-practices.json +536 -0
  59. package/package.json +30 -4
  60. package/packaging/uv-release-lock.json +54 -0
  61. package/public/licenses/aws-architect-LICENSE.txt +17 -0
  62. package/public/licenses/aws-mcp-LICENSE.txt +175 -0
  63. package/public/licenses/aws-mcp-NOTICE.txt +1 -0
  64. package/public/licenses/cloud-emulation-notices.txt +36619 -0
  65. package/public/licenses/dependency-notices.txt +6379 -0
  66. package/public/licenses/emulation-notices.txt +717 -0
  67. package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  68. package/public/licenses/memory-dependency-notices.txt +3319 -0
  69. package/public/licenses/openarchflow-LICENSE.txt +21 -0
  70. package/public/licenses/python-dependency-notices.txt +11951 -0
  71. package/scripts/terraform-render.mjs +8 -0
  72. package/server/__init__.py +0 -0
  73. package/server/abstraction.py +28 -0
  74. package/server/agent_runtime.py +153 -0
  75. package/server/app.py +257 -0
  76. package/server/architect.py +112 -0
  77. package/server/architecture_activity.py +67 -0
  78. package/server/attachments.py +101 -0
  79. package/server/cloud-emulation-requirements.txt +29 -0
  80. package/server/codex_app_server.py +219 -0
  81. package/server/codex_engineering_backend.py +266 -0
  82. package/server/codex_worker_policy.py +170 -0
  83. package/server/contracts.py +315 -0
  84. package/server/copilot_worker.py +16 -0
  85. package/server/dependency_mirror.py +54 -0
  86. package/server/design_patch.py +43 -0
  87. package/server/emulation-requirements.txt +8 -0
  88. package/server/emulation_adapters.py +20 -0
  89. package/server/emulation_routes.py +98 -0
  90. package/server/emulation_worker.py +351 -0
  91. package/server/engineering_artifacts.py +215 -0
  92. package/server/engineering_assets/application-runtime.json +4372 -0
  93. package/server/engineering_assets/architecture_work.schema.json +79 -0
  94. package/server/engineering_assets/materialize_runtime.py +62 -0
  95. package/server/engineering_assets/prepare_tools.py +96 -0
  96. package/server/engineering_assets/sql_validation.py +59 -0
  97. package/server/engineering_contexts.py +120 -0
  98. package/server/engineering_delivery.py +210 -0
  99. package/server/engineering_finalization.py +210 -0
  100. package/server/engineering_handoff.py +163 -0
  101. package/server/engineering_jobs.py +206 -0
  102. package/server/engineering_knowledge.py +75 -0
  103. package/server/engineering_reconciliation.py +159 -0
  104. package/server/engineering_routes.py +469 -0
  105. package/server/engineering_sessions.py +273 -0
  106. package/server/engineering_tools.py +166 -0
  107. package/server/generation_lint.py +18 -0
  108. package/server/local_providers.py +181 -0
  109. package/server/local_validation.py +236 -0
  110. package/server/memory-requirements.lock +181 -0
  111. package/server/memory-requirements.txt +6 -0
  112. package/server/memory_routes.py +82 -0
  113. package/server/memory_service.py +273 -0
  114. package/server/memory_worker.py +66 -0
  115. package/server/openai_voice.py +80 -0
  116. package/server/packaged_app.py +9 -0
  117. package/server/pricing.py +84 -0
  118. package/server/process_control_observation.py +31 -0
  119. package/server/prompts.py +123 -0
  120. package/server/public_faq_emulation_worker.py +44 -0
  121. package/server/requirements.lock +1618 -0
  122. package/server/requirements.txt +68 -0
  123. package/server/runtime_dependency_locks.py +27 -0
  124. package/server/runtime_evidence.py +93 -0
  125. package/server/settings.py +14 -0
  126. package/server/skills.py +103 -0
  127. package/server/storage_emulation_worker.py +160 -0
  128. package/server/structured_emulation_worker.py +48 -0
  129. package/server/terraform_routes.py +118 -0
  130. package/server/upstream/__init__.py +0 -0
  131. package/server/upstream/agent/__init__.py +1 -0
  132. package/server/upstream/agent/aws_architect.py +600 -0
  133. package/server/upstream/agent/config/__init__.py +1 -0
  134. package/server/upstream/agent/config/agent_config.py +82 -0
  135. package/server/upstream/agent/config/prompts.py +138 -0
  136. package/server/upstream/agent/rate_limiter.py +62 -0
  137. package/server/upstream/utils/__init__.py +1 -0
  138. package/server/upstream/utils/file_writer.py +43 -0
  139. package/server/validation_applicability.py +85 -0
  140. package/server/validation_jobs.py +252 -0
  141. package/server/validation_workers.py +48 -0
  142. package/server/voice_routes.py +91 -0
  143. package/server/voice_worker.py +26 -0
  144. package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
  145. package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
  146. package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
  147. package/src/ArchitectureActivity.jsx +54 -0
  148. package/src/ArchitectureWorkPanel.jsx +46 -0
  149. package/src/ArtifactChecks.jsx +49 -0
  150. package/src/BlockRequirements.jsx +13 -0
  151. package/src/BrainActivity.jsx +11 -0
  152. package/src/ChatMessage.jsx +19 -0
  153. package/src/ChatResizeHandle.jsx +6 -0
  154. package/src/CodePreview.jsx +8 -0
  155. package/src/ComponentReview.jsx +31 -0
  156. package/src/DeliveryViews.jsx +59 -0
  157. package/src/EmulationPanel.jsx +44 -0
  158. package/src/EngagementViews.jsx +385 -0
  159. package/src/EngineeringPanel.jsx +151 -0
  160. package/src/ImplementationDelivery.jsx +55 -0
  161. package/src/JourneyViews.jsx +400 -0
  162. package/src/MemoryPanel.jsx +46 -0
  163. package/src/OpenAIVoicePanel.jsx +65 -0
  164. package/src/OutputsView.jsx +65 -0
  165. package/src/TerraformView.jsx +57 -0
  166. package/src/WorkbenchDialogs.jsx +532 -0
  167. package/src/activity-presentation.mjs +28 -0
  168. package/src/ai-qualification-scenarios.mjs +31 -0
  169. package/src/approval-application-files.mjs +32 -0
  170. package/src/approval-applications.mjs +82 -0
  171. package/src/approval-spec-kit.mjs +30 -0
  172. package/src/architecture-activity.css +28 -0
  173. package/src/architecture-activity.mjs +63 -0
  174. package/src/architecture-work.mjs +176 -0
  175. package/src/artifact-work.mjs +103 -0
  176. package/src/async-documents.mjs +191 -0
  177. package/src/atflow.css +1140 -0
  178. package/src/atforma.css +748 -0
  179. package/src/atmem-tokens.css +79 -0
  180. package/src/aws-preflight.mjs +60 -0
  181. package/src/aws-public-faq.mjs +102 -0
  182. package/src/aws-validation-scenarios.mjs +10 -0
  183. package/src/aws-validation.mjs +48 -0
  184. package/src/azure-plan-mocks.mjs +25 -0
  185. package/src/azure-structured.mjs +133 -0
  186. package/src/bedrock-summary.mjs +152 -0
  187. package/src/bounded-rag.mjs +60 -0
  188. package/src/brand.css +9 -0
  189. package/src/canvas-view.mjs +28 -0
  190. package/src/capability-composition.mjs +44 -0
  191. package/src/chat-attachments.mjs +13 -0
  192. package/src/cloud-provider-locks.mjs +9 -0
  193. package/src/cloud-storage-scenarios.mjs +2 -0
  194. package/src/component-review.mjs +55 -0
  195. package/src/composition-spec-kit.mjs +30 -0
  196. package/src/consultation-context.mjs +15 -0
  197. package/src/consultation.mjs +52 -0
  198. package/src/container-contracts.mjs +5 -0
  199. package/src/container-inspection.mjs +75 -0
  200. package/src/container-runtime.mjs +30 -0
  201. package/src/container-security-inspection.mjs +49 -0
  202. package/src/container-terraform.mjs +109 -0
  203. package/src/conversation.mjs +17 -0
  204. package/src/core.mjs +863 -0
  205. package/src/cross-cloud-storage.mjs +100 -0
  206. package/src/delivery-capabilities.mjs +25 -0
  207. package/src/delivery.mjs +58 -0
  208. package/src/deployment-ownership.mjs +10 -0
  209. package/src/deployment-region.mjs +11 -0
  210. package/src/derive.mjs +31 -0
  211. package/src/design-build-flow.mjs +10 -0
  212. package/src/diagram-attention.mjs +24 -0
  213. package/src/discovery.mjs +359 -0
  214. package/src/document.mjs +170 -0
  215. package/src/emulation-feedback.mjs +61 -0
  216. package/src/emulation-guidance.mjs +37 -0
  217. package/src/engagement.mjs +274 -0
  218. package/src/engineering-client.mjs +73 -0
  219. package/src/engineering-defaults.mjs +17 -0
  220. package/src/engineering.css +31 -0
  221. package/src/enterprise-application.mjs +76 -0
  222. package/src/enterprise-aws-host-infrastructure.mjs +62 -0
  223. package/src/enterprise-aws-host-tests.mjs +15 -0
  224. package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
  225. package/src/enterprise-aws-infrastructure.mjs +138 -0
  226. package/src/enterprise-google-infrastructure.mjs +84 -0
  227. package/src/enterprise-google-tests.mjs +25 -0
  228. package/src/estimate.mjs +188 -0
  229. package/src/existing-references.mjs +18 -0
  230. package/src/external-identities.mjs +17 -0
  231. package/src/faq-lambda-build.mjs +28 -0
  232. package/src/gcp-structured.mjs +104 -0
  233. package/src/generation-provider.mjs +7 -0
  234. package/src/generation-quality.mjs +69 -0
  235. package/src/handoff.mjs +141 -0
  236. package/src/ibm-registry.mjs +12 -0
  237. package/src/ibm-structured.mjs +103 -0
  238. package/src/identifiers.mjs +15 -0
  239. package/src/implementation-map.mjs +14 -0
  240. package/src/implementation-outcome.mjs +8 -0
  241. package/src/incomplete-terraform.mjs +9 -0
  242. package/src/integrations.ts +137 -0
  243. package/src/interface-contracts.mjs +30 -0
  244. package/src/invoice-application.mjs +66 -0
  245. package/src/invoice-aws-operations.mjs +136 -0
  246. package/src/invoice-aws.mjs +143 -0
  247. package/src/invoice-azure-operations.mjs +147 -0
  248. package/src/invoice-azure.mjs +134 -0
  249. package/src/invoice-builder.mjs +35 -0
  250. package/src/invoice-dependencies.mjs +43 -0
  251. package/src/invoice-gcp-operations.mjs +178 -0
  252. package/src/invoice-gcp.mjs +104 -0
  253. package/src/invoice-ibm-operations.mjs +278 -0
  254. package/src/invoice-ibm.mjs +142 -0
  255. package/src/invoice-operations.mjs +143 -0
  256. package/src/invoice-plan.mjs +45 -0
  257. package/src/invoice-region.mjs +6 -0
  258. package/src/invoice-spec-kit.mjs +61 -0
  259. package/src/invoice-state.mjs +7 -0
  260. package/src/invoice-terraform.mjs +29 -0
  261. package/src/invoice-tests.mjs +122 -0
  262. package/src/journey-workspace.css +72 -0
  263. package/src/journey.mjs +87 -0
  264. package/src/levels.mjs +145 -0
  265. package/src/local-agent.mjs +184 -0
  266. package/src/main.jsx +2458 -0
  267. package/src/model-policy-preflight.mjs +34 -0
  268. package/src/openai-voice.mjs +102 -0
  269. package/src/output-bundle.mjs +11 -0
  270. package/src/ownership-inspection.mjs +28 -0
  271. package/src/pack.mjs +201 -0
  272. package/src/pricing.mjs +31 -0
  273. package/src/production-state.mjs +8 -0
  274. package/src/project-copy.mjs +10 -0
  275. package/src/project-file.mjs +160 -0
  276. package/src/project-links.mjs +3 -0
  277. package/src/protected-state.mjs +43 -0
  278. package/src/public-faq-builder.mjs +19 -0
  279. package/src/public-faq-clouds.mjs +251 -0
  280. package/src/public-faq.mjs +122 -0
  281. package/src/pulumi.mjs +123 -0
  282. package/src/python-container.mjs +25 -0
  283. package/src/python-runtime.mjs +50 -0
  284. package/src/rag-handler.mjs +43 -0
  285. package/src/rag-web-assets.mjs +52 -0
  286. package/src/rag-web.mjs +68 -0
  287. package/src/records.mjs +177 -0
  288. package/src/resources.mjs +163 -0
  289. package/src/review.mjs +252 -0
  290. package/src/runtime/EnterpriseDocument.java +89 -0
  291. package/src/runtime/approved_http_destination.py +72 -0
  292. package/src/runtime/aws-lambda-qualification-lock.json +7 -0
  293. package/src/runtime/aws-python-lock.json +82 -0
  294. package/src/runtime/aws-web-python-lock.json +887 -0
  295. package/src/runtime/aws_json_model.py +42 -0
  296. package/src/runtime/azure-python-lock.json +863 -0
  297. package/src/runtime/azure-sources.json +8 -0
  298. package/src/runtime/azure-test-lock.json +23 -0
  299. package/src/runtime/azure_acceptance.py +192 -0
  300. package/src/runtime/azure_structured.py +153 -0
  301. package/src/runtime/bounded_rag.py +126 -0
  302. package/src/runtime/catalog_admin.py +70 -0
  303. package/src/runtime/cloud-sql-proxy-lock.json +8 -0
  304. package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
  305. package/src/runtime/connector_cli.py +50 -0
  306. package/src/runtime/connector_sync.py +70 -0
  307. package/src/runtime/durable-python-lock.json +247 -0
  308. package/src/runtime/durable_workflow.py +325 -0
  309. package/src/runtime/enterprise-application-manifest.json +110 -0
  310. package/src/runtime/enterprise-application-sources.json +266 -0
  311. package/src/runtime/enterprise-auth-python-lock.json +592 -0
  312. package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
  313. package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
  314. package/src/runtime/enterprise-aws-task-template.json +235 -0
  315. package/src/runtime/enterprise-document-lock.json +18 -0
  316. package/src/runtime/enterprise-document.Dockerfile +15 -0
  317. package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
  318. package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
  319. package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
  320. package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
  321. package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
  322. package/src/runtime/enterprise-runtime.Dockerfile +15 -0
  323. package/src/runtime/enterprise-web-app.js +1 -0
  324. package/src/runtime/enterprise-web-entry.js +105 -0
  325. package/src/runtime/enterprise-web-lock.json +29 -0
  326. package/src/runtime/enterprise-web-notices.txt +229 -0
  327. package/src/runtime/enterprise-web.css +4 -0
  328. package/src/runtime/enterprise-web.html +8 -0
  329. package/src/runtime/enterprise_audit.py +91 -0
  330. package/src/runtime/enterprise_audit_schema.sql +25 -0
  331. package/src/runtime/enterprise_auth_schema.sql +67 -0
  332. package/src/runtime/enterprise_auth_store.py +241 -0
  333. package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
  334. package/src/runtime/enterprise_aws_host_probe.py +105 -0
  335. package/src/runtime/enterprise_aws_hosts.py +136 -0
  336. package/src/runtime/enterprise_aws_jobs.py +300 -0
  337. package/src/runtime/enterprise_aws_objects.py +93 -0
  338. package/src/runtime/enterprise_aws_transfer.py +83 -0
  339. package/src/runtime/enterprise_azure_batch_api.py +128 -0
  340. package/src/runtime/enterprise_azure_host.py +599 -0
  341. package/src/runtime/enterprise_azure_jobs.py +294 -0
  342. package/src/runtime/enterprise_azure_objects.py +234 -0
  343. package/src/runtime/enterprise_azure_pool.py +161 -0
  344. package/src/runtime/enterprise_azure_transfer.py +122 -0
  345. package/src/runtime/enterprise_configuration.py +136 -0
  346. package/src/runtime/enterprise_document.py +116 -0
  347. package/src/runtime/enterprise_document_cli.py +32 -0
  348. package/src/runtime/enterprise_document_contract.py +54 -0
  349. package/src/runtime/enterprise_document_job.py +109 -0
  350. package/src/runtime/enterprise_document_jobs.py +122 -0
  351. package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
  352. package/src/runtime/enterprise_extraction_cache.py +186 -0
  353. package/src/runtime/enterprise_extraction_schema.sql +17 -0
  354. package/src/runtime/enterprise_google_jobs.py +275 -0
  355. package/src/runtime/enterprise_google_transfer.py +85 -0
  356. package/src/runtime/enterprise_http.py +212 -0
  357. package/src/runtime/enterprise_identity.py +52 -0
  358. package/src/runtime/enterprise_knowledge.py +152 -0
  359. package/src/runtime/enterprise_models.py +94 -0
  360. package/src/runtime/enterprise_native.py +181 -0
  361. package/src/runtime/enterprise_oauth.py +132 -0
  362. package/src/runtime/enterprise_operator.py +160 -0
  363. package/src/runtime/enterprise_parser_service.py +136 -0
  364. package/src/runtime/enterprise_parser_transport.py +68 -0
  365. package/src/runtime/enterprise_queries.py +255 -0
  366. package/src/runtime/enterprise_queries_schema.sql +27 -0
  367. package/src/runtime/enterprise_query_service.py +111 -0
  368. package/src/runtime/enterprise_secrets.py +110 -0
  369. package/src/runtime/enterprise_sessions.py +95 -0
  370. package/src/runtime/enterprise_sources.py +260 -0
  371. package/src/runtime/enterprise_web.py +39 -0
  372. package/src/runtime/enterprise_worker.py +68 -0
  373. package/src/runtime/export_contract_acceptance.py +18 -0
  374. package/src/runtime/faq-output-contract.json +40 -0
  375. package/src/runtime/faq-web-app.js +41 -0
  376. package/src/runtime/faq-web.css +1 -0
  377. package/src/runtime/faq-web.html +6 -0
  378. package/src/runtime/gcp-sources.json +9 -0
  379. package/src/runtime/gcp_acceptance.py +110 -0
  380. package/src/runtime/gcp_structured.py +150 -0
  381. package/src/runtime/http_acceptance.py +39 -0
  382. package/src/runtime/iap-web-app.js +36 -0
  383. package/src/runtime/iap-web-source.json +1 -0
  384. package/src/runtime/ibm-python-lock.json +856 -0
  385. package/src/runtime/ibm-sources.json +9 -0
  386. package/src/runtime/ibm_acceptance.py +133 -0
  387. package/src/runtime/ibm_structured.py +113 -0
  388. package/src/runtime/invoice-azapi-provider-lock.json +7 -0
  389. package/src/runtime/invoice-ibm-provider-lock.json +7 -0
  390. package/src/runtime/invoice-parser-lock.json +1199 -0
  391. package/src/runtime/invoice-parser-python-lock.json +56 -0
  392. package/src/runtime/invoice-parser.Dockerfile +190 -0
  393. package/src/runtime/invoice-sources.json +58 -0
  394. package/src/runtime/invoice-test-sources.json +24 -0
  395. package/src/runtime/invoice-web.css +2 -0
  396. package/src/runtime/invoice-web.html +12 -0
  397. package/src/runtime/invoice-web.js +112 -0
  398. package/src/runtime/invoice_configuration.py +17 -0
  399. package/src/runtime/invoice_document.py +95 -0
  400. package/src/runtime/invoice_extraction.py +96 -0
  401. package/src/runtime/invoice_http.py +129 -0
  402. package/src/runtime/invoice_identity.py +5 -0
  403. package/src/runtime/invoice_maintenance.py +40 -0
  404. package/src/runtime/invoice_native.py +77 -0
  405. package/src/runtime/invoice_object_store.py +153 -0
  406. package/src/runtime/invoice_operations.py +108 -0
  407. package/src/runtime/invoice_operator.py +79 -0
  408. package/src/runtime/invoice_operator_core.py +39 -0
  409. package/src/runtime/invoice_operator_job.py +147 -0
  410. package/src/runtime/invoice_parser_cli.py +17 -0
  411. package/src/runtime/invoice_parser_identity.py +39 -0
  412. package/src/runtime/invoice_parser_service.py +89 -0
  413. package/src/runtime/invoice_parser_transport.py +89 -0
  414. package/src/runtime/invoice_processing.py +24 -0
  415. package/src/runtime/invoice_retention.py +16 -0
  416. package/src/runtime/invoice_runtime_secrets.py +43 -0
  417. package/src/runtime/invoice_stage.py +80 -0
  418. package/src/runtime/invoice_web.py +39 -0
  419. package/src/runtime/invoice_worker.py +50 -0
  420. package/src/runtime/lambda_faq.py +5 -0
  421. package/src/runtime/parser-requirements.lock +4 -0
  422. package/src/runtime/policy_review.py +18 -0
  423. package/src/runtime/postgres-qualification-lock.json +21 -0
  424. package/src/runtime/public-faq-sources.json +22 -0
  425. package/src/runtime/public_catalog_admin.py +122 -0
  426. package/src/runtime/public_catalog_store.py +125 -0
  427. package/src/runtime/public_faq.py +144 -0
  428. package/src/runtime/public_faq_app.py +49 -0
  429. package/src/runtime/python-container-lock.json +18 -0
  430. package/src/runtime/rag-sources.json +10 -0
  431. package/src/runtime/rag_acceptance.py +60 -0
  432. package/src/runtime/rag_aws_adapter.py +95 -0
  433. package/src/runtime/rds-ca-source.json +9 -0
  434. package/src/runtime/rds-global-ca.pem +2888 -0
  435. package/src/runtime/state-contract-check-source.json +4 -0
  436. package/src/runtime/state_contract_check.py +338 -0
  437. package/src/runtime/structured-source.json +1 -0
  438. package/src/runtime/structured-web-app.js +71 -0
  439. package/src/runtime/structured-web-source.json +1 -0
  440. package/src/runtime/structured_http.py +105 -0
  441. package/src/runtime/structured_output.py +48 -0
  442. package/src/runtime/web-python-lock.json +802 -0
  443. package/src/runtime/web_user_admin.py +44 -0
  444. package/src/runtime/workflow_application.py +62 -0
  445. package/src/runtime/workflow_bindings.py +128 -0
  446. package/src/runtime/workflow_configuration.py +40 -0
  447. package/src/runtime/workflow_connection.py +105 -0
  448. package/src/runtime/workflow_contract.py +54 -0
  449. package/src/runtime/workflow_database.py +94 -0
  450. package/src/runtime/workflow_errors.py +6 -0
  451. package/src/runtime/workflow_http.py +178 -0
  452. package/src/runtime/workflow_identity.py +69 -0
  453. package/src/runtime/workflow_payload.py +118 -0
  454. package/src/runtime/workflow_process_stage.py +88 -0
  455. package/src/runtime/workflow_profile.py +91 -0
  456. package/src/runtime/workflow_review.py +106 -0
  457. package/src/runtime/workflow_role_bootstrap.py +104 -0
  458. package/src/runtime/workflow_schema.sql +72 -0
  459. package/src/runtime/workflow_worker.py +142 -0
  460. package/src/runtime-settings.mjs +15 -0
  461. package/src/s3-access.mjs +14 -0
  462. package/src/source-contracts.mjs +17 -0
  463. package/src/spec-kit.mjs +186 -0
  464. package/src/state-custody.mjs +32 -0
  465. package/src/state-inspection.mjs +45 -0
  466. package/src/state-spec-kit.mjs +13 -0
  467. package/src/storage-tests.mjs +25 -0
  468. package/src/structured-api.mjs +133 -0
  469. package/src/structured-contract.mjs +19 -0
  470. package/src/structured-operation-policy.mjs +2 -0
  471. package/src/structured-web.mjs +17 -0
  472. package/src/style.css +1 -0
  473. package/src/system-description.mjs +26 -0
  474. package/src/terraform-admission.mjs +66 -0
  475. package/src/terraform-composition.mjs +83 -0
  476. package/src/terraform-expressions.mjs +46 -0
  477. package/src/terraform-validation-coverage.mjs +11 -0
  478. package/src/terraform.mjs +128 -0
  479. package/src/text-statistics.mjs +51 -0
  480. package/src/upstream/openarchflow/format-payload.ts +26 -0
  481. package/src/upstream/openarchflow/import/mermaid.ts +205 -0
  482. package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
  483. package/src/upstream/openarchflow/layout.ts +54 -0
  484. package/src/upstream/openarchflow/providers/aws.ts +196 -0
  485. package/src/upstream/openarchflow/providers/azure.ts +126 -0
  486. package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
  487. package/src/upstream/openarchflow/providers/generic.ts +227 -0
  488. package/src/upstream/openarchflow/providers/integration.ts +22 -0
  489. package/src/upstream/openarchflow/providers/types.ts +2 -0
  490. package/src/upstream/openarchflow/templates.ts +937 -0
  491. package/src/upstream-catalog.mjs +10 -0
  492. package/src/useChatScroll.jsx +36 -0
  493. package/src/useEmulation.jsx +36 -0
  494. package/src/validation-receipt.mjs +10 -0
  495. package/src/visual-export.mjs +92 -0
  496. package/src/voice-conversation.mjs +117 -0
  497. package/src/work-intake.mjs +24 -0
  498. package/src/workflow-runtime-inventory.mjs +3 -0
@@ -0,0 +1,275 @@
1
+ """Owned Google Batch per-file jobs; no source/model/SQL credentials in workloads.
2
+
3
+ The VM transfer identity is trusted, distinct from the question worker. Only the
4
+ parser runnable is network-blocked. Its mounts are explicit local POSIX paths;
5
+ no FUSE, identity mounts, background listener or shared parser service is used.
6
+ Cloud IAM/network/provisioning remain required parts of the generated composition.
7
+ """
8
+ import json
9
+ import hashlib
10
+ import re
11
+ import time
12
+ import uuid
13
+ from types import MappingProxyType
14
+ from urllib.parse import quote
15
+ import requests
16
+ from enterprise_document_contract import MAX_INPUT
17
+ from enterprise_document_job import MAX_ENVELOPE, request, result, unique
18
+ from enterprise_sources import SourceUnavailable
19
+ from workflow_connection import google_metadata_token
20
+ from enterprise_document_jobs import DocumentJobLedger
21
+
22
+
23
+ def configuration(value):
24
+ fields = {'format', 'projectId', 'region', 'bucket', 'parserImage', 'transferImage',
25
+ 'workerServiceAccount', 'vmServiceAccount', 'network', 'subnetwork'}
26
+ if not isinstance(value, dict) or set(value) != fields or value['format'] != 'enterprise-google-document-job-v1':
27
+ raise ValueError('Use the exact owned Google document-job configuration')
28
+ patterns = {'projectId': '[a-z][a-z0-9-]{4,28}[a-z0-9]', 'region': '[a-z]+-[a-z]+[1-9][0-9]?',
29
+ 'bucket': '[a-z0-9][a-z0-9-]{1,61}[a-z0-9]'}
30
+ for key, pattern in patterns.items():
31
+ if not isinstance(value[key], str) or not re.fullmatch(pattern, value[key]): raise ValueError('Invalid owned Google job destination')
32
+ project, region = value['projectId'], value['region']
33
+ for key in ['workerServiceAccount', 'vmServiceAccount']:
34
+ if not isinstance(value[key], str) or not re.fullmatch('[a-z][a-z0-9-]{4,28}[a-z0-9]@' + re.escape(project) + r'\.iam\.gserviceaccount\.com', value[key]):
35
+ raise ValueError('Use distinct existing owned worker and VM service accounts')
36
+ if value['workerServiceAccount'] == value['vmServiceAccount']: raise ValueError('Parser transfer VM must not inherit worker authority')
37
+ for key in ['parserImage', 'transferImage']:
38
+ if not isinstance(value[key], str) or not re.fullmatch(re.escape(region + '-docker.pkg.dev/' + project + '/') + r'[a-z][a-z0-9_-]{0,62}/[a-z][a-z0-9_-]{0,62}(?:/[a-z][a-z0-9_-]{0,62}){0,3}@sha256:[0-9a-f]{64}', value[key]):
39
+ raise ValueError('Use immutable owned regional Artifact Registry images')
40
+ for key, prefix in [('network', 'projects/' + project + '/global/networks/'),
41
+ ('subnetwork', 'projects/' + project + '/regions/' + region + '/subnetworks/')]:
42
+ if not isinstance(value[key], str) or not re.fullmatch(re.escape(prefix) + '[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?', value[key]):
43
+ raise ValueError('Use explicit same-project regional private networking')
44
+ return dict(value)
45
+
46
+
47
+ def identity(value):
48
+ if not isinstance(value, str) or not re.fullmatch('[0-9a-f]{64}', value): raise ValueError('Invalid owned per-file identity')
49
+ return value
50
+
51
+
52
+ def object_key(identifier, side):
53
+ if side not in {'input', 'result'}: raise ValueError('Use the owned input or result object')
54
+ return 'document-jobs/' + identity(identifier) + '/' + side
55
+
56
+
57
+ def transfer_metadata(config, metadata, generation):
58
+ if (not isinstance(metadata, dict) or set(metadata) != {'format', 'requestId', 'sha256', 'mediaType'} or
59
+ metadata['format'] != 'enterprise-document-job-v1' or not isinstance(metadata['sha256'], str) or
60
+ not re.fullmatch('[0-9a-f]{64}', metadata['sha256'])):
61
+ raise ValueError('Use the exact byte-bound document request')
62
+ identity(metadata['requestId'])
63
+ from enterprise_document_contract import media_type
64
+ if media_type(metadata['mediaType']) != metadata['mediaType']: raise ValueError('Use canonical document MIME')
65
+ if not isinstance(generation, str) or not re.fullmatch('[1-9][0-9]{0,29}', generation): raise ValueError('Use the immutable native input generation')
66
+ return {'format': 'enterprise-google-document-transfer-v1', 'projectId': config['projectId'],
67
+ 'region': config['region'], 'bucket': config['bucket'], 'request': dict(metadata), 'inputGeneration': generation}
68
+
69
+
70
+ def job_spec(config, metadata, generation):
71
+ owned = configuration(config)
72
+ transfer = transfer_metadata(owned, metadata, generation)
73
+ base = '/mnt/disks/share/document-' + metadata['requestId']
74
+ common = '--read-only --cap-drop=ALL --security-opt=no-new-privileges:true --pids-limit=128 --log-driver=none'
75
+ def bridge(mode, user):
76
+ return {'container': {'imageUri': owned['transferImage'], 'entrypoint': 'python',
77
+ 'commands': ['-I', '/app/enterprise_google_transfer.py', mode],
78
+ 'volumes': [base + ':/work:' + ('rw' if mode == 'download' else 'ro')],
79
+ 'options': common + ' --user=' + user + (' --cap-add=CHOWN' if mode == 'download' else '')},
80
+ 'environment': {'variables': {'DOCUMENT_JOB': json.dumps(transfer, separators=(',', ':'))}},
81
+ 'timeout': '25s'}
82
+ parser = {'container': {'imageUri': owned['parserImage'], 'entrypoint': 'python',
83
+ 'commands': ['-I', '/app/enterprise_document_cli.py', '--job'],
84
+ 'volumes': [base + '/input:/input:ro', base + '/output:/output:rw'],
85
+ 'options': common + ' --user=10001:10001 --tmpfs=/tmp:rw,noexec,nosuid,size=128m',
86
+ 'blockExternalNetwork': True}, 'timeout': '110s'}
87
+ return {'taskGroups': [{'taskCount': '1', 'parallelism': '1', 'taskCountPerNode': '1',
88
+ 'taskSpec': {'runnables': [bridge('download', '0:0'), parser, bridge('upload', '10001:10001')],
89
+ 'maxRetryCount': 0, 'maxRunDuration': '160s', 'computeResource': {'cpuMilli': '2000', 'memoryMib': '2048'}}}],
90
+ 'allocationPolicy': {'location': {'allowedLocations': ['regions/' + owned['region']]},
91
+ 'serviceAccount': {'email': owned['vmServiceAccount']},
92
+ 'network': {'networkInterfaces': [{'network': owned['network'], 'subnetwork': owned['subnetwork'], 'noExternalIpAddress': True}]},
93
+ 'instances': [{'policy': {'machineType': 'e2-standard-2', 'provisioningModel': 'STANDARD'}, 'blockProjectSshKeys': True}]},
94
+ 'logsPolicy': {'destination': 'DESTINATION_UNSPECIFIED'}, 'labels': {'atmem-workload': 'document-parser'}}
95
+
96
+
97
+ class GoogleDocumentApi:
98
+ def __init__(self, *, session=None, credential=None):
99
+ self.session = session or requests.Session(); self.session.trust_env = False
100
+ self.credential = credential or google_metadata_token
101
+
102
+ def send(self, method, url, *, body=None, data=None, params=None, limit=32768, allow_missing=False):
103
+ # Destinations are constructed internally, never supplied by a document,
104
+ # browser, model or source redirect. No environment proxies or redirects.
105
+ if not url.startswith(('https://batch.googleapis.com/v1/projects/', 'https://storage.googleapis.com/storage/v1/b/',
106
+ 'https://storage.googleapis.com/upload/storage/v1/b/')):
107
+ raise ValueError('Use the fixed native document APIs')
108
+ try:
109
+ token = self.credential()
110
+ if not isinstance(token, str) or not 8 <= len(token) <= 20000 or any(ord(c) < 33 or ord(c) > 126 for c in token): raise ValueError()
111
+ started = time.monotonic()
112
+ headers = {'Authorization': 'Bearer ' + token}
113
+ if data is not None: headers['Content-Type'] = 'application/octet-stream'
114
+ with self.session.request(method, url, headers=headers, json=body, data=data, params=params,
115
+ stream=True, allow_redirects=False, timeout=(2, 8)) as response:
116
+ if allow_missing and response.status_code == 404: return None
117
+ if response.status_code not in {200, 201, 204}: raise ValueError()
118
+ raw = bytearray()
119
+ for chunk in response.iter_content(16384):
120
+ raw.extend(chunk)
121
+ if len(raw) > limit or time.monotonic() - started > 15: raise ValueError()
122
+ return bytes(raw)
123
+ except Exception:
124
+ raise SourceUnavailable('document_job_unavailable') from None
125
+
126
+ def json(self, *args, **kwargs):
127
+ raw = self.send(*args, **kwargs)
128
+ if raw is None: return None
129
+ try:
130
+ value = json.loads(raw, object_pairs_hook=unique)
131
+ if not isinstance(value, dict): raise ValueError()
132
+ return value
133
+ except Exception: raise SourceUnavailable('document_job_unavailable') from None
134
+
135
+
136
+ class GoogleDocumentObjects:
137
+ def __init__(self, bucket, api):
138
+ if not isinstance(bucket, str) or not re.fullmatch('[a-z0-9][a-z0-9-]{1,61}[a-z0-9]', bucket): raise ValueError('Use the owned ephemeral bucket')
139
+ self.bucket, self.api = bucket, api
140
+ self.base = 'https://storage.googleapis.com/storage/v1/b/' + bucket
141
+
142
+ def url(self, identifier, side): return self.base + '/o/' + quote(object_key(identifier, side), safe='')
143
+
144
+ def put(self, identifier, side, raw):
145
+ maximum = MAX_INPUT if side == 'input' else MAX_ENVELOPE
146
+ if not isinstance(raw, bytes) or not 0 < len(raw) <= maximum: raise ValueError('Invalid bounded document object')
147
+ value = self.api.json('POST', 'https://storage.googleapis.com/upload/storage/v1/b/' + self.bucket + '/o',
148
+ params={'uploadType': 'media', 'name': object_key(identifier, side), 'ifGenerationMatch': '0', 'fields': 'generation'}, data=raw)
149
+ generation = value.get('generation')
150
+ if not isinstance(generation, str) or not re.fullmatch('[1-9][0-9]{0,29}', generation): raise SourceUnavailable('document_job_unavailable')
151
+ return generation
152
+
153
+ def load(self, identifier, side, generation=None):
154
+ params = {'alt': 'media'}
155
+ if generation is not None:
156
+ if not isinstance(generation, str) or not re.fullmatch('[1-9][0-9]{0,29}', generation): raise ValueError('Invalid native generation')
157
+ params['generation'] = generation
158
+ return self.api.send('GET', self.url(identifier, side), params=params, limit=MAX_INPUT if side == 'input' else MAX_ENVELOPE)
159
+
160
+ def delete(self, identifier, side): self.api.send('DELETE', self.url(identifier, side), allow_missing=True)
161
+
162
+
163
+ class GoogleBatchDocuments:
164
+ """Controller lifecycle; successful delete requests alone do not prove exit."""
165
+ def __init__(self, config, *, api=None, ledger=None, deadline=140, poll_seconds=1):
166
+ self.config = MappingProxyType(configuration(config))
167
+ if type(deadline) is not int or not 1 <= deadline <= 140 or type(poll_seconds) not in {int, float} or not .01 <= poll_seconds <= 2:
168
+ raise ValueError('Use bounded owned document deadlines')
169
+ self.api = api or GoogleDocumentApi()
170
+ self.objects = GoogleDocumentObjects(self.config['bucket'], self.api)
171
+ self.deadline, self.poll_seconds = deadline, poll_seconds
172
+ self.parent = 'projects/' + self.config['projectId'] + '/locations/' + self.config['region']
173
+ self.base = 'https://batch.googleapis.com/v1/' + self.parent
174
+ self.target = hashlib.sha256(json.dumps({key:self.config[key]for key in ['projectId','region','bucket']},sort_keys=True,separators=(',',':')).encode()).hexdigest()
175
+ self.ledger = ledger
176
+
177
+ def __call__(self, raw, mime):
178
+ raise SourceUnavailable('source_validation_required')
179
+
180
+ def bind_runtime(self, provider, environment):
181
+ if (provider != 'gcp' or environment.get('PROJECT_ID') != self.config['projectId'] or
182
+ environment.get('REGION') != self.config['region'] or
183
+ environment.get('DATABASE_SERVICE_ACCOUNT') != self.config['workerServiceAccount']):
184
+ raise ValueError('Document execution must match the selected provider, project, region and worker identity')
185
+
186
+ def attach_runtime(self, auth, account_id):
187
+ if self.ledger is None:self.ledger=DocumentJobLedger(auth,account_id,self.target)
188
+ elif (not isinstance(self.ledger,DocumentJobLedger)or self.ledger.auth is not auth or
189
+ self.ledger.account_id!=account_id or self.ledger.target!=self.target):
190
+ raise ValueError('Use the owned persistent worker cleanup ledger')
191
+
192
+ def reconcile(self, *, limit=5):
193
+ if self.ledger is None:raise SourceUnavailable('document_job_recovery_required')
194
+ if type(limit)is not int or not 1<=limit<=20:raise ValueError('Use a bounded cleanup batch')
195
+ counts={'claimed':0,'retired':0,'settling':0,'failed':0}
196
+ for _ in range(limit):
197
+ lease=self.ledger.claim_due()
198
+ if lease is None:break
199
+ counts['claimed']+=1
200
+ try:
201
+ self.cleanup(self.parent+'/jobs/document-'+lease.request_id[:32],lease.request_id)
202
+ except Exception:
203
+ self.ledger.failed(lease);counts['failed']+=1
204
+ else:
205
+ retired=self.ledger.completed(lease);counts['retired'if retired else'settling']+=1
206
+ return counts
207
+
208
+ def from_source(self, raw, mime, guard):
209
+ if not callable(guard): raise ValueError('Supply the current source/company/job check')
210
+ identifier = uuid.uuid4().hex + uuid.uuid4().hex
211
+ metadata = request(raw, mime, identifier)
212
+ name = self.parent + '/jobs/document-' + identifier[:32]
213
+ url = 'https://batch.googleapis.com/v1/' + name
214
+ started = time.monotonic()
215
+ guard()
216
+ if self.ledger is None:raise SourceUnavailable('document_job_recovery_required')
217
+ lease=self.ledger.allocate(identifier)
218
+ def check():
219
+ self.ledger.guard(lease);guard();self.ledger.guard(lease)
220
+ # Cleanup always inspects this unique owned name, including lost create
221
+ # acknowledgements. It never lists/deletes another project's jobs/files.
222
+ try:
223
+ self.ledger.guard(lease)
224
+ generation = self.objects.put(identifier, 'input', raw)
225
+ self.ledger.advance(lease,'job_pending')
226
+ check()
227
+ value = self.api.json('POST', self.base + '/jobs', params={'jobId': name.rsplit('/', 1)[1]},
228
+ body=job_spec(dict(self.config), metadata, generation))
229
+ if value.get('name') != name: raise SourceUnavailable('document_job_unavailable')
230
+ self.ledger.advance(lease,'job_running')
231
+ while True:
232
+ check()
233
+ if time.monotonic() - started > self.deadline: raise SourceUnavailable('document_job_unavailable')
234
+ current = self.api.json('GET', url)
235
+ if current.get('name') != name: raise SourceUnavailable('document_job_unavailable')
236
+ state = current.get('status', {}).get('state')
237
+ if state == 'SUCCEEDED': break
238
+ if state not in {'QUEUED', 'SCHEDULED', 'RUNNING'}: raise SourceUnavailable('document_job_unavailable')
239
+ time.sleep(self.poll_seconds)
240
+ self.ledger.advance(lease,'succeeded')
241
+ check()
242
+ encoded = self.objects.load(identifier, 'result')
243
+ value = result(raw, mime, identifier, encoded)
244
+ check()
245
+ return value
246
+ finally:
247
+ cleanup=self.ledger.claim_owner(lease)
248
+ try:self.cleanup(name,identifier)
249
+ except BaseException:
250
+ self.ledger.failed(cleanup)
251
+ raise
252
+ else:self.ledger.completed(cleanup)
253
+
254
+ def cleanup(self, name, identifier):
255
+ failure = False
256
+ try:
257
+ operation = self.api.json('DELETE', 'https://batch.googleapis.com/v1/' + name, allow_missing=True)
258
+ if operation is not None:
259
+ expected = self.parent + '/operations/'
260
+ op = operation.get('name')
261
+ if not isinstance(op, str) or not re.fullmatch(re.escape(expected) + '[A-Za-z0-9_-]{1,200}', op): raise ValueError()
262
+ deadline = time.monotonic() + 30
263
+ while True:
264
+ if type(operation.get('done', False)) is not bool: raise ValueError()
265
+ if operation.get('done', False): break
266
+ if time.monotonic() > deadline: raise ValueError()
267
+ time.sleep(self.poll_seconds)
268
+ operation = self.api.json('GET', 'https://batch.googleapis.com/v1/' + op)
269
+ if operation.get('name') != op: raise ValueError()
270
+ if operation.get('error'): raise ValueError()
271
+ except Exception: failure = True
272
+ for side in ['input', 'result']:
273
+ try: self.objects.delete(identifier, side)
274
+ except Exception: failure = True
275
+ if failure: raise SourceUnavailable('document_job_cleanup_unavailable') from None
@@ -0,0 +1,85 @@
1
+ """Trusted, sequential Google Batch byte transfer; never execute a document.
2
+
3
+ Only the download/upload runnables have native VM network/identity access. They
4
+ use fixed native APIs and one bounded per-file mount. The parser never runs in
5
+ this image, and no service is left listening for it.
6
+ """
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+ import sys
11
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
12
+ from enterprise_document_contract import MAX_INPUT
13
+ from enterprise_document_job import MAX_ENVELOPE, read_file, request, result, unique
14
+ from enterprise_google_jobs import GoogleDocumentApi, GoogleDocumentObjects, transfer_metadata
15
+
16
+
17
+ def metadata(encoded):
18
+ if not isinstance(encoded, str) or not 0 < len(encoded.encode()) <= 4096: raise ValueError('Invalid owned transfer metadata')
19
+ value = json.loads(encoded, object_pairs_hook=unique)
20
+ fields = {'format', 'projectId', 'region', 'bucket', 'request', 'inputGeneration'}
21
+ if not isinstance(value, dict) or set(value) != fields or value['format'] != 'enterprise-google-document-transfer-v1': raise ValueError('Invalid owned transfer metadata')
22
+ import re
23
+ for key, pattern in {'projectId': '[a-z][a-z0-9-]{4,28}[a-z0-9]', 'region': '[a-z]+-[a-z]+[1-9][0-9]?', 'bucket': '[a-z0-9][a-z0-9-]{1,61}[a-z0-9]'}.items():
24
+ if not isinstance(value[key], str) or not re.fullmatch(pattern, value[key]): raise ValueError('Invalid owned transfer destination')
25
+ if value != transfer_metadata(value, value['request'], value['inputGeneration']): raise ValueError('Invalid owned transfer metadata')
26
+ return value
27
+
28
+
29
+ def directory(parent, name, *, create=False):
30
+ if create: os.mkdir(name, 0o700, dir_fd=parent)
31
+ return os.open(name, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=parent)
32
+
33
+
34
+ def download(value, objects, mount='/work'):
35
+ """Root/CHOWN only to prepare isolated UID10001 mounts, then this exits."""
36
+ expected = value['request']; identifier = expected['requestId']
37
+ raw = objects.load(identifier, 'input', value['inputGeneration'])
38
+ if request(raw, expected['mediaType'], identifier) != expected: raise ValueError('Document input binding changed')
39
+ root = source = target = None
40
+ try:
41
+ root = os.open(mount, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
42
+ # Refuse previous per-file state, even if contents appear compatible.
43
+ source = directory(root, 'input', create=True)
44
+ target = directory(root, 'output', create=True)
45
+ for name, content in [('document', raw), ('request.json', json.dumps(expected, separators=(',', ':')).encode())]:
46
+ fd = os.open(name, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o400, dir_fd=source)
47
+ with os.fdopen(fd, 'wb') as stream:
48
+ stream.write(content); stream.flush(); os.fsync(stream.fileno()); os.fchown(stream.fileno(), 10001, 10001)
49
+ os.fchmod(source, 0o500); os.fchown(source, 10001, 10001); os.fchown(target, 10001, 10001)
50
+ os.fsync(source); os.fsync(target); os.fsync(root)
51
+ finally:
52
+ for fd in (target, source, root):
53
+ if fd is not None: os.close(fd)
54
+
55
+
56
+ def upload(value, objects, mount='/work'):
57
+ root = source = target = None
58
+ try:
59
+ root = os.open(mount, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
60
+ source, target = directory(root, 'input'), directory(root, 'output')
61
+ raw = read_file(source, 'document', MAX_INPUT)
62
+ expected = value['request']
63
+ if request(raw, expected['mediaType'], expected['requestId']) != expected: raise ValueError('Document input binding changed')
64
+ encoded = read_file(target, 'result.json', MAX_ENVELOPE)
65
+ result(raw, expected['mediaType'], expected['requestId'], encoded)
66
+ objects.put(expected['requestId'], 'result', encoded)
67
+ finally:
68
+ for fd in (target, source, root):
69
+ if fd is not None: os.close(fd)
70
+
71
+
72
+ def main():
73
+ # No document content, token, upstream response or traceback enters job logs.
74
+ try:
75
+ if sys.argv[1:] not in [['download'], ['upload']]: raise ValueError()
76
+ mode = sys.argv[1]
77
+ if os.getuid() != (0 if mode == 'download' else 10001): raise ValueError()
78
+ value = metadata(os.environ['DOCUMENT_JOB'])
79
+ objects = GoogleDocumentObjects(value['bucket'], GoogleDocumentApi())
80
+ (download if mode == 'download' else upload)(value, objects)
81
+ except Exception:
82
+ raise SystemExit(2) from None
83
+
84
+
85
+ if __name__ == '__main__': main()
@@ -0,0 +1,212 @@
1
+ """Company-session API and native source consent callbacks.
2
+
3
+ Existing company login supplies the verified API bearer token at bootstrap. The
4
+ browser cannot choose an account, source account, model, URL or permission mapping.
5
+ Native secrets/model/decoding/SQL transports are separate owned adapters.
6
+ """
7
+ from dataclasses import dataclass
8
+ import html
9
+ import json
10
+ import re
11
+ from urllib.parse import parse_qs,urlsplit
12
+ from fastapi import FastAPI,Request
13
+ from fastapi.responses import JSONResponse,HTMLResponse,RedirectResponse,Response
14
+ from starlette.concurrency import run_in_threadpool
15
+ from enterprise_identity import trusted_https
16
+ from enterprise_sources import SourceDenied,SourceUnavailable,checked_url
17
+ from public_faq import InvalidAnswer,object_pairs
18
+
19
+ COOKIE='__Host-enterprise-session'
20
+ CSRF='x-csrf-token'
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class SourceConnection:
25
+ label: str
26
+ kind: str
27
+ driver: object
28
+
29
+
30
+ def problem(code,status):
31
+ messages={'signin':'Sign in with your company account to continue.','forbidden':'Your account cannot access this workspace. Ask your workspace owner for access.','request':'Check your question or refresh this page and try again.','setup':'Your workspace owner needs to enable a company source for your account.','source':'Connect your company sources before asking a question.','busy':'Your source connection is updating. Try again in a moment.','capacity':'You already have questions processing. Wait for one to finish or cancel it before asking another.','unavailable':'Your workspace could not finish this answer. Please try again shortly.','answer':'This answer could not be verified against your current sources. Please try again.'}
32
+ return JSONResponse({'error':{'code':code,'message':messages[code]}},status_code=status)
33
+
34
+
35
+ def callback_notice(label):
36
+ title='We could not finish connecting '+label
37
+ return HTMLResponse('<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>'+html.escape(title)+'</title><main><h1>'+html.escape(title)+'</h1><p>Return to your workspace, sign in if needed, and choose Connect again.</p><a href="/">Return to your workspace</a></main></html>',status_code=400)
38
+
39
+
40
+ def create_app(identity,sessions,connections,make_engine,*,public_origin,web_config=None,static_root=None,answers=None,allow_synchronous=True):
41
+ if type(allow_synchronous)is not bool:raise ValueError('Use the owned question transport mode')
42
+ trusted_https(public_origin);origin=urlsplit(public_origin)
43
+ if origin.path not in {'','/'}or public_origin.endswith('/'):raise ValueError('Use the canonical owned HTTPS origin without a trailing slash')
44
+ if not isinstance(connections,dict)or not 1<=len(connections)<=20 or not callable(make_engine):raise ValueError('Supply owned native source and application adapters')
45
+ grants=set()
46
+ for alias,entry in connections.items():
47
+ if not isinstance(alias,str)or not re.fullmatch(r'[a-z][a-z0-9_-]{0,31}',alias)or not isinstance(entry,SourceConnection)or entry.kind not in {'graph','confluence'}or not isinstance(entry.label,str)or not 1<=len(entry.label)<=80:raise ValueError('Invalid configured source connection')
48
+ driver=entry.driver
49
+ if driver.store is not sessions.auth or driver.redirect!=public_origin+'/oauth/callback/'+alias:raise ValueError('Source consent must use the owned store and exact callback')
50
+ key=(entry.kind,driver.client,getattr(driver,'tenant',None))
51
+ if key in grants:raise ValueError('Use one consent/cache per vendor grant and share it across its document sources')
52
+ grants.add(key)
53
+ app=FastAPI(docs_url=None,redoc_url=None,openapi_url=None)
54
+ callback_paths={'/oauth/callback/'+alias for alias in connections}
55
+ auth=sessions.auth
56
+ if answers is not None and(answers.store.auth is not auth or answers.store.account_id!=identity.account_id or answers.connections!=connections):raise ValueError('Queued answers must use the same owned account, authority and sources')
57
+ csp="default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'"
58
+ if web_config is not None:
59
+ if answers is None:raise ValueError('The browser workspace requires the durable question service')
60
+ from enterprise_web import install_web
61
+ csp=install_web(app,identity,connections,web_config,static_root)
62
+ def cookie(request):
63
+ fields=[part.strip().split('=',1)for header in request.headers.getlist('cookie')for part in header.split(';')]
64
+ matches=[value for field,*value in fields if field==COOKIE]
65
+ if len(matches)>1:raise SourceDenied('company_identity_required')
66
+ return request.cookies.get(COOKIE,'')
67
+ async def actor(request,*,csrf=False):
68
+ session=await run_in_threadpool(sessions.authenticate,cookie(request))
69
+ if session.principal.account_id!=identity.account_id:raise SourceDenied('company_identity_required')
70
+ if csrf:
71
+ if request.headers.get('origin')!=public_origin:raise SourceDenied('browser_request_denied')
72
+ session.require_csrf(request.headers.get(CSRF,''))
73
+ return session
74
+ async def body(request,limit=8192):
75
+ data=bytearray()
76
+ async for chunk in request.stream():
77
+ data.extend(chunk)
78
+ if len(data)>limit:raise ValueError('Body exceeds the owned request limit')
79
+ return bytes(data)
80
+ async def json_body(request):
81
+ if request.headers.get('content-type','').split(';')[0].strip().lower()!='application/json':raise ValueError('Use JSON')
82
+ value=json.loads(await body(request),object_pairs_hook=object_pairs)
83
+ if not isinstance(value,dict):raise ValueError('Use an object')
84
+ return value
85
+ def source(alias):
86
+ if alias not in connections:raise ValueError('Unknown source')
87
+ return connections[alias]
88
+ @app.middleware('http')
89
+ async def boundary(request,next_handler):
90
+ try:
91
+ callback=request.url.path in callback_paths
92
+ supplied=request.headers.get('origin')
93
+ if not callback and supplied and supplied!=public_origin:response=problem('forbidden',403)
94
+ else:response=await next_handler(request)
95
+ except SourceDenied as error:
96
+ code=str(error)
97
+ if code in {'company_identity_required','company_identity_expired'}:response=problem('signin',401)
98
+ elif code=='source_consent_unavailable':response=problem('unavailable',503)
99
+ elif code=='source_mapping_required':response=problem('setup',403)
100
+ elif code in {'source_reconnection_required','source_consent_required'}:response=problem('source',409)
101
+ else:response=problem('forbidden',403)
102
+ except SourceUnavailable as error:response=problem('capacity',429)if str(error)=='query_capacity_reached'else problem('busy'if str(error)=='source_refresh_in_progress'else'unavailable',503)
103
+ except InvalidAnswer:response=problem('answer',503)
104
+ except(ValueError,UnicodeError,RecursionError):response=problem('request',400)
105
+ except Exception:response=problem('unavailable',503)
106
+ response.headers.update({'Cache-Control':'no-store','Pragma':'no-cache','X-Content-Type-Options':'nosniff','Referrer-Policy':'no-referrer','Strict-Transport-Security':'max-age=31536000','Content-Security-Policy':csp})
107
+ return response
108
+ @app.get('/healthz')
109
+ def health():return {'status':'running'}
110
+ @app.post('/api/session')
111
+ async def bootstrap(request:Request):
112
+ if request.headers.get('origin')!=public_origin:raise SourceDenied('browser_request_denied')
113
+ if await json_body(request)!={}:raise ValueError('No caller identity fields')
114
+ header=request.headers.get('authorization','')
115
+ principal=await run_in_threadpool(identity.verify,header[7:]if header.startswith('Bearer ')else'')
116
+ created=await run_in_threadpool(sessions.create,principal,cookie(request))
117
+ response=JSONResponse({'signedIn':True,'csrfToken':created.csrf})
118
+ response.set_cookie(COOKIE,created.token,max_age=created.max_age,secure=True,httponly=True,samesite='none',path='/')
119
+ return response
120
+ @app.get('/api/session')
121
+ async def describe(request:Request):
122
+ # Non-simple header blocks cross-site scripts/tags from retrieving CSRF.
123
+ # No CORS capability is granted to another origin.
124
+ if request.headers.get('x-workspace-request')!='session':raise SourceDenied('browser_request_denied')
125
+ user=await actor(request)
126
+ rows=[]
127
+ for alias,entry in connections.items():rows.append({'id':alias,'name':entry.label,'status':await run_in_threadpool(auth.connection_status,user.principal,entry.driver.source_id)})
128
+ return {'signedIn':True,'csrfToken':user.csrf,'sources':rows}
129
+ @app.delete('/api/session')
130
+ async def logout(request:Request):
131
+ user=await actor(request,csrf=True);await run_in_threadpool(sessions.revoke,user)
132
+ response=Response(status_code=204);response.delete_cookie(COOKIE,path='/',secure=True,httponly=True,samesite='none');return response
133
+ @app.post('/api/sources/{alias}/connect')
134
+ async def connect(alias:str,request:Request):
135
+ user=await actor(request,csrf=True);entry=source(alias)
136
+ if await json_body(request)!={}:raise ValueError('No caller source or callback settings')
137
+ if await run_in_threadpool(auth.connection_status,user.principal,entry.driver.source_id)=='needs_setup':raise SourceDenied('source_mapping_required')
138
+ start=await run_in_threadpool(entry.driver.start,user.principal,user.browser_nonce)
139
+ checked_url(start.url,{'login.microsoftonline.com'if entry.kind=='graph'else'auth.atlassian.com'})
140
+ return {'authorizationUrl':start.url}
141
+ @app.delete('/api/sources/{alias}')
142
+ async def disconnect(alias:str,request:Request):
143
+ user=await actor(request,csrf=True);entry=source(alias);await run_in_threadpool(auth.disconnect,user.principal,entry.driver.source_id);return Response(status_code=204)
144
+ @app.api_route('/oauth/callback/{alias}',methods=['GET','POST'])
145
+ async def callback(alias:str,request:Request):
146
+ entry=source(alias)
147
+ try:
148
+ if request.method!=('POST'if entry.kind=='graph'else'GET'):raise ValueError('Incorrect consent response method')
149
+ user=await actor(request)
150
+ if request.method=='POST':
151
+ if request.headers.get('content-type','').split(';')[0].strip().lower()!='application/x-www-form-urlencoded':raise ValueError('Incorrect consent content type')
152
+ params=parse_qs((await body(request,16384)).decode('utf-8'),keep_blank_values=True,strict_parsing=True,max_num_fields=12)
153
+ else:
154
+ if len(request.url.query)>16384:raise ValueError('Consent response too large')
155
+ params=parse_qs(request.url.query,keep_blank_values=True,strict_parsing=True,max_num_fields=12)
156
+ if any(len(values)!=1 for values in params.values())or not params.get('state'):raise ValueError('Invalid consent response')
157
+ if params.get('error'):
158
+ await run_in_threadpool(auth.consume,user.principal,entry.driver.source_id,user.browser_nonce,params['state'][0]);return callback_notice(entry.label)
159
+ if not params.get('code'):raise ValueError('Missing consent code')
160
+ await run_in_threadpool(entry.driver.finish,user.principal,user.browser_nonce,params['state'][0],params['code'][0])
161
+ return RedirectResponse('/?connection=connected&source='+alias,status_code=303)
162
+ except Exception:return callback_notice(entry.label)
163
+ @app.post('/api/ask')
164
+ async def ask(request:Request):
165
+ if not allow_synchronous:return JSONResponse({'error':{'code':'request','message':'Use the workspace to submit your question.'}},status_code=410)
166
+ user=await actor(request,csrf=True);value=await json_body(request)
167
+ if set(value)!={'question'}or not isinstance(value['question'],str)or not value['question'].strip()or len(value['question'])>1000:raise ValueError('Supply only the question')
168
+ def answer():
169
+ def authorize(subject):
170
+ if subject!=user.principal.key:raise SourceDenied('company_identity_required')
171
+ sessions.authenticate(cookie(request))
172
+ from enterprise_knowledge import current_engine
173
+ engine=current_engine(auth,connections,user.principal,make_engine,authorize)
174
+ result=engine.answer(value['question'],user.principal.key)
175
+ # Independent final company-session gate, beyond source document gates.
176
+ sessions.authenticate(cookie(request))
177
+ return result
178
+ return await run_in_threadpool(answer)
179
+ @app.post('/api/queries')
180
+ async def submit_question(request:Request):
181
+ user=await actor(request,csrf=True);value=await json_body(request)
182
+ if answers is None:raise SourceUnavailable('question_worker_unavailable')
183
+ if set(value)!={'question','requestId'}or not isinstance(value['question'],str)or not value['question'].strip()or len(value['question'])>1000:raise ValueError('Supply a question and request identity')
184
+ from enterprise_queries import QueryConflict
185
+ try:identity=await run_in_threadpool(answers.store.submit,user,value['question'],value['requestId'])
186
+ except QueryConflict:return JSONResponse({'error':{'code':'request','message':'This question could not be submitted. Please send it again.'}},status_code=409)
187
+ return JSONResponse({'id':identity,'status':'queued'},status_code=202)
188
+ @app.get('/api/queries/{query_id}')
189
+ async def question_status(query_id:str,request:Request):
190
+ if request.headers.get('x-workspace-request')!='session':raise SourceDenied('browser_request_denied')
191
+ user=await actor(request)
192
+ if answers is None:raise SourceUnavailable('question_worker_unavailable')
193
+ from enterprise_queries import QueryMissing
194
+ try:result=await run_in_threadpool(answers.status,user,query_id)
195
+ except QueryMissing:return JSONResponse({'error':{'code':'request','message':'This question is no longer available.'}},status_code=404)
196
+ await run_in_threadpool(sessions.authenticate,cookie(request))
197
+ return result
198
+ @app.get('/api/queries')
199
+ async def pending_questions(request:Request):
200
+ if request.headers.get('x-workspace-request')!='session':raise SourceDenied('browser_request_denied')
201
+ user=await actor(request)
202
+ if answers is None:raise SourceUnavailable('question_worker_unavailable')
203
+ return {'queries':await run_in_threadpool(answers.store.pending,user)}
204
+ @app.delete('/api/queries/{query_id}')
205
+ async def cancel_question(query_id:str,request:Request):
206
+ user=await actor(request,csrf=True)
207
+ if answers is None:raise SourceUnavailable('question_worker_unavailable')
208
+ from enterprise_queries import QueryMissing
209
+ try:await run_in_threadpool(answers.store.cancel,user,query_id)
210
+ except QueryMissing:return JSONResponse({'error':{'code':'request','message':'This question is no longer available.'}},status_code=404)
211
+ return Response(status_code=204)
212
+ return app
@@ -0,0 +1,52 @@
1
+ """Existing company API identity; no replacement IdP or source-token claims.
2
+
3
+ Issuer, API audience, JWKS URL, delegated scope and account are operator-owned
4
+ configuration. Live membership remains a separate required database check.
5
+ """
6
+ from dataclasses import dataclass,field
7
+ import hashlib
8
+ import re
9
+ import time
10
+ from urllib.parse import urlsplit
11
+ import jwt
12
+ from enterprise_sources import SourceDenied
13
+
14
+
15
+ def trusted_https(value):
16
+ try:parts=urlsplit(value);port=parts.port
17
+ except (TypeError,ValueError):raise ValueError('Invalid owned identity endpoint')from None
18
+ if parts.scheme!='https'or not parts.hostname or (port is not None and not 1<=port<=65535)or parts.username or parts.password or parts.query or parts.fragment or any(ord(c)<33 for c in value):raise ValueError('Invalid owned identity endpoint')
19
+ return value
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class CompanyPrincipal:
24
+ account_id: str
25
+ issuer: str
26
+ subject: str = field(repr=False)
27
+ expires_at: int
28
+
29
+ @property
30
+ def key(self):return hashlib.sha256((self.issuer+'\0'+self.subject).encode()).hexdigest()
31
+
32
+ def require(self):
33
+ if type(self.expires_at)is not int or self.expires_at<=time.time()+5:raise SourceDenied('company_identity_expired')
34
+
35
+
36
+ class CompanyIdentity:
37
+ def __init__(self,account_id,issuer,audience,jwks_uri,scope='knowledge.read',*,jwks=None):
38
+ if not isinstance(account_id,str)or not re.fullmatch(r'[a-zA-Z0-9_-]{1,128}',account_id):raise ValueError('Use the owned company account')
39
+ if not isinstance(audience,str)or not 1<=len(audience)<=300 or not isinstance(scope,str)or not re.fullmatch(r'[A-Za-z0-9_./:-]{1,200}',scope):raise ValueError('Use the owned API audience and delegated scope')
40
+ self.account_id=account_id;self.issuer=trusted_https(issuer);self.audience=audience;self.scope=scope
41
+ self.jwks_uri=trusted_https(jwks_uri)
42
+ self.jwks=jwks or jwt.PyJWKClient(self.jwks_uri,timeout=3,lifespan=300,cache_keys=False)
43
+
44
+ def verify(self,token):
45
+ try:
46
+ if not isinstance(token,str)or not 1<=len(token)<=16384:raise ValueError('Invalid token')
47
+ key=self.jwks.get_signing_key_from_jwt(token).key
48
+ claims=jwt.decode(token,key,algorithms=['RS256'],issuer=self.issuer,audience=self.audience,options={'require':['iss','sub','aud','exp','iat']})
49
+ scope=claims.get('scp',claims.get('scope'))
50
+ if not isinstance(scope,str)or self.scope not in scope.split()or claims['aud']!=self.audience or not isinstance(claims['sub'],str)or not 1<=len(claims['sub'].encode())<=255 or any(ord(c)<32 for c in claims['sub'])or any(type(claims[k])is not int for k in ['iat','exp'])or not 0<claims['exp']-claims['iat']<=3600:raise ValueError('Invalid company API credential')
51
+ principal=CompanyPrincipal(self.account_id,self.issuer,claims['sub'],claims['exp']);principal.require();return principal
52
+ except Exception:raise SourceDenied('company_identity_required')from None