@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
@@ -4,7 +4,7 @@
4
4
  <head>
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Reset password | AgentSam</title>
7
+ <title>Reset password</title>
8
8
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
9
9
  rel="stylesheet">
10
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
@@ -280,8 +280,8 @@
280
280
  <div class="login-card">
281
281
  <img
282
282
  class="auth-logo"
283
- src="/shared/agentsam-mark.svg"
284
- alt="AgentSam"
283
+ src="/shared/app-icon.png"
284
+ alt="Sign in"
285
285
  style="width:120px;height:120px;object-fit:contain;display:block;margin:0 auto 20px;"
286
286
  />
287
287
 
@@ -290,7 +290,7 @@
290
290
  <p class="subtitle">We'll send you a secure verification code.</p>
291
291
 
292
292
  <div class="email-note">
293
- You'll receive an email from <strong data-company-support-email>support@agentsam.dev</strong>.<br>
293
+ You'll receive an email from <strong data-company-support-email>support@example.com</strong>.<br>
294
294
  Check your inbox and spam folder.
295
295
  </div>
296
296
 
@@ -365,7 +365,7 @@
365
365
  </div>
366
366
 
367
367
  <footer class="auth-min-footer" aria-label="Legal">
368
- <span data-company-name>AgentSam</span>
368
+ <span data-company-name>App</span>
369
369
  <span class="auth-min-footer-sep" aria-hidden="true">|</span>
370
370
  <a href="/privacy">Privacy</a>
371
371
  <span class="auth-min-footer-sep" aria-hidden="true">|</span>
@@ -4,7 +4,7 @@
4
4
  <head>
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Sign up | AgentSam</title>
7
+ <title>Sign up</title>
8
8
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
9
9
  rel="stylesheet">
10
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
@@ -398,12 +398,12 @@
398
398
  <div class="login-card">
399
399
  <img
400
400
  class="auth-logo"
401
- src="/shared/agentsam-mark.svg"
402
- alt="AgentSam"
401
+ src="/shared/app-icon.png"
402
+ alt="Sign in"
403
403
  />
404
404
 
405
405
  <h1>Create account</h1>
406
- <p class="subtitle" data-company-subtitle="signup">Join AgentSam</p>
406
+ <p class="subtitle" data-company-subtitle="signup">Create an account</p>
407
407
  <p id="registeredMsg" class="subtitle" style="color: #10B981; display: none; margin-top: -16px; margin-bottom: 16px;">Account created. Sign in below.</p>
408
408
  <p id="oauthErrorMsg" class="subtitle" style="color: #FF3B30; display: none; margin-top: -16px; margin-bottom: 16px;"></p>
409
409
  <p id="cfConnectNotice" class="subtitle" style="color: #fdba74; display: none; margin-top: -12px; margin-bottom: 12px; font-size: 12px; line-height: 1.45; text-align: center; max-width: 320px;">Sign in with Google, GitHub, or Supabase first, then connect Cloudflare from Integrations.</p>
@@ -485,7 +485,7 @@
485
485
  </div>
486
486
 
487
487
  <footer class="auth-min-footer" aria-label="Legal">
488
- <span data-company-name>AgentSam</span>
488
+ <span data-company-name>App</span>
489
489
  <span class="auth-min-footer-sep" aria-hidden="true">|</span>
490
490
  <a href="/privacy">Privacy</a>
491
491
  <span class="auth-min-footer-sep" aria-hidden="true">|</span>
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auth portal branding from D1 `company` via GET /api/company.
3
- * Static HTML keeps portable AgentSam defaults as fallback when host branding is unavailable.
3
+ * Static HTML keeps neutral portable defaults as fallback when host branding is unavailable.
4
4
  */
