@inneranimalmedia/agentsam-sdk 2.6.5 → 2.6.8

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 (400) hide show
  1. package/AGENTSAM.md +29 -0
  2. package/README.md +37 -0
  3. package/apps/README.md +5 -0
  4. package/apps/cad-creator/README.md +4 -4
  5. package/apps/cad-creator/shared/cad/package.json +1 -1
  6. package/apps/client-cms-editor/AGENTS.md +12 -7
  7. package/apps/client-cms-editor/README.md +76 -48
  8. package/apps/client-cms-editor/agentsam.app.json +26 -9
  9. package/apps/client-cms-editor/backend/package.json +20 -3
  10. package/apps/client-cms-editor/backend/src/api/client.ts +134 -101
  11. package/apps/client-cms-editor/backend/src/index.ts +1 -1
  12. package/apps/client-cms-editor/backend/src/model.ts +11 -115
  13. package/apps/client-cms-editor/backend/src/preview/bridge.ts +1 -1
  14. package/apps/client-cms-editor/backend/tsconfig.json +6 -1
  15. package/apps/client-cms-editor/bin/agentsam-cms.js +262 -0
  16. package/apps/client-cms-editor/bin/agentsam-cms.mjs +2 -228
  17. package/apps/client-cms-editor/frontend/package.json +16 -7
  18. package/apps/client-cms-editor/frontend/src/AgentSamDrawer.tsx +9 -41
  19. package/apps/client-cms-editor/frontend/src/CmsAgentSurface.tsx +19 -174
  20. package/apps/client-cms-editor/frontend/src/adapters/heuristic.ts +29 -0
  21. package/apps/client-cms-editor/frontend/src/adapters/http.ts +628 -0
  22. package/apps/client-cms-editor/frontend/src/adapters/memory.ts +510 -0
  23. package/apps/client-cms-editor/frontend/src/editor/CmsEditor.tsx +213 -0
  24. package/apps/client-cms-editor/frontend/src/editor/CmsEditorProvider.tsx +20 -0
  25. package/apps/client-cms-editor/frontend/src/editor/canvas/PreviewCanvas.tsx +53 -0
  26. package/apps/client-cms-editor/frontend/src/{CmsEditor.tsx → editor/legacy/CmsEditorPrototype.tsx} +52 -23
  27. package/apps/client-cms-editor/frontend/src/editor/useCmsEditorController.ts +218 -0
  28. package/apps/client-cms-editor/frontend/src/index.ts +19 -5
  29. package/apps/client-cms-editor/frontend/src/lib/agent-host.ts +24 -0
  30. package/apps/client-cms-editor/frontend/src/main.tsx +82 -8
  31. package/apps/client-cms-editor/frontend/src/mount.tsx +92 -30
  32. package/apps/client-cms-editor/frontend/src/styles/agentsam-drawer.css +1 -1
  33. package/apps/client-cms-editor/frontend/src/styles/cmsShell.css +8 -8
  34. package/apps/client-cms-editor/frontend/src/styles/studio.css +55 -3
  35. package/apps/client-cms-editor/frontend/tsconfig.json +12 -1
  36. package/apps/client-cms-editor/frontend/vite.config.ts +12 -0
  37. package/apps/client-cms-editor/package-lock.json +973 -134
  38. package/apps/client-cms-editor/package.json +118 -6
  39. package/apps/client-cms-editor/reference/harvest/HARVEST_RECEIPT.json +201 -0
  40. package/apps/client-cms-editor/reference/harvest/PROVENANCE_HISTORY.txt +10 -0
  41. package/apps/client-cms-editor/reference/harvest/README.md +8 -0
  42. package/apps/client-cms-editor/reference/harvest/plans/ARCHIVE-INTAKE.md +135 -0
  43. package/apps/client-cms-editor/reference/harvest/plans/CHATGPT-IDENTITY-HARVEST.md +83 -0
  44. package/apps/client-cms-editor/reference/harvest/plans/CMS-EDITOR-HARVEST.md +166 -0
  45. package/apps/client-cms-editor/reference/harvest/plans/HARVEST-INDEX.md +196 -0
  46. package/apps/client-cms-editor/reference/harvest/plans/INNERANIMALS-CONTENT-001.md +137 -0
  47. package/apps/client-cms-editor/reference/harvest/plans/NO-IMPLICIT-SIDE-EFFECTS.md +87 -0
  48. package/apps/client-cms-editor/reference/harvest/plans/STOREFRONT-SURFACE-MAP.md +140 -0
  49. package/apps/client-cms-editor/reference/harvest/plans/THEME-INNERANIMALS-HARVEST.md +137 -0
  50. package/apps/client-cms-editor/reference/harvest/plans/TOOLING-LAB.md +63 -0
  51. package/apps/client-cms-editor/reference/harvest/plans/harvest-manifest.json +97 -0
  52. package/apps/client-cms-editor/reference/harvest/plans/local-cms-harvest-schema.sql +328 -0
  53. package/apps/client-cms-editor/shared/cms/package.json +17 -7
  54. package/apps/client-cms-editor/shared/cms/src/adapter.ts +204 -0
  55. package/apps/client-cms-editor/shared/cms/src/agent-context.ts +7 -5
  56. package/apps/client-cms-editor/shared/cms/src/editor-types.ts +32 -0
  57. package/apps/client-cms-editor/shared/cms/src/host.ts +82 -0
  58. package/apps/client-cms-editor/shared/cms/src/index.ts +4 -0
  59. package/apps/client-cms-editor/shared/cms/src/map.ts +115 -0
  60. package/apps/client-cms-editor/shared/cms/src/starter-pack.ts +158 -0
  61. package/contracts/source-types.v1.json +353 -0
  62. package/docs/CMS_CONTEXTUAL_COMPOSER.md +1 -1
  63. package/docs/PRODUCT_LIFECYCLE.md +566 -0
  64. package/docs/REPO_FILE_MAP.txt +132 -0
  65. package/docs/THEME_REFINERY.md +2 -2
  66. package/docs/architecture/AGENTSAM_RUNTIME_PROTOCOL.md +40 -20
  67. package/docs/content-studio/REVISION_GATE_2026-09-27.md +1 -1
  68. package/docs/plans/LOCAL_STUDIO_DESKTOP_RELEASE_2026-09-29.md +150 -0
  69. package/migrations/d1/0017_identity_native_oauth.sql +21 -0
  70. package/migrations/d1/0018_goap_control_plane_remaster.sql +40 -0
  71. package/migrations/d1/0019_agentsam_agent_run_plan_todo.sql +14 -0
  72. package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +20 -9
  73. package/migrations/runtime/0002_goap_blackboard_v1.sql +15 -0
  74. package/migrations/runtime/0004_agent_run_plan_linkage.sql +8 -0
  75. package/native/agentsam-machine/Cargo.lock +426 -0
  76. package/native/agentsam-machine/Cargo.toml +9 -0
  77. package/native/agentsam-machine/cli/Cargo.toml +21 -0
  78. package/native/agentsam-machine/cli/README.md +21 -0
  79. package/native/agentsam-machine/cli/src/main.rs +77 -0
  80. package/native/agentsam-machine/core/Cargo.toml +22 -0
  81. package/native/agentsam-machine/core/README.md +14 -0
  82. package/native/agentsam-machine/core/build.rs +33 -0
  83. package/native/agentsam-machine/core/contracts/source-types.v1.json +353 -0
  84. package/native/agentsam-machine/core/src/assets.rs +790 -0
  85. package/native/agentsam-machine/core/src/bound.rs +298 -0
  86. package/native/agentsam-machine/core/src/enrich.rs +706 -0
  87. package/native/agentsam-machine/core/src/frontend.rs +442 -0
  88. package/native/agentsam-machine/core/src/lib.rs +1092 -0
  89. package/native/agentsam-machine/core/src/liquid.rs +528 -0
  90. package/native/agentsam-machine/core/src/source_types.rs +78 -0
  91. package/package.json +64 -24
  92. package/packages/agentsam-brand/README.md +1 -1
  93. package/packages/agentsam-brand/package.json +4 -4
  94. package/packages/agentsam-brand/src/index.js +1 -1
  95. package/packages/agentsam-contracts/package.json +13 -3
  96. package/packages/agentsam-contracts/src/tools.ts +22 -0
  97. package/packages/agentsam-errors/package.json +13 -3
  98. package/packages/agentsam-errors/src/index.js +1 -0
  99. package/packages/agentsam-errors/src/tool.js +56 -0
  100. package/packages/agentsam-errors/test/tool.test.mjs +58 -0
  101. package/packages/agentsam-goap/README.md +52 -0
  102. package/packages/agentsam-goap/package.json +37 -0
  103. package/packages/agentsam-goap/src/adapters/d1-sqlite.js +570 -0
  104. package/packages/agentsam-goap/src/adapters/memory.js +199 -0
  105. package/packages/agentsam-goap/src/contracts.js +226 -0
  106. package/packages/agentsam-goap/src/control-plane.js +112 -0
  107. package/packages/agentsam-goap/src/errors.js +157 -0
  108. package/packages/agentsam-goap/src/index.js +5 -0
  109. package/packages/agentsam-goap/src/world.js +202 -0
  110. package/packages/agentsam-goap/test/goap-control-plane.test.mjs +245 -0
  111. package/packages/agentsam-goap/test/world.test.mjs +79 -0
  112. package/packages/agentsam-knowledge/package.json +18 -4
  113. package/packages/agentsam-knowledge/src/autorag/index.js +10 -3
  114. package/packages/agentsam-knowledge/src/backends/index.js +33 -1
  115. package/packages/agentsam-knowledge/test/autorag.test.mjs +60 -1
  116. package/packages/agentsam-nav/dist/browser.global.js +21 -18
  117. package/packages/agentsam-nav/dist/browser.js +6807 -130
  118. package/packages/agentsam-nav/dist/index.js +6799 -122
  119. package/packages/agentsam-nav/package.json +24 -6
  120. package/packages/agentsam-queue-control/README.md +1 -1
  121. package/packages/agentsam-queue-control/package.json +7 -2
  122. package/packages/agentsam-rapid-rust/src/main.rs +1 -1
  123. package/packages/agentsam-repository/package.json +13 -3
  124. package/packages/agentsam-repository/src/goap.js +13 -14
  125. package/packages/agentsam-repository/src/source-types.js +8 -0
  126. package/packages/agentsam-repository/src/tickets.js +15 -22
  127. package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +19 -8
  128. package/packages/agentsam-vault/package.json +14 -4
  129. package/packages/agentsam-workbench/package.json +15 -4
  130. package/packages/agentsam-workbench/src/agent/ToolPermissionRequest.tsx +94 -0
  131. package/packages/agentsam-workbench/src/agent/index.ts +2 -0
  132. package/packages/agentsam-workbench/src/agent/tool-permission.css +134 -0
  133. package/packages/connectors/cloudflare/package.json +15 -3
  134. package/packages/connectors/cloudflare/src/api-client.js +3 -2
  135. package/packages/connectors/cloudflare/src/families/vectorize.js +127 -0
  136. package/packages/connectors/cloudflare/src/index.js +1 -0
  137. package/packages/heuristic-theme/README.md +2 -2
  138. package/packages/heuristic-theme/package.json +8 -3
  139. package/packages/heuristic-theme/src/registry.js +2 -2
  140. package/packages/heuristic-theme/src/theme.js +1 -1
  141. package/packages/heuristic-theme/test/theme.test.mjs +1 -1
  142. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
  143. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +27 -7
  144. package/packages/identity/README.md +2 -2
  145. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +18 -15
  146. package/packages/identity/migrations/apply-portable.js +109 -0
  147. package/packages/identity/migrations/sqlite/004_identity_oauth_consent_catalog.sql +1 -1
  148. package/packages/identity/migrations/sqlite/005_identity_company_native.sql +52 -0
  149. package/packages/identity/package.json +27 -6
  150. package/packages/identity/schema/agentsam.identity/manifest.json +101 -0
  151. package/packages/identity/scripts/local-identity-bridge.mjs +13 -7
  152. package/packages/identity/scripts/preview-auth-portal.mjs +6 -6
  153. package/packages/identity/src/adapters/cloudflare-d1/index.js +8 -401
  154. package/packages/identity/src/adapters/iam-compat/index.js +540 -0
  155. package/packages/identity/src/adapters/portable-d1/index.js +18 -0
  156. package/packages/identity/src/adapters/portable-store.js +479 -0
  157. package/packages/identity/src/adapters/sqlite/index.js +13 -312
  158. package/packages/identity/src/contracts/auth-config.js +0 -1
  159. package/packages/identity/src/contracts/identity-store.js +56 -5
  160. package/packages/identity/src/contracts/provider.js +1 -0
  161. package/packages/identity/src/contracts/sign-in-options.js +104 -0
  162. package/packages/identity/src/core/constants.js +1 -1
  163. package/packages/identity/src/core/session-policy.js +31 -0
  164. package/packages/identity/src/frontend/auth-portal/pages/login.html +6 -6
  165. package/packages/identity/src/frontend/auth-portal/pages/reset.html +5 -5
  166. package/packages/identity/src/frontend/auth-portal/pages/signup.html +5 -5
  167. package/packages/identity/src/frontend/auth-portal/shared/company-branding.js +1 -1
  168. package/packages/identity/src/frontend/auth-portal/shared/desktop-identity-bridge.js +6 -37
  169. package/packages/identity/src/index.js +28 -1
  170. package/packages/identity/src/migrations/README.md +23 -23
  171. package/packages/identity/src/oauth/README.md +1 -1
  172. package/packages/identity/src/oauth/credentials.js +10 -11
  173. package/packages/identity/src/oauth/google-desktop-exchange.js +195 -27
  174. package/packages/identity/src/oauth/iam-platform.js +21 -9
  175. package/packages/identity/src/oauth/native-handoff.js +136 -0
  176. package/packages/identity/src/providers/chatgpt/headers.js +65 -0
  177. package/packages/identity/src/providers/chatgpt/index.js +86 -0
  178. package/packages/identity/src/providers/chatgpt/mapper.js +43 -0
  179. package/packages/identity/src/providers/chatgpt/paths.js +45 -0
  180. package/packages/identity/src/providers/github/oauth.js +1 -2
  181. package/packages/identity/src/providers/github/profile.js +16 -2
  182. package/packages/identity/src/providers/index.js +11 -3
  183. package/packages/identity/src/server/identity-service.js +2 -1
  184. package/packages/identity/src/server/worker-router.js +71 -18
  185. package/packages/identity/tests/chatgpt-provider.test.mjs +75 -0
  186. package/packages/identity/tests/google-desktop-identity-login.test.mjs +210 -0
  187. package/packages/identity/tests/identity-init-scaffold.test.mjs +15 -4
  188. package/packages/identity/tests/lane1-identity-store-parity.test.mjs +527 -0
  189. package/packages/identity/tests/lane1-packed-consumer.test.mjs +175 -0
  190. package/packages/identity/tests/native-oauth-handoff.test.mjs +292 -0
  191. package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
  192. package/packages/identity/tests/preview-auth-portal.test.mjs +4 -2
  193. package/packages/identity/tests/session-routes-policy.test.mjs +13 -1
  194. package/packages/identity/tests/worker-runtime-boundary.test.mjs +11 -0
  195. package/packages/providers/completeful/package.json +7 -2
  196. package/packages/theme-church-site/package.json +17 -6
  197. package/packages/theme-church-site/src/harvest.json +4 -2
  198. package/packages/theme-church-site/src/index.js +7 -3
  199. package/packages/theme-church-site/test/theme.test.mjs +4 -2
  200. package/packages/theme-companions-site/package.json +17 -6
  201. package/packages/theme-companions-site/src/harvest.json +4 -2
  202. package/packages/theme-companions-site/src/index.js +7 -3
  203. package/packages/theme-companions-site/test/theme.test.mjs +4 -2
  204. package/packages/theme-floors-site/package.json +17 -6
  205. package/packages/theme-floors-site/src/harvest.json +4 -2
  206. package/packages/theme-floors-site/src/index.js +7 -3
  207. package/packages/theme-floors-site/test/theme.test.mjs +4 -2
  208. package/packages/theme-fuelnfree-site/package.json +17 -6
  209. package/packages/theme-fuelnfree-site/src/harvest.json +4 -2
  210. package/packages/theme-fuelnfree-site/src/index.js +7 -3
  211. package/packages/theme-fuelnfree-site/test/theme.test.mjs +4 -2
  212. package/packages/theme-handyman-site/package.json +17 -6
  213. package/packages/theme-handyman-site/src/harvest.json +4 -2
  214. package/packages/theme-handyman-site/src/index.js +7 -3
  215. package/packages/theme-handyman-site/test/theme.test.mjs +4 -2
  216. package/packages/theme-insurance-site/package.json +17 -6
  217. package/packages/theme-insurance-site/src/harvest.json +4 -2
  218. package/packages/theme-insurance-site/src/index.js +7 -3
  219. package/packages/theme-insurance-site/test/theme.test.mjs +4 -2
  220. package/packages/theme-scenes/README.md +1 -1
  221. package/packages/theme-scenes/package.json +4 -3
  222. package/packages/theme-scenes/src/registry.js +187 -0
  223. package/packages/theme-shinshu-site/package.json +17 -6
  224. package/packages/theme-shinshu-site/src/harvest.json +4 -2
  225. package/packages/theme-shinshu-site/src/index.js +7 -3
  226. package/packages/theme-shinshu-site/test/theme.test.mjs +4 -2
  227. package/packages/work-graph/package.json +14 -4
  228. package/protocol/FILEMETA_V1.md +2 -2
  229. package/protocol/capabilities/manifest.json +1 -1
  230. package/protocol/database/agentsam.schema-pack.v1.schema.json +72 -0
  231. package/protocol/presets/catalog.json +2 -2
  232. package/protocol/theme-refinery/lineage-registry.json +105 -0
  233. package/protocol/theme-refinery/product-metadata.schema.json +21 -1
  234. package/python/agentsam_sdk/repository/intelligence/discovery.py +27 -2
  235. package/python/agentsam_sdk/repository/intelligence/git.py +4 -0
  236. package/python/tests/test_repository_intelligence.py +30 -0
  237. package/src/cli/command-catalog.js +48 -5
  238. package/src/cli/dispatch.js +1 -0
  239. package/src/cli.js +20 -1
  240. package/src/cloudflare/runtime-status.js +2 -1
  241. package/src/commands/autorag.js +67 -13
  242. package/src/commands/cheat-sheet.js +29 -4
  243. package/src/commands/codebaseindex.js +45 -2
  244. package/src/commands/goap-world.js +134 -0
  245. package/src/commands/goap.js +11 -4
  246. package/src/commands/knowledge.js +59 -2
  247. package/src/commands/machine-binary.js +109 -0
  248. package/src/commands/machine-runtime.js +266 -0
  249. package/src/commands/machine.js +208 -0
  250. package/src/commands/package.js +530 -0
  251. package/src/commands/plan-ledger.js +370 -0
  252. package/src/commands/plan.js +183 -0
  253. package/src/commands/security.js +67 -4
  254. package/src/commands/setup.js +33 -16
  255. package/src/commands/skill.js +78 -17
  256. package/src/commands/status.js +8 -1
  257. package/src/features/resolve.js +1 -0
  258. package/src/knowledge/engine.js +21 -2
  259. package/src/knowledge/runtime-discovery.js +127 -0
  260. package/src/knowledge/source.js +1 -1
  261. package/src/lib/identity-scaffold.js +53 -13
  262. package/src/lib/setup/recipes.js +1 -1
  263. package/src/lib/slash-commands.js +1 -1
  264. package/src/local/runtime-store.js +24 -2
  265. package/src/local/sqlite.js +17 -0
  266. package/src/models/discovery.js +40 -22
  267. package/src/models/inventory-core.d.ts +105 -0
  268. package/src/models/inventory-core.js +54 -6
  269. package/src/sam/operations/package-audit.js +41 -0
  270. package/src/sam/seed.js +2 -0
  271. package/src/security/logs.js +3 -0
  272. package/src/ui/cli/help.js +41 -0
  273. package/apps/client-cms-editor/backend/src/demo-bootstrap.ts +0 -145
  274. package/test/account-session.test.mjs +0 -101
  275. package/test/agent-capabilities.test.mjs +0 -67
  276. package/test/app-authority-context.test.mjs +0 -32
  277. package/test/app-building-skills.test.mjs +0 -61
  278. package/test/apps-scaffold-contract.test.mjs +0 -114
  279. package/test/blender-cad.test.mjs +0 -146
  280. package/test/cad-docker.test.mjs +0 -96
  281. package/test/capabilities.test.mjs +0 -133
  282. package/test/cli/api-key-env-whoami.test.mjs +0 -129
  283. package/test/cli/codebaseindex-plan-ux.test.mjs +0 -37
  284. package/test/cli/eval-live.test.mjs +0 -219
  285. package/test/cli/go.test.mjs +0 -128
  286. package/test/cli/local-vault-and-provider-setup.test.mjs +0 -268
  287. package/test/cli/mcp-client.test.mjs +0 -256
  288. package/test/cli/preferences-runtime.test.mjs +0 -11
  289. package/test/cli/runtime-persist.test.mjs +0 -56
  290. package/test/cli/runtime-ui.test.mjs +0 -141
  291. package/test/cli/rust-command.test.mjs +0 -21
  292. package/test/cli/skill-npm-and-env.test.mjs +0 -52
  293. package/test/cli/status-actions.test.mjs +0 -88
  294. package/test/cli/wireframes-go-registry.test.mjs +0 -139
  295. package/test/cli-preferences.test.mjs +0 -46
  296. package/test/cloudflare-connector.test.mjs +0 -93
  297. package/test/cloudflare-runtime.test.mjs +0 -82
  298. package/test/context.test.mjs +0 -159
  299. package/test/credentials-audit.test.mjs +0 -113
  300. package/test/deploy-health-scan.test.mjs +0 -95
  301. package/test/deploy-receipt.test.mjs +0 -91
  302. package/test/dockerize.test.mjs +0 -93
  303. package/test/error-diagnostics.test.mjs +0 -124
  304. package/test/eval-context.test.mjs +0 -37
  305. package/test/execution-approvals.test.mjs +0 -27
  306. package/test/fixtures/cad/courtyard-house.json +0 -208
  307. package/test/fixtures/knowledge-rpc-worker.mjs +0 -16
  308. package/test/gcloud-auth.test.mjs +0 -111
  309. package/test/go/build-source-identity.test.mjs +0 -31
  310. package/test/go/cloudflare-probe.test.mjs +0 -317
  311. package/test/go/distribution.test.mjs +0 -28
  312. package/test/google-cloud-connection.test.mjs +0 -83
  313. package/test/google-cloud-inventory.test.mjs +0 -64
  314. package/test/indexing-provider.test.mjs +0 -29
  315. package/test/init-options.test.mjs +0 -35
  316. package/test/integration/ai-access-onboarding.test.mjs +0 -47
  317. package/test/integration/app-command.test.mjs +0 -85
  318. package/test/integration/cad-discovery.test.mjs +0 -158
  319. package/test/integration/cad-docker-executor.test.mjs +0 -249
  320. package/test/integration/cad-execution.test.mjs +0 -184
  321. package/test/integration/cad-project-app.test.mjs +0 -31
  322. package/test/integration/cad-project-d1.test.mjs +0 -25
  323. package/test/integration/cad-project-native.test.mjs +0 -24
  324. package/test/integration/cad-project-workflow.test.mjs +0 -9
  325. package/test/integration/cad-project.test.mjs +0 -85
  326. package/test/integration/cli-help.test.mjs +0 -41
  327. package/test/integration/cms-app-scaffold.test.mjs +0 -71
  328. package/test/integration/cms-site-tenancy-contract.test.mjs +0 -257
  329. package/test/integration/codebaseindex-ingest.test.mjs +0 -214
  330. package/test/integration/core-client.integration.test.mjs +0 -55
  331. package/test/integration/error-runtime-rpc.test.mjs +0 -71
  332. package/test/integration/features-resolve.test.mjs +0 -147
  333. package/test/integration/icon-registry.test.mjs +0 -67
  334. package/test/integration/ingest-discover-models.test.mjs +0 -30
  335. package/test/integration/inspect-path-cli.test.mjs +0 -46
  336. package/test/integration/install-script.test.mjs +0 -110
  337. package/test/integration/inventory-core.test.mjs +0 -53
  338. package/test/integration/inventory-evidence.test.mjs +0 -125
  339. package/test/integration/knowledge-rpc.test.mjs +0 -112
  340. package/test/integration/local-fs.test.mjs +0 -113
  341. package/test/integration/merkle-cli.test.mjs +0 -61
  342. package/test/integration/merkle-persistence-identity.test.mjs +0 -48
  343. package/test/integration/plugins-cloudflare.integration.test.mjs +0 -28
  344. package/test/integration/provider-env-cli.test.mjs +0 -50
  345. package/test/integration/provider-factory.test.mjs +0 -197
  346. package/test/integration/repository-company-graph.test.mjs +0 -90
  347. package/test/integration/runtime-migrations.test.mjs +0 -87
  348. package/test/integration/runtime-status.integration.test.mjs +0 -212
  349. package/test/integration/sam-activity-recovery.test.mjs +0 -147
  350. package/test/integration/sam-decision.test.mjs +0 -584
  351. package/test/integration/sam-kernel.test.mjs +0 -99
  352. package/test/integration/sam-planning-astar.test.mjs +0 -279
  353. package/test/integration/scaffold-cloudflare.test.mjs +0 -46
  354. package/test/integration/skill-runtime.test.mjs +0 -240
  355. package/test/integration/storage-architecture.test.mjs +0 -154
  356. package/test/integration/store-discovery.test.mjs +0 -44
  357. package/test/integration/studio-fs-pty-e2e.test.mjs +0 -294
  358. package/test/integration/terminal-exec.integration.test.mjs +0 -86
  359. package/test/integration/tool-runtime.test.mjs +0 -124
  360. package/test/integration/tool-schema.test.mjs +0 -59
  361. package/test/jr-dev-skill.test.mjs +0 -26
  362. package/test/knowledge-context-pack.test.mjs +0 -19
  363. package/test/knowledge-postgres.test.mjs +0 -40
  364. package/test/knowledge-service.test.mjs +0 -110
  365. package/test/knowledge.test.mjs +0 -206
  366. package/test/live/terminal-transport.live.test.mjs +0 -24
  367. package/test/local-sessions.test.mjs +0 -57
  368. package/test/local-studio-deploy.test.mjs +0 -83
  369. package/test/loopback-oauth.test.mjs +0 -53
  370. package/test/merkle-render.test.mjs +0 -31
  371. package/test/mini.test.mjs +0 -140
  372. package/test/model-catalog.test.mjs +0 -70
  373. package/test/models.test.mjs +0 -309
  374. package/test/npm10-lock.test.mjs +0 -29
  375. package/test/ollama.test.mjs +0 -137
  376. package/test/open-url.test.mjs +0 -64
  377. package/test/openai-responses.test.mjs +0 -155
  378. package/test/portable-context.test.mjs +0 -94
  379. package/test/project-config.test.mjs +0 -81
  380. package/test/project-rules.test.mjs +0 -44
  381. package/test/provider-credentials.test.mjs +0 -106
  382. package/test/rehydrate.test.mjs +0 -25
  383. package/test/release-hygiene.test.mjs +0 -42
  384. package/test/repository-snapshot-view.test.mjs +0 -112
  385. package/test/responses-runner.test.mjs +0 -465
  386. package/test/runtime-activity.test.mjs +0 -98
  387. package/test/sdk-worker-contract.test.mjs +0 -72
  388. package/test/security.test.mjs +0 -252
  389. package/test/setup-planner.test.mjs +0 -74
  390. package/test/setup-runtime.test.mjs +0 -51
  391. package/test/shell.test.mjs +0 -281
  392. package/test/skills.test.mjs +0 -22
  393. package/test/smoke.mjs +0 -122
  394. package/test/telemetry.test.mjs +0 -79
  395. package/test/terminal/local-pty.mock.test.mjs +0 -153
  396. package/test/terminal-machine-identity.test.mjs +0 -88
  397. package/test/theme-portability.test.mjs +0 -14
  398. package/test/tools-search.test.mjs +0 -73
  399. package/test/unit/website-assets-bindings.test.mjs +0 -60
  400. package/test/whoami-resume.test.mjs +0 -157
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Native (desktop) OAuth handoff.
3
+ *
4
+ * A desktop app signs in through the system browser, so the provider redirect
5
+ * lands in a browser that cannot hand a Set-Cookie to the app. Instead:
6
+ *
7
+ * start GET /api/oauth/{provider}/start?client=native
8
+ * &native_challenge=<S256(verifier)>&native_redirect=<deep link>
9
+ * callback creates an auth_sessions row (type=desktop), stores a single-use
10
+ * handoff bound to the PKCE challenge, and 302s to the deep link
11
+ * with ?handoff=<one-time code> — the session id never rides a URL.
12
+ * exchange POST /api/oauth/native/exchange { handoff, code_verifier }
13
+ * → same JSON as native password login (session_id, expires_at, user)
14
+ *
15
+ * Identity only. This never writes provider resource grants (user_oauth_tokens).
16
+ */
17
+
18
+ import { jsonResponse } from '../core/http-json.js';
19
+ import { NATIVE_HANDOFF_TTL_SECONDS, SESSION_TYPES } from '../core/session-policy.js';
20
+ import { pkceChallenge, randomOAuthState } from './pkce.js';
21
+
22
+ const DEFAULT_NATIVE_SCHEMES = ['agentsamstudio'];
23
+ const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '[::1]']);
24
+ const S256_CHALLENGE = /^[A-Za-z0-9_-]{43}$/;
25
+
26
+ export const NATIVE_EXCHANGE_PATH = '/api/oauth/native/exchange';
27
+
28
+ async function sha256Hex(value) {
29
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(String(value)));
30
+ return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');
31
+ }
32
+
33
+ function allowedSchemes(env) {
34
+ const configured = String(env?.IDENTITY_NATIVE_REDIRECT_SCHEMES || '')
35
+ .split(',')
36
+ .map((s) => s.trim().toLowerCase())
37
+ .filter(Boolean);
38
+ return configured.length ? configured : DEFAULT_NATIVE_SCHEMES;
39
+ }
40
+
41
+ /** Deep-link scheme allowlist, or an http loopback redirect. Never a web origin. */
42
+ export function isAllowedNativeRedirect(uri, env) {
43
+ let parsed;
44
+ try {
45
+ parsed = new URL(String(uri || ''));
46
+ } catch {
47
+ return false;
48
+ }
49
+ if (parsed.protocol === 'http:') return LOOPBACK_HOSTS.has(parsed.hostname);
50
+ return allowedSchemes(env).includes(parsed.protocol.replace(/:$/, '').toLowerCase());
51
+ }
52
+
53
+ /**
54
+ * Read native params off an OAuth start URL.
55
+ * @returns {{ native: false } | { native: true, error: string } |
56
+ * { native: true, clientType: 'desktop', nativeChallenge: string, nativeRedirect: string }}
57
+ */
58
+ export function parseNativeStart(url, env) {
59
+ const client = String(url.searchParams.get('client') || '').toLowerCase();
60
+ if (client !== 'native' && client !== 'desktop') return { native: false };
61
+ const nativeChallenge = url.searchParams.get('native_challenge') || '';
62
+ const nativeRedirect = url.searchParams.get('native_redirect') || '';
63
+ if (!S256_CHALLENGE.test(nativeChallenge)) return { native: true, error: 'native_challenge_invalid' };
64
+ if (!isAllowedNativeRedirect(nativeRedirect, env)) return { native: true, error: 'native_redirect_not_allowed' };
65
+ return { native: true, clientType: SESSION_TYPES.DESKTOP, nativeChallenge, nativeRedirect };
66
+ }
67
+
68
+ /** Session type an OAuth transaction should mint. */
69
+ export function sessionTypeForTransaction(saved) {
70
+ return saved?.client_type === SESSION_TYPES.DESKTOP ? SESSION_TYPES.DESKTOP : SESSION_TYPES.BROWSER;
71
+ }
72
+
73
+ /**
74
+ * Shared callback tail for every OAuth lane. Browser transactions keep the
75
+ * existing cookie + globe redirect; native transactions get the handoff redirect.
76
+ */
77
+ export async function finishOAuthLogin({ request, env, identity, adapter, saved, result, loginPath }) {
78
+ const url = new URL(request.url);
79
+ const redirectTo = identity.resolvePostLoginPath(saved?.redirect_to);
80
+
81
+ if (sessionTypeForTransaction(saved) === SESSION_TYPES.DESKTOP) {
82
+ if (!saved.native_challenge || !isAllowedNativeRedirect(saved.native_redirect, env)) {
83
+ await adapter.revokeSession?.(result.sessionId);
84
+ return Response.redirect(`${url.origin}${loginPath}?error=native_handoff_invalid`, 302);
85
+ }
86
+ const handoff = randomOAuthState();
87
+ await adapter.createNativeHandoff({
88
+ handoffHash: await sha256Hex(handoff),
89
+ sessionId: result.sessionId,
90
+ challenge: saved.native_challenge,
91
+ ttlSeconds: NATIVE_HANDOFF_TTL_SECONDS,
92
+ });
93
+ const target = new URL(saved.native_redirect);
94
+ target.searchParams.set('handoff', handoff);
95
+ return new Response(null, {
96
+ status: 302,
97
+ headers: { Location: target.toString(), 'Cache-Control': 'no-store' },
98
+ });
99
+ }
100
+
101
+ const res = identity.buildLoginSuccessResponse(request, result.sessionId, redirectTo);
102
+ const globeUrl = `${url.origin}${loginPath}?globe_exit=1&next=${encodeURIComponent(redirectTo)}`;
103
+ return new Response(null, {
104
+ status: 302,
105
+ headers: { Location: globeUrl, 'Set-Cookie': res.headers.get('Set-Cookie') || '' },
106
+ });
107
+ }
108
+
109
+ /** POST /api/oauth/native/exchange — single-use, PKCE-bound. */
110
+ export async function handleNativeExchangeRequest(request, { adapter, identity }) {
111
+ if (request.method !== 'POST') return jsonResponse({ ok: false, error: 'method_not_allowed' }, 405);
112
+ let body;
113
+ try {
114
+ body = await request.json();
115
+ } catch {
116
+ return jsonResponse({ ok: false, error: 'invalid_json' }, 400);
117
+ }
118
+ const handoff = String(body?.handoff || '').trim();
119
+ const verifier = String(body?.code_verifier || body?.codeVerifier || '').trim();
120
+ if (!handoff || !verifier) {
121
+ return jsonResponse({ ok: false, error: 'handoff_and_code_verifier_required' }, 400);
122
+ }
123
+
124
+ // Consumed before the verifier check, so a wrong verifier burns the code.
125
+ const row = await adapter.consumeNativeHandoff(await sha256Hex(handoff));
126
+ if (!row) return jsonResponse({ ok: false, error: 'handoff_invalid_or_expired' }, 400);
127
+ if ((await pkceChallenge(verifier)) !== row.challenge) {
128
+ await adapter.revokeSession?.(row.session_id);
129
+ return jsonResponse({ ok: false, error: 'pkce_verification_failed' }, 400);
130
+ }
131
+
132
+ const session = await adapter.getSession(row.session_id);
133
+ const user = session ? await adapter.findUserById(session.user_id) : null;
134
+ if (!session || !user) return jsonResponse({ ok: false, error: 'session_unavailable' }, 401);
135
+ return identity.buildNativeLoginSuccessResponse({ sessionId: session.id, session, user }, body?.next);
136
+ }
@@ -0,0 +1,65 @@
1
+ /** ChatGPT Apps SDK / OpenAI hosted identity header constants. */
2
+
3
+ export const CHATGPT_USER_EMAIL_HEADER = 'oai-authenticated-user-email';
4
+ export const CHATGPT_USER_FULL_NAME_HEADER = 'oai-authenticated-user-full-name';
5
+ export const CHATGPT_USER_FULL_NAME_ENCODING_HEADER =
6
+ 'oai-authenticated-user-full-name-encoding';
7
+ export const CHATGPT_PERCENT_ENCODED_UTF8 = 'percent-encoded-utf-8';
8
+
9
+ export const CHATGPT_SIGN_IN_PATH = '/signin-with-chatgpt';
10
+ export const CHATGPT_SIGN_OUT_PATH = '/signout-with-chatgpt';
11
+ export const CHATGPT_CALLBACK_PATH = '/callback';
12
+
13
+ /**
14
+ * @param {Headers | Record<string, string | string[] | undefined> | null | undefined} headers
15
+ * @param {string} name
16
+ */
17
+ export function readHeader(headers, name) {
18
+ if (!headers) return null;
19
+ const key = String(name).toLowerCase();
20
+ if (typeof headers.get === 'function') {
21
+ const value = headers.get(name) ?? headers.get(key);
22
+ return value == null ? null : String(value);
23
+ }
24
+ const raw =
25
+ headers[name] ??
26
+ headers[key] ??
27
+ Object.entries(headers).find(([k]) => k.toLowerCase() === key)?.[1];
28
+ if (Array.isArray(raw)) return raw[0] == null ? null : String(raw[0]);
29
+ return raw == null ? null : String(raw);
30
+ }
31
+
32
+ function safeDecodeURIComponent(value) {
33
+ try {
34
+ return decodeURIComponent(value);
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Extract ChatGPT hosted user profile from request headers (Apps SDK).
42
+ * Does not leak transport headers into NormalizedExternalIdentity.raw beyond a redacted snapshot.
43
+ *
44
+ * @param {Headers | Record<string, string | string[] | undefined> | null | undefined} headers
45
+ * @returns {{ email: string, fullName: string | null, displayName: string } | null}
46
+ */
47
+ export function readChatGptUserFromHeaders(headers) {
48
+ const email = readHeader(headers, CHATGPT_USER_EMAIL_HEADER);
49
+ if (!email) return null;
50
+
51
+ const encodedFullName = readHeader(headers, CHATGPT_USER_FULL_NAME_HEADER);
52
+ const encoding = readHeader(headers, CHATGPT_USER_FULL_NAME_ENCODING_HEADER);
53
+ const fullName =
54
+ encodedFullName && encoding === CHATGPT_PERCENT_ENCODED_UTF8
55
+ ? safeDecodeURIComponent(encodedFullName)
56
+ : encodedFullName && encoding !== CHATGPT_PERCENT_ENCODED_UTF8
57
+ ? encodedFullName
58
+ : null;
59
+
60
+ return {
61
+ email: email.trim().toLowerCase(),
62
+ fullName,
63
+ displayName: (fullName || email).trim(),
64
+ };
65
+ }
@@ -0,0 +1,86 @@
1
+ import { createIdentityProvider } from '../../provider-contract.js';
2
+ import { normalizeChatGptIdentity } from './mapper.js';
3
+ import { readChatGptUserFromHeaders } from './headers.js';
4
+ import { chatGPTSignInPath, chatGPTSignOutPath, safeRelativeReturnPath } from './paths.js';
5
+
6
+ /**
7
+ * ChatGPT hosted identity provider (OpenAI Apps SDK).
8
+ *
9
+ * Auth is header-based in the ChatGPT host — not classic OAuth client credentials.
10
+ * `authorizeUrl` points at the hosted sign-in path; `exchangeCode` is unsupported.
11
+ */
12
+ export const ChatGptProvider = createIdentityProvider({
13
+ id: 'chatgpt',
14
+ authorizeUrl: ({ redirectUri, state } = {}) => {
15
+ const returnTo =
16
+ typeof redirectUri === 'string' && redirectUri.startsWith('/')
17
+ ? redirectUri
18
+ : '/';
19
+ const path = chatGPTSignInPath(returnTo);
20
+ return state ? `${path}&state=${encodeURIComponent(state)}` : path;
21
+ },
22
+ exchangeCode: async () => null,
23
+ getProfile: async () => null,
24
+ normalizeIdentity: normalizeChatGptIdentity,
25
+ });
26
+
27
+ /**
28
+ * Build a portable CmsAuthHost for ChatGPT-hosted apps.
29
+ * Reads `oai-authenticated-*` headers; challenges to hosted sign-in when missing.
30
+ *
31
+ * @param {{ cmsBase?: string }} [options]
32
+ */
33
+ export function createChatGptCmsAuthHost(options = {}) {
34
+ const cmsBase = options.cmsBase || '/cms';
35
+ return {
36
+ async authorizeCmsAccess({ path, headers }) {
37
+ const user = readChatGptUserFromHeaders(headers);
38
+ if (!user) {
39
+ return {
40
+ allowed: false,
41
+ principal: null,
42
+ reason: 'chatgpt_identity_required',
43
+ challengeUrl: chatGPTSignInPath(path || cmsBase),
44
+ };
45
+ }
46
+ const identity = normalizeChatGptIdentity({
47
+ email: user.email,
48
+ name: user.fullName || user.displayName,
49
+ sub: user.email,
50
+ });
51
+ return {
52
+ allowed: true,
53
+ principal: {
54
+ id: `chatgpt:${identity.subject}`,
55
+ displayName: identity.name || identity.email || identity.subject,
56
+ email: identity.email || undefined,
57
+ roles: ['cms_admin'],
58
+ localDevelopment: false,
59
+ provider: 'chatgpt',
60
+ },
61
+ reason: 'chatgpt_hosted_identity',
62
+ };
63
+ },
64
+ buildLoginUrl({ returnTo }) {
65
+ return chatGPTSignInPath(returnTo || cmsBase);
66
+ },
67
+ };
68
+ }
69
+
70
+ export {
71
+ normalizeChatGptIdentity,
72
+ readChatGptUserFromHeaders,
73
+ chatGPTSignInPath,
74
+ chatGPTSignOutPath,
75
+ safeRelativeReturnPath,
76
+ };
77
+
78
+ export {
79
+ CHATGPT_USER_EMAIL_HEADER,
80
+ CHATGPT_USER_FULL_NAME_HEADER,
81
+ CHATGPT_USER_FULL_NAME_ENCODING_HEADER,
82
+ CHATGPT_PERCENT_ENCODED_UTF8,
83
+ CHATGPT_SIGN_IN_PATH,
84
+ CHATGPT_SIGN_OUT_PATH,
85
+ CHATGPT_CALLBACK_PATH,
86
+ } from './headers.js';
@@ -0,0 +1,43 @@
1
+ import { normalizeExternalIdentity } from '../../contracts/external-identity.js';
2
+
3
+ /**
4
+ * Normalize ChatGPT / OpenAI Apps SDK identity into portable external identity.
5
+ * Provider id is always `chatgpt` — never leak `oai-authenticated-*` header names into contracts.
6
+ *
7
+ * @param {Record<string, unknown> | null | undefined} profile
8
+ */
9
+ export function normalizeChatGptIdentity(profile) {
10
+ const raw = profile && typeof profile === 'object' ? profile : {};
11
+ const email =
12
+ typeof raw.email === 'string' && raw.email.trim()
13
+ ? raw.email.trim().toLowerCase()
14
+ : null;
15
+ const subject = String(raw.sub || raw.id || raw.subject || email || '').trim();
16
+ if (!subject) {
17
+ throw new Error('chatgpt_identity_requires_subject');
18
+ }
19
+
20
+ const name =
21
+ typeof raw.name === 'string' && raw.name.trim()
22
+ ? raw.name.trim()
23
+ : typeof raw.displayName === 'string' && raw.displayName.trim()
24
+ ? raw.displayName.trim()
25
+ : typeof raw.fullName === 'string' && raw.fullName.trim()
26
+ ? raw.fullName.trim()
27
+ : null;
28
+
29
+ return normalizeExternalIdentity({
30
+ provider: 'chatgpt',
31
+ subject,
32
+ email,
33
+ emailVerified: Boolean(email),
34
+ name,
35
+ avatar: null,
36
+ username: null,
37
+ raw: {
38
+ source: 'chatgpt_hosted',
39
+ email,
40
+ name,
41
+ },
42
+ });
43
+ }
@@ -0,0 +1,45 @@
1
+ import {
2
+ CHATGPT_CALLBACK_PATH,
3
+ CHATGPT_SIGN_IN_PATH,
4
+ CHATGPT_SIGN_OUT_PATH,
5
+ } from './headers.js';
6
+
7
+ function isReservedAuthPath(pathname) {
8
+ return (
9
+ pathname === CHATGPT_SIGN_IN_PATH ||
10
+ pathname === CHATGPT_SIGN_OUT_PATH ||
11
+ pathname === CHATGPT_CALLBACK_PATH
12
+ );
13
+ }
14
+
15
+ /** Safe relative return path for ChatGPT hosted sign-in/out redirects. */
16
+ export function safeRelativeReturnPath(value) {
17
+ const raw = String(value || '/');
18
+ if (!raw.startsWith('/') || raw.startsWith('//')) return '/';
19
+
20
+ let url;
21
+ try {
22
+ url = new URL(raw, 'https://app.local');
23
+ } catch {
24
+ return '/';
25
+ }
26
+ if (url.origin !== 'https://app.local') return '/';
27
+ if (isReservedAuthPath(url.pathname)) return '/';
28
+ return `${url.pathname}${url.search}${url.hash}`;
29
+ }
30
+
31
+ export function chatGPTSignInPath(returnTo = '/') {
32
+ const safeReturnTo = safeRelativeReturnPath(returnTo);
33
+ return `${CHATGPT_SIGN_IN_PATH}?return_to=${encodeURIComponent(safeReturnTo)}`;
34
+ }
35
+
36
+ export function chatGPTSignOutPath(returnTo = '/') {
37
+ const safeReturnTo = safeRelativeReturnPath(returnTo);
38
+ return `${CHATGPT_SIGN_OUT_PATH}?return_to=${encodeURIComponent(safeReturnTo)}`;
39
+ }
40
+
41
+ export {
42
+ CHATGPT_SIGN_IN_PATH,
43
+ CHATGPT_SIGN_OUT_PATH,
44
+ CHATGPT_CALLBACK_PATH,
45
+ };
@@ -2,12 +2,11 @@ const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize';
2
2
  const GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token';
3
3
 
4
4
  /** @param {import('../../provider-contract.js').OAuthAuthorizeInput} input */
5
- export function getGithubAuthUrl({ clientId, redirectUri, state, codeChallenge, scope } = {}) {
5
+ export function getGithubAuthUrl({ clientId, redirectUri, state, codeChallenge } = {}) {
6
6
  const url = new URL(GITHUB_AUTH_URL);
7
7
  url.searchParams.set('client_id', clientId || '');
8
8
  url.searchParams.set('redirect_uri', redirectUri || '');
9
9
  url.searchParams.set('response_type', 'code');
10
- url.searchParams.set('scope', scope || 'read:user user:email');
11
10
  url.searchParams.set('state', state || '');
12
11
  if (codeChallenge) {
13
12
  url.searchParams.set('code_challenge', codeChallenge);
@@ -3,7 +3,8 @@ const GITHUB_EMAILS_URL = 'https://api.github.com/user/emails';
3
3
 
4
4
  const GITHUB_HEADERS = {
5
5
  Accept: 'application/vnd.github+json',
6
- 'X-GitHub-Api-Version': '2022-11-28',
6
+ 'X-GitHub-Api-Version': '2026-03-10',
7
+ 'User-Agent': 'AgentSam-Local-Studio',
7
8
  };
8
9
 
9
10
  /** @param {string} accessToken */
@@ -17,7 +18,15 @@ export async function fetchGithubProfile(accessToken) {
17
18
  headers: { Authorization: `Bearer ${accessToken}`, ...GITHUB_HEADERS },
18
19
  }),
19
20
  ]);
20
- if (!userRes.ok) return null;
21
+ if (!userRes.ok) {
22
+ const detail = (await userRes.text()).slice(0, 500);
23
+ console.error('github_userinfo_failed', {
24
+ status: userRes.status,
25
+ requestId: userRes.headers.get('x-github-request-id'),
26
+ detail,
27
+ });
28
+ return null;
29
+ }
21
30
  const user = await userRes.json();
22
31
  let email = null;
23
32
  let emailVerified = false;
@@ -26,6 +35,11 @@ export async function fetchGithubProfile(accessToken) {
26
35
  const primary = Array.isArray(emails) ? emails.find((e) => e?.primary) : null;
27
36
  email = primary?.email || null;
28
37
  emailVerified = !!primary?.verified;
38
+ } else {
39
+ console.warn('github_user_emails_failed', {
40
+ status: emailRes.status,
41
+ requestId: emailRes.headers.get('x-github-request-id'),
42
+ });
29
43
  }
30
44
  return { ...user, email, email_verified: emailVerified };
31
45
  } catch {
@@ -5,11 +5,12 @@ import { GcpProvider } from './gcp/index.js';
5
5
  import { EmailProvider } from './email/index.js';
6
6
  import { IamProvider } from './iam/index.js';
7
7
  import { CloudflareProvider } from './cloudflare/index.js';
8
+ import { ChatGptProvider } from './chatgpt/index.js';
8
9
 
9
10
  /**
10
11
  * Active identity provider registry.
11
- * `inneranimalmedia` is the canonical platform template id; `iam` remains a
12
- * legacy lookup alias (OAuth/DB provider key on platform identities).
12
+ * `inneranimalmedia` is the canonical platform template id; `iam` and `inneranimal` remain
13
+ * legacy lookup aliases (OAuth/DB provider keys on platform identities).
13
14
  * @type {Record<string, import('../provider-contract.js').IdentityProvider>}
14
15
  */
15
16
  export const identityProviders = Object.freeze({
@@ -20,12 +21,18 @@ export const identityProviders = Object.freeze({
20
21
  inneranimalmedia: IamProvider,
21
22
  iam: IamProvider,
22
23
  cloudflare: CloudflareProvider,
24
+ chatgpt: ChatGptProvider,
23
25
  });
24
26
 
25
27
  /** @param {string} id */
26
28
  export function getIdentityProvider(id) {
27
29
  const key = String(id || '').trim().toLowerCase();
28
- if (key === 'iam') return identityProviders.inneranimalmedia || identityProviders.iam || null;
30
+ if (key === 'iam' || key === 'inneranimal') {
31
+ return identityProviders.inneranimalmedia || identityProviders.iam || null;
32
+ }
33
+ if (key === 'openai' || key === 'chatgpt-hosted' || key === 'chatgpt_hosted') {
34
+ return identityProviders.chatgpt || null;
35
+ }
29
36
  return identityProviders[key] || null;
30
37
  }
31
38
 
@@ -40,4 +47,5 @@ export {
40
47
  EmailProvider,
41
48
  IamProvider,
42
49
  CloudflareProvider,
50
+ ChatGptProvider,
43
51
  };
@@ -130,7 +130,7 @@ export function createIdentityService(config) {
130
130
  return { ok: true };
131
131
  },
132
132
 
133
- async provisionOAuthUser({ provider, providerSubject, email, displayName }) {
133
+ async provisionOAuthUser({ provider, providerSubject, email, displayName, sessionType }) {
134
134
  let user = await adapter.findUserByProvider(provider, providerSubject);
135
135
  if (!user && email) {
136
136
  user = await adapter.findUserByEmail(email);
@@ -155,6 +155,7 @@ export function createIdentityService(config) {
155
155
  displayName: displayName || user.display_name,
156
156
  provider,
157
157
  providerSubject,
158
+ type: sessionType,
158
159
  });
159
160
  return { ok: true, authUserId: user.id, sessionId: session.id, session };
160
161
  },
@@ -1,4 +1,5 @@
1
- import { createCloudflareD1Adapter } from '../adapters/cloudflare-d1/index.js';
1
+ import { createCloudflareD1Adapter, createIamCompatIdentityAdapter } from '../adapters/iam-compat/index.js';
2
+ import { createPortableD1IdentityAdapter } from '../adapters/portable-d1/index.js';
2
3
  import { createIdentityService } from './identity-service.js';
3
4
  import { jsonResponse } from '../core/http-json.js';
4
5
  import { hashPassword } from '../core/password-crypto.js';
@@ -12,9 +13,31 @@ import { fetchCloudflareProfile } from '../providers/cloudflare/profile.js';
12
13
  import { IDENTITY_ROUTE_IDS } from '../contracts/route-ids.js';
13
14
  import { IdentityRoutingError } from '../contracts/identity-store.js';
14
15
  import { resolveOAuthCredentialLane } from '../oauth/credentials.js';
15
- import { handleGoogleDesktopExchangeRequest } from '../oauth/google-desktop-exchange.js';
16
+ import {
17
+ GOOGLE_DESKTOP_LOGIN_EXCHANGE_PATH,
18
+ handleGoogleDesktopExchangeRequest,
19
+ handleGoogleDesktopLoginExchangeRequest,
20
+ } from '../oauth/google-desktop-exchange.js';
16
21
  import { iamPlatformOAuthCallback, iamPlatformOAuthStart } from '../oauth/iam-platform.js';
17
22
  import { pkceChallenge, pkceVerifier, randomOAuthState } from '../oauth/pkce.js';
23
+ import {
24
+ NATIVE_EXCHANGE_PATH,
25
+ finishOAuthLogin,
26
+ handleNativeExchangeRequest,
27
+ parseNativeStart,
28
+ sessionTypeForTransaction,
29
+ } from '../oauth/native-handoff.js';
30
+
31
+ function resolveIdentityAdapter(env, options = {}) {
32
+ if (options.adapter) return options.adapter;
33
+ const profile = String(options.identityProfile || env?.IDENTITY_ADAPTER_PROFILE || 'iam-compat')
34
+ .trim()
35
+ .toLowerCase();
36
+ if (profile === 'portable' || profile === 'portable-d1') {
37
+ return createPortableD1IdentityAdapter(env.DB);
38
+ }
39
+ return createIamCompatIdentityAdapter(env.DB);
40
+ }
18
41
 
19
42
  function escapeHtml(value) {
20
43
  return String(value ?? '')
@@ -51,7 +74,12 @@ function buildPasswordResetService(env, adapter, options = {}) {
51
74
  }
52
75
  const company = await adapter.getDefaultCompany().catch(() => null);
53
76
  const brand = company?.name || 'Your App';
54
- const fromEmail = company?.supportEmail || 'hey@inneranimalmedia.com';
77
+ const fromEmail = company?.supportEmail;
78
+ if (!fromEmail) {
79
+ const err = new Error('email_not_configured');
80
+ err.code = 'email_not_configured';
81
+ throw err;
82
+ }
55
83
  const html = `<p>Hi ${escapeHtml(name)},</p><p>Your ${escapeHtml(brand)} verification code is:</p><p style="font-size:22px;font-weight:700;letter-spacing:4px;">${escapeHtml(code)}</p><p>Enter this on the reset page. Expires in 15 minutes.</p>`;
56
84
  const res = await fetch('https://api.resend.com/emails', {
57
85
  method: 'POST',
@@ -78,14 +106,15 @@ function buildPasswordResetService(env, adapter, options = {}) {
78
106
  * Worker fetch handler for identity API + auth page routing.
79
107
  * @param {Request} request
80
108
  * @param {{ DB: import('../adapters/cloudflare-d1/index.js').D1Database, ASSETS?: { fetch: (req: Request) => Promise<Response> }, [key: string]: unknown }} env
81
- * @param {{ identity?: ReturnType<typeof createIdentityService>, brandName?: string }} [options]
109
+ * @param {{ identity?: ReturnType<typeof createIdentityService>, brandName?: string, companySlug?: string }} [options]
110
+ * companySlug — host-app portal branding key into D1 `company.slug` (never product-hardcoded here).
82
111
  */
83
112
  export async function handleIdentityWorkerRequest(request, env, options = {}) {
84
113
  const url = new URL(request.url);
85
114
  const path = url.pathname;
86
115
  const method = request.method.toUpperCase();
87
116
 
88
- const adapter = createCloudflareD1Adapter(env.DB);
117
+ const adapter = resolveIdentityAdapter(env, options);
89
118
  if (!options.identity && (!options.app?.id || !options.routeRegistry)) {
90
119
  throw new IdentityRoutingError(
91
120
  'AUTH_APP_UNRESOLVED',
@@ -156,6 +185,7 @@ export async function handleIdentityWorkerRequest(request, env, options = {}) {
156
185
  if (!ctx) return jsonResponse({ ok: false, error: 'session_required' }, 401);
157
186
  return jsonResponse({
158
187
  ok: true,
188
+ authenticated: true,
159
189
  user: {
160
190
  id: ctx.user.id,
161
191
  email: ctx.user.email,
@@ -165,8 +195,21 @@ export async function handleIdentityWorkerRequest(request, env, options = {}) {
165
195
  }
166
196
 
167
197
  if (path === '/api/company' && method === 'GET') {
168
- const slug = url.searchParams.get('slug') || undefined;
169
- const company = slug ? await adapter.getCompanyBySlug(slug) : await adapter.getDefaultCompany();
198
+ // Portable: ?slug= → options.companySlug → env override → host match in company.meta.hosts → default
199
+ const slug = String(
200
+ url.searchParams.get('slug')
201
+ || options.companySlug
202
+ || env.IDENTITY_COMPANY_SLUG
203
+ || '',
204
+ ).trim() || undefined;
205
+ let company = null;
206
+ if (slug) {
207
+ company = await adapter.getCompanyBySlug(slug);
208
+ }
209
+ if (!company && typeof adapter.getCompanyByHost === 'function') {
210
+ company = await adapter.getCompanyByHost(url.hostname || request.headers.get('host'));
211
+ }
212
+ if (!company) company = await adapter.getDefaultCompany();
170
213
  if (!company) return jsonResponse({ ok: false, error: 'company_not_found' }, 404);
171
214
  return jsonResponse({ ok: true, company });
172
215
  }
@@ -236,6 +279,12 @@ export async function handleIdentityWorkerRequest(request, env, options = {}) {
236
279
  if (path === '/api/oauth/google/desktop-exchange') {
237
280
  return handleGoogleDesktopExchangeRequest(request, env);
238
281
  }
282
+ if (path === GOOGLE_DESKTOP_LOGIN_EXCHANGE_PATH) {
283
+ return handleGoogleDesktopLoginExchangeRequest(request, env, { adapter, identity });
284
+ }
285
+ if (path === NATIVE_EXCHANGE_PATH) {
286
+ return handleNativeExchangeRequest(request, { adapter, identity });
287
+ }
239
288
  if (path === '/api/oauth/inneranimalmedia/callback' || path === '/api/oauth/iam/callback') {
240
289
  if (method === 'GET') {
241
290
  return iamPlatformOAuthCallback(request, env, adapter, identity);
@@ -332,12 +381,23 @@ async function oauthStart(request, env, identity, adapter, provider, creds) {
332
381
  const redirectTo = identity.resolvePostLoginPath(
333
382
  url.searchParams.get('next') || url.searchParams.get('return_to'),
334
383
  );
384
+ const nativeStart = parseNativeStart(url, env);
385
+ if (nativeStart.error) {
386
+ return jsonResponse({ ok: false, error: nativeStart.error }, 400);
387
+ }
335
388
  await adapter.saveOAuthState({
336
389
  state,
337
390
  provider,
338
391
  codeVerifier,
339
392
  redirectTo,
340
393
  appId: identity.app?.id || null,
394
+ ...(nativeStart.native
395
+ ? {
396
+ clientType: nativeStart.clientType,
397
+ nativeChallenge: nativeStart.nativeChallenge,
398
+ nativeRedirect: nativeStart.nativeRedirect,
399
+ }
400
+ : {}),
341
401
  });
342
402
 
343
403
  const redirectUri = `${url.origin}/api/oauth/${provider}/callback`;
@@ -458,21 +518,14 @@ async function oauthCallback(request, env, identity, adapter, provider, creds) {
458
518
  providerSubject: normalized.subject,
459
519
  email: normalized.email,
460
520
  displayName: normalized.name,
521
+ sessionType: sessionTypeForTransaction(saved),
461
522
  });
462
523
  await adapter.logAuthEvent({
463
524
  userId: result.authUserId, eventType: 'login', status: 'ok', provider, request,
525
+ metadata: { session_type: sessionTypeForTransaction(saved) },
464
526
  });
465
527
 
466
- const redirectTo = identity.resolvePostLoginPath(saved.redirect_to);
467
- const res = identity.buildLoginSuccessResponse(request, result.sessionId, redirectTo);
468
- const globeUrl = `${url.origin}${login}?globe_exit=1&next=${encodeURIComponent(redirectTo)}`;
469
- return new Response(null, {
470
- status: 302,
471
- headers: {
472
- Location: globeUrl,
473
- 'Set-Cookie': res.headers.get('Set-Cookie') || '',
474
- },
475
- });
528
+ return finishOAuthLogin({ request, env, identity, adapter, saved, result, loginPath: login });
476
529
  } catch (error) {
477
530
  const message = String(error?.message || error || 'oauth_callback_failed');
478
531
  console.error('oauth_callback_failed', provider, message);
@@ -484,4 +537,4 @@ async function oauthCallback(request, env, identity, adapter, provider, creds) {
484
537
  }
485
538
  }
486
539
 
487
- export { createIdentityService, createCloudflareD1Adapter };
540
+ export { createIdentityService, createCloudflareD1Adapter, createIamCompatIdentityAdapter, createPortableD1IdentityAdapter };