5
5
  (function () {
6
6
  var SUBTITLE_PREFIX = {
@@ -12,8 +12,6 @@
12
12
  var invoke = getInvoke();
13
13
  if (!invoke) return;
14
14
 
15
- var APP_ID = 'local-studio';
16
- var SESSION_ACCOUNT = 'identity_session';
17
15
  var originalFetch = window.fetch.bind(window);
18
16
 
19
17
  async function callIdentity(payload) {
@@ -27,29 +25,6 @@
27
25
  return body;
28
26
  }
29
27
 
30
- async function getSessionId() {
31
- return await invoke('secure_store_get', {
32
- appId: APP_ID,
33
- account: SESSION_ACCOUNT,
34
- });
35
- }
36
-
37
- async function setSessionId(sessionId) {
38
- await invoke('secure_store_set', {
39
- appId: APP_ID,
40
- account: SESSION_ACCOUNT,
41
- value: String(sessionId || ''),
42
- });
43
- }
44
-
45
- async function clearSessionId() {
46
- try {
47
- await invoke('secure_store_delete', {
48
- appId: APP_ID,
49
- account: SESSION_ACCOUNT,
50
- });
51
- } catch {}
52
- }
53
28
 
54
29
  function jsonResponse(body, status) {
55
30
  return new Response(JSON.stringify(body), {
@@ -87,9 +62,9 @@
87
62
 
88
63
  if (url.pathname === '/api/company') {
89
64
  return jsonResponse({
90
- name: 'AgentSam',
91
- tagline: 'Instant Access',
92
- logoUrl: '/shared/agentsam-mark.svg',
65
+ name: 'App',
66
+ tagline: 'Welcome',
67
+ logoUrl: '/shared/app-icon.png',
93
68
  authBgColor: '#050508',
94
69
  });
95
70
  }
@@ -103,7 +78,6 @@
103
78
  password: loginBody.password,
104
79
  next: loginBody.next || '/agentsam',
105
80
  });
106
- await setSessionId(login.session_id);
107
81
  notifyAuthenticated(login);
108
82
  return jsonResponse({ ok: true, user: login.user, redirect: '/agentsam' });
109
83
  } catch (error) {
@@ -121,7 +95,6 @@
121
95
  displayName: signupBody.name || signupBody.displayName || signupBody.display_name,
122
96
  next: signupBody.next || '/agentsam',
123
97
  });
124
- await setSessionId(signup.session_id);
125
98
  notifyAuthenticated(signup);
126
99
  return jsonResponse({ ok: true, user: signup.user, redirect: '/agentsam' });
127
100
  } catch (error) {
@@ -159,13 +132,9 @@
159
132
  }
160
133
 
161
134
  if (url.pathname === '/api/auth/logout') {
162
- var sessionId = await getSessionId();
163
- if (sessionId) {
164
- try {
165
- await callIdentity({ op: 'logout', session_id: sessionId });
166
- } catch {}
167
- }
168
- await clearSessionId();
135
+ try {
136
+ await callIdentity({ op: 'logout' });
137
+ } catch {}
169
138
  return jsonResponse({ ok: true });
170
139
  }
171
140
 
@@ -21,7 +21,21 @@ import {
21
21
  GcpProvider,
22
22
  EmailProvider,
23
23
  CloudflareProvider,
24
+ ChatGptProvider,
24
25
  } from './providers/index.js';
26
+ import {
27
+ STOCK_SIGN_IN_OPTIONS,
28
+ CMS_STOCK_SIGN_IN_IDS,
29
+ listStockSignInOptions,
30
+ getStockSignInOption,
31
+ } from './contracts/sign-in-options.js';
32
+ import {
33
+ createChatGptCmsAuthHost,
34
+ normalizeChatGptIdentity,
35
+ readChatGptUserFromHeaders,
36
+ chatGPTSignInPath,
37
+ chatGPTSignOutPath,
38
+ } from './providers/chatgpt/index.js';
25
39
 
26
40
  export {
27
41
  IdentityContractVersion,
@@ -40,6 +54,16 @@ export {
40
54
  GcpProvider,
41
55
  EmailProvider,
42
56
  CloudflareProvider,
57
+ ChatGptProvider,
58
+ STOCK_SIGN_IN_OPTIONS,
59
+ CMS_STOCK_SIGN_IN_IDS,
60
+ listStockSignInOptions,
61
+ getStockSignInOption,
62
+ createChatGptCmsAuthHost,
63
+ normalizeChatGptIdentity,
64
+ readChatGptUserFromHeaders,
65
+ chatGPTSignInPath,
66
+ chatGPTSignOutPath,
43
67
  };
44
68
 
45
69
  export * from './core/index.js';
@@ -88,11 +112,14 @@ export {
88
112
  handleGoogleDesktopExchangeRequest,
89
113
  resolveGoogleExchangeSecret,
90
114
  } from './oauth/google-desktop-exchange.js';
91
- export { createCloudflareD1Adapter } from './adapters/cloudflare-d1/index.js';
115
+ export { createCloudflareD1Adapter, createIamCompatIdentityAdapter } from './adapters/cloudflare-d1/index.js';
92
116
  export {
93
117
  createSqliteIdentityAdapter,
94
118
  applySqliteIdentityMigrations,
119
+ applyPortableIdentityMigrations,
120
+ resolveIdentitySchemaPack,
95
121
  } from './adapters/sqlite/index.js';
122
+ export { createPortableD1IdentityAdapter } from './adapters/portable-d1/index.js';
96
123
  export { createIdentityService } from './server/identity-service.js';
97
124
  export { handleIdentityWorkerRequest } from './server/worker-router.js';
98
125
 
@@ -1,31 +1,31 @@
1
- # Identity D1 migrations
1
+ # Identity migrations under `src/migrations/` — IAM-compat scaffold / history
2
2
 
3
- Customer-scoped identity tables for `@inneranimalmedia/agentsam-sdk` identity apps.
3
+ **This tree is NOT the portable customer D1 authority.**
4
4
 
5
- ## Apply
5
+ | Path | Authority |
6
+ |---|---|
7
+ | `packages/identity/migrations/sqlite/` | **Portable** `identity_*` SQL SSOT (`agentsam.identity` pack) |
8
+ | `packages/identity/src/migrations/` | **IAM-shaped** scaffold / history (accounts, auth_users, company, …) |
6
9
 
7
- ```bash
8
- # local
9
- wrangler d1 execute <database_name> --local --file=migrations/0001_identity_core.sql
10
+ ## When to use which
10
11
 
11
- # remote
12
- wrangler d1 execute <database_name> --remote --file=migrations/0001_identity_core.sql
13
- ```
12
+ | Audience | Adapter | Schema |
13
+ |---|---|---|
14
+ | New customer clean D1 / local SQLite | `createSqliteIdentityAdapter` / `createPortableD1IdentityAdapter` | Portable pack via `applyPortableIdentityMigrations` |
15
+ | Existing InnerAnimalMedia / Local Studio hosted DB | `createIamCompatIdentityAdapter` (`createCloudflareD1Adapter` alias) | Live IAM tables — do not rename for portability |
14
16
 
15
- `agentsam identity init` copies this migration into the customer project automatically.
16
-
17
- Existing scaffolds missing `accounts`: also apply `0003_accounts_ssot.sql`.
18
-
19
- ## Tables
17
+ ## IAM-shaped tables in this directory
20
18
 
21
19
  | Table | Purpose |
22
20
  |-------|---------|
23
- | `accounts` | **Account SSOT** — row of record (`au_*` ids shared with auth_users in portable scaffold) |
24
- | `auth_users` | Login principal (password hash, 1:1 with accounts.id) |
25
- | `auth_sessions` | Browser sessions |
26
- | `account_identities` | IdP linkage only (provider + subject → accounts.id) — **not** the account SSOT |
27
- | `oauth_states` | PKCE/state for OAuth start |
28
- | `password_reset_tokens` | Reset flow (grow when wired) |
29
- | `company` | Branding SSOT — name, logo, colors, support info (`GET /api/company`) |
30
-
31
- IAM production tables remain the reference; this schema is **portable and boring** for customer Workers.
21
+ | `accounts` | Account SSOT on hosted IAM |
22
+ | `auth_users` | Login principal |
23
+ | `auth_sessions` | Browser/desktop sessions |
24
+ | `account_identities` | IdP linkage |
25
+ | `oauth_states` | Historical PKCE/state (note: missing `app_id` vs portable invariant) |
26
+ | `password_reset_tokens` | Historical SQL reset tokens — runtime recovery uses KV/`createPasswordResetService` |
27
+ | `company` | Hosted branding table |
28
+
29
+ New generic scaffolds must be generated from the **portable** `agentsam.identity` pack, not by copying this tree as “portable D1.”
30
+
31
+ Preserve these files for IAM-compat fixtures and historical feature resources. Do not delete until Lane 1 parity is proven and call sites no longer need them.
@@ -4,7 +4,7 @@
4
4
  |------|----------|------|
5
5
  | **IAM platform (default)** | `IAM_CLIENT_ID` + `IAM_CLIENT_SECRET` | Minted at install/build for every customer worker |
6
6
  | Developer Google (web) | `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` | Takes `/api/oauth/google/start` when set |
7
- | Google Desktop / CLI | `GOOGLE_DESKTOP_CLIENT_ID` (+ optional `GOOGLE_DESKTOP_CLIENT_SECRET`) | `POST /api/oauth/google/desktop-exchange` (loopback PKCE) |
7
+ | Google Desktop / CLI | `GOOGLE_DESKTOP_CLIENT_ID` | Public Authorization Code + PKCE with loopback redirect; no desktop secret |
8
8
  | Developer GitHub | `GITHUB_CLIENT_ID` + `GITHUB_CLIENT_SECRET` | Takes `/api/oauth/github/start` when set |
9
9
  | Cloudflare sign-in | `CLOUDFLARE_OAUTH_CLIENT_ID` (+ optional secret) | Takes `/api/oauth/cloudflare/start` when set |
10
10
 
@@ -89,12 +89,11 @@ export function resolveOAuthCredentialLane(env, provider) {
89
89
 
90
90
  if (key === 'google_desktop') {
91
91
  const clientId = String(env?.GOOGLE_DESKTOP_CLIENT_ID || '').trim();
92
- const clientSecret = String(env?.GOOGLE_DESKTOP_CLIENT_SECRET || '').trim();
93
92
  if (clientId) {
94
93
  return {
95
94
  lane: 'byok_google_desktop',
96
95
  clientId,
97
- clientSecret,
96
+ clientSecret: '',
98
97
  provider: 'google_desktop',
99
98
  };
100
99
  }
@@ -122,18 +121,18 @@ export function resolveOAuthCredentialLane(env, provider) {
122
121
  return null;
123
122
  }
124
123
 
125
- // Default: Google/GitHub buttons route through IAM platform when minted.
126
- const iam = resolveIamPlatformCredentials(env);
127
- if (iam) {
124
+ if (key === 'chatgpt' || key === 'openai' || key === 'chatgpt_hosted') {
125
+ // Hosted Apps SDK identity — no OAuth client secret. Selection is always
126
+ // available; runtime proof comes from oai-authenticated-* request headers.
128
127
  return {
129
- lane: 'iam_platform',
130
- clientId: iam.clientId,
131
- clientSecret: iam.clientSecret,
132
- origin: iam.origin,
133
- issuer: iam.issuer,
134
- provider: 'inneranimalmedia',
128
+ lane: 'hosted_chatgpt',
129
+ clientId: String(env?.CHATGPT_APP_ID || env?.OPENAI_APP_ID || 'chatgpt-hosted').trim(),
130
+ clientSecret: '',
131
+ provider: 'chatgpt',
135
132
  };
136
133
  }
137
134
 
135
+ // Provider selection is fail-closed. A Google or GitHub button must never
136
+ // silently become InnerAnimalMedia/IAM. IAM remains an explicit provider.
138
137
  return null;
139
138
  }
@@ -1,10 +1,14 @@
1
+ import { fetchGoogleProfile } from '../providers/google/profile.js';
2
+ import { SESSION_TYPES } from '../core/session-policy.js';
3
+
1
4
  /**
2
5
  * Server-side Google token exchange for CLI / desktop PKCE.
3
6
  *
4
- * Desktop/"installed" OAuth clients must be type Desktop in Google Console (no secret).
5
- * If the configured desktop client_id was created as a Web client, Google demands a secret.
6
- * Secrets never leave the Worker: GOOGLE_DESKTOP_CLIENT_SECRET (optional) or GOOGLE_CLIENT_SECRET
7
- * when client_id matches the web client.
7
+ * Desktop/"installed" OAuth clients are public PKCE clients. Some Google
8
+ * installed-app credentials still include a provider-issued client_secret.
9
+ * That value is not a confidentiality boundary, but if Google requires it for
10
+ * token exchange the service may keep it in GOOGLE_DESKTOP_CLIENT_SECRET.
11
+ * Hosted web OAuth remains confidential and uses GOOGLE_CLIENT_SECRET.
8
12
  */
9
13
 
10
14
  const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
@@ -40,17 +44,18 @@ export function resolveGoogleExchangeSecret(env, clientId) {
40
44
  const id = clean(clientId);
41
45
  const desktopId = clean(env?.GOOGLE_DESKTOP_CLIENT_ID);
42
46
  const webId = clean(env?.GOOGLE_CLIENT_ID);
43
- const desktopSecret = clean(env?.GOOGLE_DESKTOP_CLIENT_SECRET);
44
47
  const webSecret = clean(env?.GOOGLE_CLIENT_SECRET);
48
+ const desktopSecret = clean(env?.GOOGLE_DESKTOP_CLIENT_SECRET);
45
49
 
46
- if (id && desktopId && id === desktopId && desktopSecret) {
47
- return { clientId: id, clientSecret: desktopSecret, mode: 'desktop_with_optional_secret' };
48
- }
49
50
  if (id && webId && id === webId && webSecret) {
50
51
  return { clientId: id, clientSecret: webSecret, mode: 'web_confidential' };
51
52
  }
52
53
  if (id && desktopId && id === desktopId) {
53
- return { clientId: id, clientSecret: null, mode: 'desktop_public_pkce' };
54
+ return {
55
+ clientId: id,
56
+ clientSecret: desktopSecret || null,
57
+ mode: 'desktop_public_pkce',
58
+ };
54
59
  }
55
60
  return { clientId: id || null, clientSecret: null, mode: 'unknown_client' };
56
61
  }
@@ -135,24 +140,6 @@ export async function handleGoogleDesktopExchangeRequest(request, env, opts = {}
135
140
  });
136
141
 
137
142
  const detail = String(result.data?.error_description || result.data?.error || '');
138
- if (
139
- !result.ok
140
- && /client_secret/i.test(detail)
141
- && resolved.mode === 'desktop_public_pkce'
142
- && !resolved.clientSecret
143
- ) {
144
- return json({
145
- ok: false,
146
- error: 'desktop_client_requires_secret',
147
- detail,
148
- remediation: [
149
- 'In Google Cloud Console create OAuth client type Desktop app (no secret) and set GOOGLE_DESKTOP_CLIENT_ID to that client id.',
150
- 'Or if this client id is a Web app, put its secret on the Worker: wrangler secret put GOOGLE_DESKTOP_CLIENT_SECRET',
151
- 'Web browser login continues to use GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET.',
152
- ],
153
- }, 502);
154
- }
155
-
156
143
  if (!result.ok) {
157
144
  return json({
158
145
  ok: false,
@@ -171,3 +158,184 @@ export async function handleGoogleDesktopExchangeRequest(request, env, opts = {}
171
158
  exchange_mode: resolved.mode,
172
159
  });
173
160
  }
161
+
162
+
163
+ export const GOOGLE_DESKTOP_LOGIN_EXCHANGE_PATH = '/api/oauth/google/desktop-login-exchange';
164
+ const GOOGLE_ID_TOKEN_INFO_URL = 'https://oauth2.googleapis.com/tokeninfo';
165
+
166
+ async function verifyGoogleDesktopIdToken(idToken, clientId, fetchImpl = fetch) {
167
+ const url = new URL(GOOGLE_ID_TOKEN_INFO_URL);
168
+ url.searchParams.set('id_token', clean(idToken));
169
+ const response = await fetchImpl(url, {
170
+ headers: { Accept: 'application/json' },
171
+ });
172
+ const data = await response.json().catch(() => ({}));
173
+ if (!response.ok) {
174
+ return {
175
+ ok: false,
176
+ error: 'google_id_token_invalid',
177
+ detail: clean(data.error_description || data.error),
178
+ };
179
+ }
180
+
181
+ const audience = clean(data.aud);
182
+ const issuer = clean(data.iss);
183
+ const expiresAt = Number(data.exp || 0);
184
+ const emailVerified = data.email_verified === true || String(data.email_verified) === 'true';
185
+
186
+ if (audience !== clean(clientId)) {
187
+ return { ok: false, error: 'google_id_token_audience_mismatch' };
188
+ }
189
+ if (!['accounts.google.com', 'https://accounts.google.com'].includes(issuer)) {
190
+ return { ok: false, error: 'google_id_token_issuer_invalid' };
191
+ }
192
+ if (!Number.isFinite(expiresAt) || expiresAt <= Math.floor(Date.now() / 1000)) {
193
+ return { ok: false, error: 'google_id_token_expired' };
194
+ }
195
+ if (!emailVerified) {
196
+ return { ok: false, error: 'google_email_not_verified' };
197
+ }
198
+
199
+ return { ok: true, data };
200
+ }
201
+
202
+ /**
203
+ * Google Desktop account identity handoff.
204
+ *
205
+ * The installed app is the public OAuth client. It performs Google
206
+ * Authorization Code + PKCE + loopback; this endpoint receives the one-time
207
+ * code + verifier, performs the provider token exchange, validates the Google
208
+ * ID token against GOOGLE_DESKTOP_CLIENT_ID, resolves the profile, and mints
209
+ * the normal AgentSam desktop session. No Google access token is persisted in
210
+ * the desktop app for identity-only sign-in.
211
+ */
212
+ export async function handleGoogleDesktopLoginExchangeRequest(request, env, opts = {}) {
213
+ if (request.method !== 'POST') {
214
+ return json({ ok: false, error: 'method_not_allowed' }, 405);
215
+ }
216
+ const identity = opts.identity;
217
+ const adapter = opts.adapter;
218
+ if (!identity || !adapter) {
219
+ return json({ ok: false, error: 'identity_service_unavailable' }, 503);
220
+ }
221
+
222
+ let payload = {};
223
+ try {
224
+ payload = await request.json();
225
+ } catch {
226
+ return json({ ok: false, error: 'invalid_json' }, 400);
227
+ }
228
+
229
+ let accessToken = clean(payload.access_token || payload.accessToken);
230
+ let idToken = clean(payload.id_token || payload.idToken);
231
+ const clientId = clean(payload.client_id || payload.clientId);
232
+
233
+ const desktopId = clean(env?.GOOGLE_DESKTOP_CLIENT_ID);
234
+ if (!desktopId || !clientId || clientId !== desktopId) {
235
+ return json({ ok: false, error: 'google_desktop_client_id_required' }, 403);
236
+ }
237
+
238
+ if (!accessToken || !idToken) {
239
+ const code = clean(payload.code);
240
+ const codeVerifier = clean(payload.code_verifier || payload.codeVerifier);
241
+ const redirectUri = clean(payload.redirect_uri || payload.redirectUri);
242
+ if (!code || !codeVerifier || !redirectUri) {
243
+ return json({
244
+ ok: false,
245
+ error: 'code_code_verifier_redirect_uri_required',
246
+ }, 400);
247
+ }
248
+ if (!isLoopbackRedirect(redirectUri)) {
249
+ return json({ ok: false, error: 'redirect_uri_must_be_loopback' }, 400);
250
+ }
251
+
252
+ const resolved = resolveGoogleExchangeSecret(env, clientId);
253
+ const exchanged = await exchangeGoogleAuthorizationCode({
254
+ code,
255
+ codeVerifier,
256
+ clientId,
257
+ redirectUri,
258
+ clientSecret: resolved.clientSecret,
259
+ fetchImpl: opts.fetchImpl || fetch,
260
+ });
261
+ if (!exchanged.ok) {
262
+ return json({
263
+ ok: false,
264
+ error: 'google_token_exchange_failed',
265
+ detail: clean(
266
+ exchanged.data?.error_description
267
+ || exchanged.data?.error
268
+ || ('http_' + exchanged.status),
269
+ ),
270
+ }, 502);
271
+ }
272
+ accessToken = clean(exchanged.data?.access_token);
273
+ idToken = clean(exchanged.data?.id_token);
274
+ if (!accessToken || !idToken) {
275
+ return json({ ok: false, error: 'google_token_payload_incomplete' }, 502);
276
+ }
277
+ }
278
+
279
+ const verified = await verifyGoogleDesktopIdToken(
280
+ idToken,
281
+ clientId,
282
+ opts.fetchImpl || fetch,
283
+ );
284
+ if (!verified.ok) {
285
+ return json({
286
+ ok: false,
287
+ error: verified.error,
288
+ detail: verified.detail || null,
289
+ }, 401);
290
+ }
291
+
292
+ const profile = opts.fetchProfile
293
+ ? await opts.fetchProfile(accessToken)
294
+ : await fetchGoogleProfile(accessToken);
295
+ if (!profile?.sub) {
296
+ return json({ ok: false, error: 'google_userinfo_failed' }, 502);
297
+ }
298
+ if (clean(verified.data.sub) && clean(verified.data.sub) !== clean(profile.sub)) {
299
+ return json({ ok: false, error: 'google_subject_mismatch' }, 401);
300
+ }
301
+ if (
302
+ clean(verified.data.email) &&
303
+ clean(profile.email) &&
304
+ clean(verified.data.email).toLowerCase() !== clean(profile.email).toLowerCase()
305
+ ) {
306
+ return json({ ok: false, error: 'google_email_mismatch' }, 401);
307
+ }
308
+
309
+ const provisioned = await identity.provisionOAuthUser({
310
+ provider: 'google',
311
+ providerSubject: String(profile.sub),
312
+ email: profile.email || verified.data.email || null,
313
+ displayName: profile.name || profile.email || verified.data.email || null,
314
+ sessionType: SESSION_TYPES.DESKTOP,
315
+ });
316
+ const user = await adapter.findUserById(provisioned.authUserId);
317
+ await adapter.logAuthEvent?.({
318
+ userId: provisioned.authUserId,
319
+ eventType: 'login',
320
+ status: 'ok',
321
+ provider: 'google',
322
+ request,
323
+ metadata: {
324
+ session_type: SESSION_TYPES.DESKTOP,
325
+ oauth_client_type: 'desktop_public_pkce',
326
+ },
327
+ });
328
+
329
+ return json({
330
+ ok: true,
331
+ authenticated: true,
332
+ session_id: provisioned.sessionId,
333
+ expires_at: provisioned.session?.expires_at ?? null,
334
+ user: user ? {
335
+ id: user.id,
336
+ email: user.email ?? null,
337
+ displayName: user.display_name ?? profile.name ?? null,
338
+ } : null,
339
+ exchange_mode: 'desktop_public_pkce_native_exchange',
340
+ });
341
+ }
@@ -5,6 +5,7 @@ import {
5
5
  resolveIamPlatformCredentials,
6
6
  } from './credentials.js';
7
7
  import { pkceChallenge, pkceVerifier, randomOAuthState } from './pkce.js';
8
+ import { finishOAuthLogin, parseNativeStart, sessionTypeForTransaction } from './native-handoff.js';
8
9
  import {
9
10
  exchangeIamCode,
10
11
  getIamAuthUrl,
@@ -33,12 +34,23 @@ export async function iamPlatformOAuthStart(request, env, adapter, identity) {
33
34
  url.searchParams.get('next') || url.searchParams.get('return_to'),
34
35
  ) || '/';
35
36
 
37
+ const nativeStart = parseNativeStart(url, env);
38
+ if (nativeStart.error) {
39
+ return Response.json({ ok: false, error: nativeStart.error }, { status: 400 });
40
+ }
36
41
  await adapter.saveOAuthState({
37
42
  state,
38
43
  provider: IAM_PLATFORM_STATE_PROVIDER,
39
44
  codeVerifier,
40
45
  redirectTo,
41
46
  appId: identity?.app?.id || null,
47
+ ...(nativeStart.native
48
+ ? {
49
+ clientType: nativeStart.clientType,
50
+ nativeChallenge: nativeStart.nativeChallenge,
51
+ nativeRedirect: nativeStart.nativeRedirect,
52
+ }
53
+ : {}),
42
54
  });
43
55
 
44
56
  const redirectUri = `${url.origin}${IAM_PLATFORM_CALLBACK_PATH}`;
@@ -106,17 +118,17 @@ export async function iamPlatformOAuthCallback(request, env, adapter, identity)
106
118
  providerSubject: normalized.subject,
107
119
  email: normalized.email,
108
120
  displayName: normalized.name || normalized.email.split('@')[0] || 'User',
121
+ sessionType: sessionTypeForTransaction(saved),
109
122
  });
110
123
 
111
- const redirectTo = identity.resolvePostLoginPath(saved.redirect_to);
112
- const res = identity.buildLoginSuccessResponse(request, result.sessionId, redirectTo);
113
- const globeUrl = `${url.origin}${identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN)}?globe_exit=1&next=${encodeURIComponent(redirectTo)}`;
114
- return new Response(null, {
115
- status: 302,
116
- headers: {
117
- Location: globeUrl,
118
- 'Set-Cookie': res.headers.get('Set-Cookie') || '',
119
- },
124
+ return finishOAuthLogin({
125
+ request,
126
+ env,
127
+ identity,
128
+ adapter,
129
+ saved,
130
+ result,
131
+ loginPath: identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN),
120
132
  });
121
133
  }
122
134