@inneranimalmedia/agentsam-sdk 2.6.3 → 2.6.4

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 (233) hide show
  1. package/AGENTSAM.md +6 -0
  2. package/README.md +16 -1
  3. package/bin/agentsam +16 -1
  4. package/docs/BRAND_INTELLIGENCE.md +1 -1
  5. package/docs/architecture/AGENTSAM_DISTRIBUTION_OWNERSHIP.md +176 -0
  6. package/docs/architecture/AGENTSAM_GO_RUNTIME.md +282 -0
  7. package/docs/architecture/CODEBASEINDEX_GUIDED_PATH.md +202 -0
  8. package/docs/architecture/FS_E2E_CLOSURE_RECEIPT.md +59 -0
  9. package/docs/architecture/LOCAL_FS_AUTHORITY.md +38 -0
  10. package/docs/architecture/LOCAL_STUDIO_CLOUDFLARE_OAUTH.md +28 -0
  11. package/docs/architecture/PLAN_CLI_AND_LOCAL_STUDIO_DESKTOP.md +96 -0
  12. package/docs/architecture/SAM_ACTIVITY_RECOVERY_RECEIPT.md +39 -0
  13. package/docs/architecture/SAM_DECISION_WORK_RECEIPT.md +54 -0
  14. package/docs/architecture/SAM_KERNEL.md +181 -0
  15. package/docs/architecture/SAM_MACHINE_NORMALIZATION_PRECOMMIT_REPORT.md +208 -0
  16. package/docs/architecture/SLASH_SKILLS_PORTABLE.md +116 -0
  17. package/docs/architecture/fs-e2e-receipt.latest.json +42 -0
  18. package/docs/architecture/previews/codebaseindex-guided-path/CODEBASEINDEX_GUIDED_PATH.md +202 -0
  19. package/docs/architecture/previews/codebaseindex-guided-path/index.html +321 -0
  20. package/migrations/d1/0010_portable_tickets_memory.sql +140 -0
  21. package/migrations/d1/0011_agentsam_skill_v2.sql +185 -0
  22. package/migrations/d1/0011b_agentsam_skill_v2_cutover.sql +22 -0
  23. package/migrations/d1/0011c_agentsam_skill_v2_backfill.sql +76 -0
  24. package/migrations/d1/0011d_agentsam_skill_v2_retrieval_revisions.sql +50 -0
  25. package/migrations/d1/0012_agentsam_tools_required_seed.sql +67 -0
  26. package/migrations/d1/0013_identity_oauth_states.sql +15 -0
  27. package/migrations/d1/0014_auth_event_log.sql +20 -0
  28. package/migrations/d1/0015_identity_oauth_state_app_id.sql +3 -0
  29. package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +16 -0
  30. package/migrations/sqlite/agentsam_skill_retrieval.portable.sql +42 -0
  31. package/package.json +19 -4
  32. package/packages/agentsam-contracts/src/errors.ts +16 -0
  33. package/packages/agentsam-errors/src/envelope.js +53 -0
  34. package/packages/agentsam-errors/src/index.js +1 -0
  35. package/packages/agentsam-errors/src/recovery.js +301 -0
  36. package/packages/agentsam-knowledge/src/providers/index.js +18 -6
  37. package/packages/connectors/cloudflare/src/routes.js +9 -0
  38. package/packages/connectors/cloudflare/tests/connector.test.mjs +26 -1
  39. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
  40. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +11 -2
  41. package/packages/identity/docs/PORTABLE_IDENTITY_ARCHITECTURE.md +50 -0
  42. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +31 -0
  43. package/packages/identity/migrations/sqlite/001_identity_core.sql +99 -0
  44. package/packages/identity/migrations/sqlite/002_identity_oauth_client.sql +38 -0
  45. package/packages/identity/migrations/sqlite/003_identity_oauth_server.sql +64 -0
  46. package/packages/identity/package.json +2 -2
  47. package/packages/identity/src/adapters/cloudflare-d1/index.js +122 -22
  48. package/packages/identity/src/adapters/sqlite/index.js +319 -0
  49. package/packages/identity/src/app/verify-app.js +95 -0
  50. package/packages/identity/src/contracts/identity-store.js +115 -0
  51. package/packages/identity/src/contracts/route-ids.js +30 -0
  52. package/packages/identity/src/contracts/route-projection.js +218 -0
  53. package/packages/identity/src/contracts/routes.js +11 -0
  54. package/packages/identity/src/core/browser-paths.js +4 -5
  55. package/packages/identity/src/core/constants.js +13 -8
  56. package/packages/identity/src/core/session-policy.js +32 -0
  57. package/packages/identity/src/frontend/auth-portal/pages/login.html +10 -10
  58. package/packages/identity/src/frontend/auth-portal/pages/reset.html +3 -3
  59. package/packages/identity/src/frontend/auth-portal/pages/signup.html +3 -3
  60. package/packages/identity/src/frontend/auth-portal/preview/dashboard-stub.html +1 -1
  61. package/packages/identity/src/index.js +25 -0
  62. package/packages/identity/src/oauth/README.md +10 -4
  63. package/packages/identity/src/oauth/credentials.js +20 -13
  64. package/packages/identity/src/oauth/finalize-inbound.js +1 -1
  65. package/packages/identity/src/oauth/iam-platform.js +8 -7
  66. package/packages/identity/src/oauth/redirect-paths.js +27 -36
  67. package/packages/identity/src/server/identity-service.js +30 -13
  68. package/packages/identity/src/server/mount-policy.js +30 -0
  69. package/packages/identity/src/server/post-auth.js +79 -0
  70. package/packages/identity/src/server/worker-router.js +104 -72
  71. package/packages/identity/tests/finalize-inbound-oauth.test.mjs +6 -6
  72. package/packages/identity/tests/iam-provider.test.mjs +1 -1
  73. package/packages/identity/tests/identity-service.test.mjs +36 -5
  74. package/packages/identity/tests/oauth-credentials.test.mjs +3 -1
  75. package/packages/identity/tests/portable-identity-architecture.test.mjs +157 -0
  76. package/packages/identity/tests/session-routes-policy.test.mjs +21 -0
  77. package/packages/theme-church-site/package.json +2 -1
  78. package/packages/theme-church-site/src/index.js +1 -0
  79. package/packages/theme-companions-site/package.json +2 -1
  80. package/packages/theme-companions-site/src/index.js +1 -0
  81. package/packages/theme-floors-site/package.json +2 -1
  82. package/packages/theme-floors-site/src/index.js +1 -0
  83. package/packages/theme-fuelnfree-site/package.json +2 -1
  84. package/packages/theme-fuelnfree-site/src/index.js +1 -0
  85. package/packages/theme-handyman-site/package.json +2 -1
  86. package/packages/theme-handyman-site/src/index.js +1 -0
  87. package/packages/theme-insurance-site/package.json +2 -1
  88. package/packages/theme-insurance-site/src/index.js +1 -0
  89. package/packages/theme-shinshu-site/package.json +2 -1
  90. package/packages/theme-shinshu-site/src/index.js +1 -0
  91. package/protocol/apps/agentsam.app.v1.schema.json +51 -0
  92. package/protocol/brand/brandpack.v1.schema.json +43 -0
  93. package/protocol/credentials/issue.v1.schema.json +38 -0
  94. package/protocol/database/connection.v1.schema.json +41 -0
  95. package/protocol/embeddings/embedding-profile.v1.schema.json +20 -0
  96. package/protocol/errors/error-envelope.schema.json +135 -1
  97. package/protocol/errors/recovery.v1.schema.json +50 -0
  98. package/protocol/runtime/workspace-fs.v1.schema.json +71 -0
  99. package/protocol/sam/activity.v1.schema.json +48 -0
  100. package/protocol/sam/answer.v1.schema.json +35 -0
  101. package/protocol/sam/calibration.v1.schema.json +21 -0
  102. package/protocol/sam/decision-receipt.v1.schema.json +29 -0
  103. package/protocol/sam/evaluation.v1.schema.json +19 -0
  104. package/protocol/sam/operation.schema.json +66 -0
  105. package/protocol/sam/outcome.v1.schema.json +36 -0
  106. package/protocol/sam/question.v1.schema.json +26 -0
  107. package/protocol/sam/registry.seed.json +153 -0
  108. package/protocol/sam/result.schema.json +53 -0
  109. package/protocol/sam/state.v1.schema.json +23 -0
  110. package/protocol/skills/agentsam.interaction.v1.schema.json +50 -0
  111. package/protocol/skills/agentsam.skill.v1.schema.json +57 -0
  112. package/protocol/ui/icon-registry.mjs +226 -0
  113. package/protocol/ui/icon.v1.schema.json +51 -0
  114. package/skills/README.md +22 -9
  115. package/skills/agentsam-codebaseindex/SKILL.md +225 -0
  116. package/skills/catalog.json +14 -0
  117. package/src/cli/command-catalog.js +130 -0
  118. package/src/cli/dispatch.js +48 -0
  119. package/src/cli.js +43 -1
  120. package/src/commands/api-key.js +244 -0
  121. package/src/commands/app.js +60 -28
  122. package/src/commands/brand.js +17 -19
  123. package/src/commands/codebaseindex.js +688 -0
  124. package/src/commands/env.js +152 -25
  125. package/src/commands/go.js +366 -53
  126. package/src/commands/interaction-clack.js +115 -0
  127. package/src/commands/models.js +1 -1
  128. package/src/commands/providers.js +62 -14
  129. package/src/commands/shell.js +43 -2
  130. package/src/commands/skill.js +248 -0
  131. package/src/commands/skills.js +1 -1
  132. package/src/commands/start-local.js +4 -0
  133. package/src/commands/whoami.js +90 -18
  134. package/src/go/build.js +229 -39
  135. package/src/go/cloudflare.js +506 -105
  136. package/src/go/container.js +120 -0
  137. package/src/go/contract.js +9 -4
  138. package/src/go/discover.js +149 -33
  139. package/src/go/index.js +15 -3
  140. package/src/go/native-probe-runner.mjs +119 -0
  141. package/src/go/{registry.js → official-registry.js} +64 -7
  142. package/src/go/official-release.js +10 -0
  143. package/src/go/receipts.js +77 -10
  144. package/src/go/verify.js +9 -2
  145. package/src/index.js +27 -0
  146. package/src/indexing/ingest/discover-models.js +298 -0
  147. package/src/indexing/ingest/inventory.js +243 -0
  148. package/src/indexing/ingest/job-graph.js +181 -0
  149. package/src/indexing/ingest/materials.js +210 -0
  150. package/src/lib/provider-credentials.js +63 -21
  151. package/src/lib/slash-commands.js +1 -0
  152. package/src/local-fs/capability.js +121 -0
  153. package/src/local-fs/freshness.js +75 -0
  154. package/src/local-fs/index.js +385 -0
  155. package/src/local-fs/paths.js +100 -0
  156. package/src/local-pty/server.js +295 -19
  157. package/src/mcp/client.js +2 -2
  158. package/src/models/ai-access-onboarding.js +112 -0
  159. package/src/models/discovery.js +10 -2
  160. package/src/models/inventory-core.js +9 -1
  161. package/src/sam/activity/index.js +183 -0
  162. package/src/sam/client.js +252 -0
  163. package/src/sam/decision/calibration.js +109 -0
  164. package/src/sam/decision/confidence.js +126 -0
  165. package/src/sam/decision/evaluate.js +157 -0
  166. package/src/sam/decision/evaluators/deterministic.js +341 -0
  167. package/src/sam/decision/evaluators/heuristic.js +61 -0
  168. package/src/sam/decision/evaluators/select.js +50 -0
  169. package/src/sam/decision/evaluators/semantic.js +149 -0
  170. package/src/sam/decision/hierarchical.js +61 -0
  171. package/src/sam/decision/index.js +53 -0
  172. package/src/sam/decision/policy.js +86 -0
  173. package/src/sam/decision/questions.js +120 -0
  174. package/src/sam/decision/receipt.js +148 -0
  175. package/src/sam/decision/state.js +117 -0
  176. package/src/sam/decision/types.js +22 -0
  177. package/src/sam/decision/validate.js +200 -0
  178. package/src/sam/define.js +51 -0
  179. package/src/sam/index.js +65 -0
  180. package/src/sam/operations/brand-scan.js +62 -0
  181. package/src/sam/operations/cad-blender-inspect.js +36 -0
  182. package/src/sam/operations/codebaseindex-ingest.js +49 -0
  183. package/src/sam/operations/decision-evaluate.js +59 -0
  184. package/src/sam/operations/planning-astar.js +77 -0
  185. package/src/sam/operations/planning-goap.js +60 -0
  186. package/src/sam/operations/repository-inspect.js +72 -0
  187. package/src/sam/operations/security-scan.js +33 -0
  188. package/src/sam/operations/terminal-exec.js +29 -0
  189. package/src/sam/planning/astar.js +311 -0
  190. package/src/sam/planning/goap.js +177 -0
  191. package/src/sam/planning/index.js +21 -0
  192. package/src/sam/planning/state.js +84 -0
  193. package/src/sam/registry.js +48 -0
  194. package/src/sam/result.js +77 -0
  195. package/src/sam/seed.js +44 -0
  196. package/src/sam/types.js +91 -0
  197. package/src/skills/catalog.js +64 -0
  198. package/src/skills/content-resolver.js +124 -0
  199. package/src/skills/hosted-store.js +37 -0
  200. package/src/skills/index.js +29 -64
  201. package/src/skills/interaction.js +102 -0
  202. package/src/skills/local-store.js +228 -0
  203. package/src/skills/manifest.js +104 -0
  204. package/src/skills/metrics.js +31 -0
  205. package/src/skills/registry.js +184 -0
  206. package/src/skills/runtime.js +287 -0
  207. package/src/skills/slash.js +44 -0
  208. package/src/ui/cli/help.js +94 -101
  209. package/test/cli/api-key-env-whoami.test.mjs +129 -0
  210. package/test/cli/codebaseindex-plan-ux.test.mjs +37 -0
  211. package/test/cli/go.test.mjs +62 -5
  212. package/test/cli/skill-npm-and-env.test.mjs +52 -0
  213. package/test/cli/wireframes-go-registry.test.mjs +87 -2
  214. package/test/go/build-source-identity.test.mjs +31 -0
  215. package/test/go/cloudflare-probe.test.mjs +274 -10
  216. package/test/go/distribution.test.mjs +28 -0
  217. package/test/integration/ai-access-onboarding.test.mjs +47 -0
  218. package/test/integration/cli-help.test.mjs +1 -1
  219. package/test/integration/cms-site-tenancy-contract.test.mjs +6 -6
  220. package/test/integration/codebaseindex-ingest.test.mjs +166 -0
  221. package/test/integration/icon-registry.test.mjs +67 -0
  222. package/test/integration/ingest-discover-models.test.mjs +30 -0
  223. package/test/integration/install-script.test.mjs +12 -9
  224. package/test/integration/local-fs.test.mjs +113 -0
  225. package/test/integration/provider-env-cli.test.mjs +2 -1
  226. package/test/integration/sam-activity-recovery.test.mjs +147 -0
  227. package/test/integration/sam-decision.test.mjs +584 -0
  228. package/test/integration/sam-kernel.test.mjs +99 -0
  229. package/test/integration/sam-planning-astar.test.mjs +279 -0
  230. package/test/integration/skill-runtime.test.mjs +240 -0
  231. package/test/integration/studio-fs-pty-e2e.test.mjs +294 -0
  232. package/test/models.test.mjs +14 -7
  233. package/test/shell.test.mjs +4 -4
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Host/app HTTP projection of semantic identity route IDs.
3
+ * Identity core never hardcodes /auth/login, /agentsam, etc.
4
+ */
5
+
6
+ import { IDENTITY_ROUTE_IDS, REQUIRED_AUTH_ROUTE_IDS } from './route-ids.js';
7
+ import { IdentityRoutingError } from './identity-store.js';
8
+ import { sanitizeBrowserNextPath } from '../core/browser-paths.js';
9
+
10
+ /**
11
+ * @typedef {{ path: string, auth?: 'required'|'optional'|'public', spa?: boolean, shell?: string, pattern?: string }} RouteProjection
12
+ * @typedef {Record<string, string|RouteProjection>} RouteProjectionMap
13
+ */
14
+
15
+ /**
16
+ * @param {{ appId: string, routes: RouteProjectionMap }} input
17
+ */
18
+ export function defineRouteProjection(input) {
19
+ const appId = String(input?.appId || '').trim();
20
+ if (!appId) throw new IdentityRoutingError('AUTH_APP_UNRESOLVED', 'defineRouteProjection requires appId');
21
+ /** @type {Map<string, RouteProjection>} */
22
+ const routes = new Map();
23
+ for (const [routeId, value] of Object.entries(input.routes || {})) {
24
+ const path = typeof value === 'string' ? value : value?.path;
25
+ if (!path || !String(path).startsWith('/')) {
26
+ throw new IdentityRoutingError('AUTH_ROUTE_PROJECTION_INVALID', `Invalid path for ${routeId}`, { appId, routeId });
27
+ }
28
+ routes.set(routeId, Object.freeze({
29
+ path: String(path),
30
+ auth: typeof value === 'object' ? value.auth : undefined,
31
+ spa: typeof value === 'object' ? value.spa : undefined,
32
+ shell: typeof value === 'object' ? value.shell : undefined,
33
+ pattern: typeof value === 'object' ? value.pattern : undefined,
34
+ }));
35
+ }
36
+ return Object.freeze({ appId, routes });
37
+ }
38
+
39
+ /**
40
+ * @param {ReturnType<typeof defineRouteProjection>[]} projections
41
+ */
42
+ export function createRouteRegistry(projections = []) {
43
+ /** @type {Map<string, ReturnType<typeof defineRouteProjection>>} */
44
+ const byApp = new Map();
45
+ for (const p of projections) {
46
+ if (!p?.appId) continue;
47
+ byApp.set(p.appId, p);
48
+ }
49
+
50
+ return Object.freeze({
51
+ get(appId) {
52
+ return byApp.get(String(appId || '')) || null;
53
+ },
54
+
55
+ /**
56
+ * @param {string} appId
57
+ * @param {string} routeId
58
+ * @returns {string|null}
59
+ */
60
+ resolve(appId, routeId) {
61
+ const proj = byApp.get(String(appId || ''));
62
+ if (!proj) return null;
63
+ const entry = proj.routes.get(routeId);
64
+ return entry?.path || null;
65
+ },
66
+
67
+ /**
68
+ * Expand parameterized patterns, e.g. /api/oauth/:provider/callback
69
+ * @param {string} appId
70
+ * @param {string} routeId
71
+ * @param {Record<string, string>} params
72
+ */
73
+ resolvePath(appId, routeId, params = {}) {
74
+ const pattern = this.resolve(appId, routeId);
75
+ if (!pattern) return null;
76
+ return pattern.replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, (_, key) => {
77
+ const v = params[key];
78
+ if (v == null || v === '') {
79
+ throw new IdentityRoutingError('AUTH_ROUTE_PARAM_MISSING', `Missing :${key} for ${routeId}`, { appId, routeId });
80
+ }
81
+ return encodeURIComponent(String(v));
82
+ });
83
+ },
84
+
85
+ /**
86
+ * return_to must be same-origin relative and owned by this app's projected mounts/paths.
87
+ * @param {{ appId: string, value?: string|null }} opts
88
+ */
89
+ resolveReturnTo({ appId, value }) {
90
+ const cleaned = sanitizeBrowserNextPath(value);
91
+ if (!cleaned) return null;
92
+ const pathname = cleaned.split('?')[0];
93
+ const proj = byApp.get(String(appId || ''));
94
+ if (!proj) return null;
95
+
96
+ const authenticated = proj.routes.get(IDENTITY_ROUTE_IDS.APP_AUTHENTICATED)?.path
97
+ || proj.routes.get(IDENTITY_ROUTE_IDS.APP_HOME)?.path;
98
+ if (authenticated && (pathname === authenticated || pathname.startsWith(`${authenticated}/`))) {
99
+ return cleaned;
100
+ }
101
+
102
+ for (const [routeId, entry] of proj.routes) {
103
+ if (routeId.startsWith('identity.')) continue;
104
+ const base = entry.path.replace(/:[^/]+/g, ''); // strip params for prefix mounts
105
+ const mount = base.replace(/\/$/, '') || entry.path;
106
+ if (pathname === mount || pathname.startsWith(`${mount}/`)) return cleaned;
107
+ // Explicit mount keys: app.mount./projects style — also accept path values with auth required
108
+ if (entry.auth === 'required' && (pathname === entry.path || pathname.startsWith(`${entry.path}/`))) {
109
+ return cleaned;
110
+ }
111
+ }
112
+ return null;
113
+ },
114
+
115
+ /**
116
+ * Fail closed if auth-capable app is missing required semantic projections.
117
+ * @param {string} appId
118
+ */
119
+ assertAuthCapable(appId) {
120
+ const proj = byApp.get(String(appId || ''));
121
+ if (!proj) {
122
+ throw new IdentityRoutingError('AUTH_APP_UNRESOLVED', `No route projection for app ${appId}`, { appId });
123
+ }
124
+ for (const routeId of REQUIRED_AUTH_ROUTE_IDS) {
125
+ if (!proj.routes.has(routeId)) {
126
+ throw new IdentityRoutingError(
127
+ 'AUTH_ROUTE_MISSING',
128
+ `App ${appId} missing required route projection ${routeId}`,
129
+ { appId, routeId },
130
+ );
131
+ }
132
+ }
133
+ return proj;
134
+ },
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Build a projection map from an agentsam.app.v1 auth+routes manifest section.
140
+ * @param {object} manifest
141
+ */
142
+ export function projectionFromAppManifest(manifest) {
143
+ const appId = String(manifest?.id || '').trim();
144
+ if (!appId) throw new IdentityRoutingError('AUTH_APP_UNRESOLVED', 'manifest.id required');
145
+
146
+ /** @type {RouteProjectionMap} */
147
+ const routes = {};
148
+
149
+ const rawRoutes = manifest.routes || {};
150
+ for (const [routeId, value] of Object.entries(rawRoutes)) {
151
+ if (routeId === 'entry' || routeId === 'mounts' || routeId === 'paths') continue;
152
+ routes[routeId] = value;
153
+ }
154
+ for (const [routeId, value] of Object.entries(rawRoutes.paths || {})) {
155
+ routes[routeId] = value;
156
+ }
157
+
158
+ const auth = manifest.auth || {};
159
+ const entry = auth.entry || {};
160
+
161
+ function pathFor(ref, legacyPath) {
162
+ if (legacyPath) return legacyPath;
163
+ if (!ref) return null;
164
+ if (typeof ref === 'string' && ref.startsWith('/')) return ref;
165
+ if (typeof ref === 'string' && routes[ref]) {
166
+ const v = routes[ref];
167
+ return typeof v === 'string' ? v : v.path;
168
+ }
169
+ return null;
170
+ }
171
+
172
+ const authenticatedPath = pathFor(
173
+ entry.authenticated || IDENTITY_ROUTE_IDS.APP_AUTHENTICATED,
174
+ typeof rawRoutes.entry === 'string' ? rawRoutes.entry : null,
175
+ );
176
+ const loginPath = pathFor(entry.login || IDENTITY_ROUTE_IDS.LOGIN, null);
177
+ const failurePath = pathFor(
178
+ entry.failure || IDENTITY_ROUTE_IDS.RECOVERY || IDENTITY_ROUTE_IDS.LOGIN,
179
+ loginPath,
180
+ );
181
+
182
+ if (authenticatedPath) {
183
+ routes[IDENTITY_ROUTE_IDS.APP_AUTHENTICATED] = {
184
+ path: authenticatedPath,
185
+ auth: 'required',
186
+ spa: true,
187
+ shell: manifest.runtime?.shell
188
+ ? `${manifest.runtime.assetRoot || 'dist'}/${manifest.runtime.shell}`.replace(/\/{2,}/g, '/')
189
+ : undefined,
190
+ };
191
+ routes[IDENTITY_ROUTE_IDS.APP_HOME] = authenticatedPath;
192
+ }
193
+ if (loginPath) routes[IDENTITY_ROUTE_IDS.LOGIN] = loginPath;
194
+ if (failurePath) routes[IDENTITY_ROUTE_IDS.RECOVERY] = failurePath;
195
+
196
+ for (const mount of rawRoutes.mounts || []) {
197
+ if (!mount?.path) continue;
198
+ routes[`app.mount.${mount.path}`] = {
199
+ path: mount.path,
200
+ auth: mount.auth || 'required',
201
+ spa: mount.spa,
202
+ shell: mount.shell,
203
+ };
204
+ }
205
+
206
+ if (auth.oauth?.callbackPattern) {
207
+ routes[IDENTITY_ROUTE_IDS.OAUTH_CALLBACK] = auth.oauth.callbackPattern;
208
+ } else if (!routes[IDENTITY_ROUTE_IDS.OAUTH_CALLBACK]) {
209
+ routes[IDENTITY_ROUTE_IDS.OAUTH_CALLBACK] = '/api/oauth/:provider/callback';
210
+ }
211
+ if (auth.oauth?.startPattern) {
212
+ routes[IDENTITY_ROUTE_IDS.OAUTH_START] = auth.oauth.startPattern;
213
+ } else if (!routes[IDENTITY_ROUTE_IDS.OAUTH_START]) {
214
+ routes[IDENTITY_ROUTE_IDS.OAUTH_START] = '/api/oauth/:provider/start';
215
+ }
216
+
217
+ return defineRouteProjection({ appId, routes });
218
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Portable identity = semantic route IDs only.
3
+ * Concrete paths live in host defineRouteProjection / agentsam.app.json.
4
+ */
5
+
6
+ export { IDENTITY_ROUTE_IDS, REQUIRED_AUTH_ROUTE_IDS } from './route-ids.js';
7
+ export {
8
+ defineRouteProjection,
9
+ createRouteRegistry,
10
+ projectionFromAppManifest,
11
+ } from './route-projection.js';
@@ -1,4 +1,7 @@
1
- import { AUTH_LOGIN_PATH, AUTH_SIGNUP_PATH } from './constants.js';
1
+ /**
2
+ * Same-origin relative path sanitizer — no product-path rewriting.
3
+ * Alias normalization (/login → identity.login projection) is a host concern.
4
+ */
2
5
 
3
6
  export function sanitizeBrowserNextPath(raw) {
4
7
  if (raw == null) return null;
@@ -13,10 +16,6 @@ export function sanitizeBrowserNextPath(raw) {
13
16
  pathname = s.slice(0, q);
14
17
  search = s.slice(q);
15
18
  }
16
- const lower = pathname.toLowerCase();
17
- if (lower === '/login' || lower === '/auth/signin') pathname = AUTH_LOGIN_PATH;
18
- else if (lower === '/signup' || lower === '/auth/register') pathname = AUTH_SIGNUP_PATH;
19
-
20
19
  return pathname + search;
21
20
  }
22
21
 
@@ -1,9 +1,14 @@
1
- /** SDK-portable session/auth path constants. */
1
+ /**
2
+ * Cookie name + session policy re-exports.
3
+ * Route vocabulary → contracts/routes.js (identity endpoints only).
4
+ * Product homes / mounts → each app's agentsam.app.json (not this package).
5
+ */
6
+
7
+ import { SESSION_POLICY } from './session-policy.js';
8
+
9
+ export { SESSION_POLICY, clampAgentSessionTtl } from './session-policy.js';
10
+
2
11
  export const AUTH_COOKIE_NAME = 'session';
3
- export const AUTH_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
4
- export const MIN_AGENT_SESSION_TTL_SECONDS = 60;
5
- export const MAX_AGENT_SESSION_TTL_SECONDS = 86400;
6
- export const DEFAULT_AGENT_SESSION_TTL_SECONDS = 900;
7
- export const AUTH_LOGIN_PATH = '/auth/login';
8
- export const AUTH_SIGNUP_PATH = '/auth/signup';
9
- export const DASHBOARD_AFTER_LOGIN_PATH = '/dashboard/cms';
12
+
13
+ /** @deprecated Prefer SESSION_POLICY.browser.ttlSeconds */
14
+ export const AUTH_SESSION_TTL_SECONDS = SESSION_POLICY.browser.ttlSeconds;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Session policy — three concerns stay separate:
3
+ * browser = normal logged-in account session (cookie / auth_sessions)
4
+ * agent = short-lived delegated/runtime grant (execution, terminal, sandbox)
5
+ *
6
+ * Product routing does not live here.
7
+ */
8
+
9
+ export const SESSION_POLICY = Object.freeze({
10
+ browser: Object.freeze({
11
+ ttlSeconds: 30 * 24 * 60 * 60,
12
+ }),
13
+ /** Temporary execution authority — not the user's Local Studio login. */
14
+ agent: Object.freeze({
15
+ minTtlSeconds: 60,
16
+ defaultTtlSeconds: 15 * 60,
17
+ maxTtlSeconds: 24 * 60 * 60,
18
+ }),
19
+ });
20
+
21
+ /**
22
+ * Clamp a requested agent/runtime grant TTL into policy bounds.
23
+ * @param {number|null|undefined} requestedSeconds
24
+ */
25
+ export function clampAgentSessionTtl(requestedSeconds) {
26
+ const n = Number(requestedSeconds);
27
+ if (!Number.isFinite(n) || n <= 0) return SESSION_POLICY.agent.defaultTtlSeconds;
28
+ return Math.min(
29
+ SESSION_POLICY.agent.maxTtlSeconds,
30
+ Math.max(SESSION_POLICY.agent.minTtlSeconds, Math.floor(n)),
31
+ );
32
+ }
@@ -404,15 +404,15 @@
404
404
  <button type="submit" id="submitBtn" class="btn-primary">Connect</button>
405
405
 
406
406
  <div class="oauth-stack">
407
- <a id="googleSignIn" href="/api/oauth/google/start?login=1&return_to=/dashboard/agent" class="btn-primary" style="background: #ffffff; color: #1E293B; text-decoration: none; padding: 12px 24px; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; gap: 8px;">
407
+ <a id="googleSignIn" href="/api/oauth/google/start?login=1&return_to=/" class="btn-primary" style="background: #ffffff; color: #1E293B; text-decoration: none; padding: 12px 24px; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; gap: 8px;">
408
408
  <svg width="18" height="18" viewBox="0 0 24 24"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
409
409
  Sign in with Google
410
410
  </a>
411
- <a id="githubSignIn" href="/api/oauth/github/start?next=/dashboard/agent" class="btn-primary" style="background: rgba(255,255,255,0.1); color: white; text-decoration: none; padding: 12px 24px; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; gap: 8px;">
411
+ <a id="githubSignIn" href="/api/oauth/github/start?next=/" class="btn-primary" style="background: rgba(255,255,255,0.1); color: white; text-decoration: none; padding: 12px 24px; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; gap: 8px;">
412
412
  <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.43 9.8 8.2 11.38.6.11.82-.26.82-.58 0-.29-.01-1.06-.01-2.08-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.08 1.83 1.23 1.83 1.23 1.07 1.83 2.81 1.3 3.49.99.11-.78.42-1.3.76-1.6-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.12-.3-.54-1.52.12-3.18 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 013-.41c1.02.01 2.05.14 3 .41 2.29-1.55 3.3-1.23 3.3-1.23.66 1.66.24 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.8 5.63-5.48 5.92.43.37.81 1.1.81 2.22 0 1.6-.01 2.89-.01 3.28 0 .32.21.69.83.57C20.57 21.79 24 17.3 24 12c0-6.63-5.37-12-12-12z"/></svg>
413
413
  Sign in with GitHub
414
414
  </a>
415
- <a id="cloudflareSignIn" href="/api/oauth/cloudflare/start?next=/dashboard/agent" class="btn-primary" style="background: #F6821F; color: white; text-decoration: none; padding: 12px 24px; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; gap: 8px;">
415
+ <a id="cloudflareSignIn" href="/api/oauth/cloudflare/start" class="btn-primary" style="background: #F6821F; color: white; text-decoration: none; padding: 12px 24px; border-radius: 10px; font-weight: 600; font-size: 14px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; gap: 8px;">
416
416
  Sign in with Cloudflare
417
417
  </a>
418
418
  </div>
@@ -466,13 +466,13 @@
466
466
  var flow = qs.get('flow');
467
467
  var challenge = qs.get('challenge');
468
468
  var nextParam = qs.get('next');
469
- var oauthDest = '/dashboard/agent';
469
+ // No product-home default here — host bounce supplies ?next=; otherwise
470
+ // the Worker resolves against the app manifest entry after auth.
471
+ var oauthDest = '/';
470
472
  if (flow === 'oauth' && challenge && challenge.indexOf('olc_') === 0) {
471
473
  oauthDest = '/api/oauth/login-challenge/resume?challenge=' + encodeURIComponent(challenge);
472
474
  } else if (nextParam && nextParam.startsWith('/') && !nextParam.startsWith('//')) {
473
- if (nextParam.indexOf('/dashboard/settings/integrations') !== 0) {
474
- oauthDest = nextParam;
475
- }
475
+ oauthDest = nextParam;
476
476
  }
477
477
  var googleLink = document.getElementById('googleSignIn');
478
478
  if (googleLink) {
@@ -571,7 +571,7 @@
571
571
  if (res.ok && data.ok) {
572
572
  btn.textContent = 'Success';
573
573
  btn.style.background = '#10B981';
574
- var redirectUrl = data.redirect || '/dashboard/agent';
574
+ var redirectUrl = data.redirect || '/';
575
575
  if (typeof window.runGlobeExitTransition === 'function') {
576
576
  window.runGlobeExitTransition(redirectUrl);
577
577
  } else {
@@ -803,8 +803,8 @@
803
803
  (function() {
804
804
  var params = new URLSearchParams(window.location.search);
805
805
  if (params.get('globe_exit') !== '1') return;
806
- var next = params.get('next') || '/dashboard/agent';
807
- if (!next.startsWith('/') || next.startsWith('//')) next = '/dashboard/agent';
806
+ var next = params.get('next') || '/';
807
+ if (!next.startsWith('/') || next.startsWith('//')) next = '/';
808
808
  document.body.classList.add('oauth-globe-exit');
809
809
  var tries = 0;
810
810
  var maxFrames = 180;
@@ -346,11 +346,11 @@
346
346
  </form>
347
347
 
348
348
  <div style="margin: 20px 0 0; width: 100%; text-align: center; display: flex; flex-direction: column; gap: 12px;">
349
- <a id="googleSignIn" href="/api/oauth/google/start?login=1&return_to=/dashboard/agent" class="oauth-btn oauth-google">
349
+ <a id="googleSignIn" href="/api/oauth/google/start?login=1&return_to=/" class="oauth-btn oauth-google">
350
350
  <svg width="18" height="18" viewBox="0 0 24 24"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
351
351
  Sign in with Google
352
352
  </a>
353
- <a id="githubSignIn" href="/api/oauth/github/start?next=/dashboard/agent" class="oauth-btn oauth-github">
353
+ <a id="githubSignIn" href="/api/oauth/github/start?next=/" class="oauth-btn oauth-github">
354
354
  <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.43 9.8 8.2 11.38.6.11.82-.26.82-.58 0-.29-.01-1.06-.01-2.08-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.08 1.83 1.23 1.83 1.23 1.07 1.83 2.81 1.3 3.49.99.11-.78.42-1.3.76-1.6-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.12-.3-.54-1.52.12-3.18 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 013-.41c1.02.01 2.05.14 3 .41 2.29-1.55 3.3-1.23 3.3-1.23.66 1.66.24 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.8 5.63-5.48 5.92.43.37.81 1.1.81 2.22 0 1.6-.01 2.89-.01 3.28 0 .32.21.69.83.57C20.57 21.79 24 17.3 24 12c0-6.63-5.37-12-12-12z"/></svg>
355
355
  Sign in with GitHub
356
356
  </a>
@@ -385,7 +385,7 @@
385
385
  var backLink = document.getElementById('backToLogin');
386
386
  if (backLink) backLink.href = loginHref;
387
387
 
388
- var oauthDest = '/dashboard/agent';
388
+ var oauthDest = '/';
389
389
  if (nextParam && nextParam.startsWith('/') && !nextParam.startsWith('//')) {
390
390
  oauthDest = nextParam;
391
391
  }
@@ -582,7 +582,7 @@
582
582
  }
583
583
  btn.textContent = 'Success';
584
584
  btn.style.background = '#10B981';
585
- var redirectUrl = data.redirect || '/dashboard/overview';
585
+ var redirectUrl = data.redirect || '/';
586
586
  if (typeof window.runGlobeExitTransition === 'function') {
587
587
  window.runGlobeExitTransition(redirectUrl);
588
588
  } else {
@@ -814,8 +814,8 @@
814
814
  (function() {
815
815
  var params = new URLSearchParams(window.location.search);
816
816
  if (params.get('globe_exit') !== '1') return;
817
- var next = params.get('next') || '/dashboard/overview';
818
- if (!next.startsWith('/') || next.startsWith('//')) next = '/dashboard/overview';
817
+ var next = params.get('next') || '/';
818
+ if (!next.startsWith('/') || next.startsWith('//')) next = '/';
819
819
  document.body.classList.add('oauth-globe-exit');
820
820
  var tries = 0;
821
821
  var maxFrames = 180;
@@ -15,7 +15,7 @@
15
15
  <body>
16
16
  <main>
17
17
  <h1>Post-login (preview stub)</h1>
18
- <p>In production you would land on <code>/dashboard/agent</code> (or your <code>next</code> URL) after OAuth or password sign-in.</p>
18
+ <p>In production you would land on <code>/</code> (or your <code>next</code> URL) after OAuth or password sign-in.</p>
19
19
  <p><a href="/auth/login">Back to sign in</a> · <a href="/">Preview hub</a></p>
20
20
  </main>
21
21
  </body>
@@ -44,6 +44,27 @@ export {
44
44
 
45
45
  export * from './core/index.js';
46
46
  export * from './core/constants.js';
47
+ export {
48
+ IDENTITY_ROUTE_IDS,
49
+ REQUIRED_AUTH_ROUTE_IDS,
50
+ defineRouteProjection,
51
+ createRouteRegistry,
52
+ projectionFromAppManifest,
53
+ } from './contracts/routes.js';
54
+ export {
55
+ IDENTITY_STORE_SCHEMA_VERSION,
56
+ IDENTITY_PACKS,
57
+ IdentityRoutingError,
58
+ IdentitySchemaError,
59
+ } from './contracts/identity-store.js';
60
+ export {
61
+ resolvePostAuthDestination,
62
+ beginLoginRedirect,
63
+ resolveMountForPath,
64
+ mountRequiresAuth,
65
+ pathMatchesMount,
66
+ } from './server/post-auth.js';
67
+ export { verifyAppAuthContract, verifyAppPackage } from './app/verify-app.js';
47
68
  export { DEFAULT_COMPANY_ID, DEFAULT_COMPANY_SLUG, normalizeCompanyRow } from './contracts/company.js';
48
69
  export * from './contracts/auth-config.js';
49
70
  export { createPasswordResetService } from './recovery/password-reset.js';
@@ -52,6 +73,10 @@ export {
52
73
  registerFinalizeInboundOAuth,
53
74
  } from './oauth/callback.js';
54
75
  export { createCloudflareD1Adapter } from './adapters/cloudflare-d1/index.js';
76
+ export {
77
+ createSqliteIdentityAdapter,
78
+ applySqliteIdentityMigrations,
79
+ } from './adapters/sqlite/index.js';
55
80
  export { createIdentityService } from './server/identity-service.js';
56
81
  export { handleIdentityWorkerRequest } from './server/worker-router.js';
57
82
 
@@ -21,12 +21,18 @@ npx wrangler secret put IAM_CLIENT_SECRET
21
21
 
22
22
  These routes live in the **customer** worker (`handleIdentityWorkerRequest`):
23
23
 
24
- 1. `/api/oauth/iam/start` → redirects to IAM AS (requires minted `IAM_CLIENT_*`)
25
- 2. `/api/oauth/google/start` → BYOK Google if `GOOGLE_*` set, else IAM if minted, else 503
24
+ 1. `/api/oauth/inneranimalmedia/start` → redirects to IAM AS (requires minted `IAM_CLIENT_*`)
25
+ - Legacy alias: `/api/oauth/iam/start`
26
+ 2. `/api/oauth/google/start` → BYOK Google if `GOOGLE_*` set, else platform lane if minted, else 503
26
27
  3. `/api/oauth/github/start` → same for GitHub
27
- 4. Callback: `/api/oauth/iam/callback` (IAM lane) or `/api/oauth/{google|github}/callback` (BYOK)
28
+ 4. Callback: `/api/oauth/inneranimalmedia/callback` (platform lane) or `/api/oauth/{google|github|cloudflare}/callback` (BYOK)
29
+ - Legacy alias: `/api/oauth/iam/callback`
28
30
 
29
- Register IAM redirect URI: `https://<customer-host>/api/oauth/iam/callback`
31
+ Register platform redirect URI on the IAM client:
32
+
33
+ `https://<customer-host>/api/oauth/inneranimalmedia/callback`
34
+
35
+ (Keep the legacy `/api/oauth/iam/callback` registered during migration.)
30
36
 
31
37
  ## IAM authorization server endpoints (issuer)
32
38
 
@@ -8,13 +8,17 @@ import { DEFAULT_IAM_ORIGIN, resolveIamOrigin } from '../contracts/auth-config.j
8
8
  * — SECRET is wrangler secret put only (encryption is law, not luxury).
9
9
  *
10
10
  * Developer BYOK: GOOGLE_CLIENT_* / GITHUB_CLIENT_* when set for that provider
11
- * take the /api/oauth/{provider}/start button; otherwise the button uses IAM.
11
+ * take the /api/oauth/{provider}/start button; otherwise the button uses the
12
+ * Inner Animal Media platform lane (`inneranimalmedia`).
12
13
  */
13
14
 
14
15
  /** @deprecated Use DEFAULT_IAM_ORIGIN from the auth configuration contract. */
15
16
  export const DEFAULT_IAM_OAUTH_ISSUER = DEFAULT_IAM_ORIGIN;
16
17
  export const IAM_PLATFORM_STATE_PROVIDER = 'iam_platform';
17
- export const IAM_PLATFORM_CALLBACK_PATH = '/api/oauth/iam/callback';
18
+ /** Canonical platform OAuth callback (protocol id = inneranimalmedia). */
19
+ export const IAM_PLATFORM_CALLBACK_PATH = '/api/oauth/inneranimalmedia/callback';
20
+ /** @deprecated Legacy path — still accepted; prefer IAM_PLATFORM_CALLBACK_PATH. */
21
+ export const IAM_PLATFORM_CALLBACK_PATH_LEGACY = '/api/oauth/iam/callback';
18
22
 
19
23
  /**
20
24
  * @param {Record<string, unknown> | null | undefined} env
@@ -42,9 +46,9 @@ export function requireIamPlatformCredentials(env) {
42
46
 
43
47
  /**
44
48
  * @param {Record<string, unknown> | null | undefined} env
45
- * @param {'google' | 'github' | 'iam'} provider
49
+ * @param {'google' | 'github' | 'cloudflare' | 'inneranimalmedia' | 'iam'} provider
46
50
  * @returns {{
47
- * lane: 'iam_platform' | 'byok_google' | 'byok_github',
51
+ * lane: 'iam_platform' | 'byok_google' | 'byok_github' | 'byok_cloudflare',
48
52
  * clientId: string,
49
53
  * clientSecret: string,
50
54
  * origin?: string,
@@ -53,7 +57,10 @@ export function requireIamPlatformCredentials(env) {
53
57
  * } | null}
54
58
  */
55
59
  export function resolveOAuthCredentialLane(env, provider) {
56
- if (provider === 'iam') {
60
+ const key = String(provider || '').trim().toLowerCase();
61
+
62
+ // Protocol/selection id is inneranimalmedia; `iam` remains a legacy alias.
63
+ if (key === 'inneranimalmedia' || key === 'iam') {
57
64
  const iam = resolveIamPlatformCredentials(env);
58
65
  if (!iam) return null;
59
66
  return {
@@ -62,27 +69,27 @@ export function resolveOAuthCredentialLane(env, provider) {
62
69
  clientSecret: iam.clientSecret,
63
70
  origin: iam.origin,
64
71
  issuer: iam.issuer,
65
- provider: 'iam',
72
+ provider: 'inneranimalmedia',
66
73
  };
67
74
  }
68
75
 
69
- if (provider === 'google') {
76
+ if (key === 'google') {
70
77
  const clientId = String(env?.GOOGLE_CLIENT_ID || '').trim();
71
78
  const clientSecret = String(env?.GOOGLE_CLIENT_SECRET || '').trim();
72
79
  if (clientId && clientSecret) {
73
- return { lane: 'byok_google', clientId, clientSecret, provider };
80
+ return { lane: 'byok_google', clientId, clientSecret, provider: 'google' };
74
81
  }
75
82
  }
76
83
 
77
- if (provider === 'github') {
84
+ if (key === 'github') {
78
85
  const clientId = String(env?.GITHUB_CLIENT_ID || '').trim();
79
86
  const clientSecret = String(env?.GITHUB_CLIENT_SECRET || '').trim();
80
87
  if (clientId && clientSecret) {
81
- return { lane: 'byok_github', clientId, clientSecret, provider };
88
+ return { lane: 'byok_github', clientId, clientSecret, provider: 'github' };
82
89
  }
83
90
  }
84
91
 
85
- if (provider === 'cloudflare') {
92
+ if (key === 'cloudflare') {
86
93
  // Reuses the same Worker secrets as the Local Studio Cloudflare resource
87
94
  // connector (packages/connectors/cloudflare). clientSecret may be empty
88
95
  // if that client is configured as PKCE-only (Token Authentication
@@ -90,7 +97,7 @@ export function resolveOAuthCredentialLane(env, provider) {
90
97
  const clientId = String(env?.CLOUDFLARE_OAUTH_CLIENT_ID || '').trim();
91
98
  const clientSecret = String(env?.CLOUDFLARE_OAUTH_CLIENT_SECRET || '').trim();
92
99
  if (clientId) {
93
- return { lane: 'byok_cloudflare', clientId, clientSecret, provider };
100
+ return { lane: 'byok_cloudflare', clientId, clientSecret, provider: 'cloudflare' };
94
101
  }
95
102
  return null;
96
103
  }
@@ -104,7 +111,7 @@ export function resolveOAuthCredentialLane(env, provider) {
104
111
  clientSecret: iam.clientSecret,
105
112
  origin: iam.origin,
106
113
  issuer: iam.issuer,
107
- provider,
114
+ provider: 'inneranimalmedia',
108
115
  };
109
116
  }
110
117
 
@@ -51,7 +51,7 @@ export function createFinalizeInboundOAuth(ports) {
51
51
  ? String(input.supabaseUserId).trim()
52
52
  : null;
53
53
  const source = String(input?.source || `${provider}_oauth`).trim();
54
- const pageContext = String(input?.pageContext || '/dashboard/agent').trim();
54
+ const pageContext = String(input?.pageContext || '/').trim();
55
55
 
56
56
  if (!ports.hasDatabase(env) || !oauthEmail || !provider || !providerUid) {
57
57
  return /** @type {InboundOAuthFailure} */ ({ ok: false, error: 'provision_failed' });
@@ -1,4 +1,4 @@
1
- import { AUTH_LOGIN_PATH } from '../core/constants.js';
1
+ import { IDENTITY_ROUTE_IDS } from '../contracts/route-ids.js';
2
2
  import {
3
3
  IAM_PLATFORM_CALLBACK_PATH,
4
4
  IAM_PLATFORM_STATE_PROVIDER,
@@ -38,6 +38,7 @@ export async function iamPlatformOAuthStart(request, env, adapter, identity) {
38
38
  provider: IAM_PLATFORM_STATE_PROVIDER,
39
39
  codeVerifier,
40
40
  redirectTo,
41
+ appId: identity?.app?.id || null,
41
42
  });
42
43
 
43
44
  const redirectUri = `${url.origin}${IAM_PLATFORM_CALLBACK_PATH}`;
@@ -67,15 +68,15 @@ export async function iamPlatformOAuthCallback(request, env, adapter, identity)
67
68
  const err = url.searchParams.get('error');
68
69
 
69
70
  if (err || !code || !state) {
70
- return Response.redirect(`${url.origin}${AUTH_LOGIN_PATH}?error=oauth_failed`, 302);
71
+ return Response.redirect(`${url.origin}${identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN)}?error=oauth_failed`, 302);
71
72
  }
72
73
  if (!creds) {
73
- return Response.redirect(`${url.origin}${AUTH_LOGIN_PATH}?error=iam_oauth_not_configured`, 302);
74
+ return Response.redirect(`${url.origin}${identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN)}?error=iam_oauth_not_configured`, 302);
74
75
  }
75
76
 
76
77
  const saved = await adapter.consumeOAuthState(state);
77
78
  if (!saved || saved.provider !== IAM_PLATFORM_STATE_PROVIDER) {
78
- return Response.redirect(`${url.origin}${AUTH_LOGIN_PATH}?error=state_mismatch`, 302);
79
+ return Response.redirect(`${url.origin}${identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN)}?error=state_mismatch`, 302);
79
80
  }
80
81
 
81
82
  const redirectUri = `${url.origin}${IAM_PLATFORM_CALLBACK_PATH}`;
@@ -88,7 +89,7 @@ export async function iamPlatformOAuthCallback(request, env, adapter, identity)
88
89
  redirectUri,
89
90
  });
90
91
  if (!token?.access_token) {
91
- return Response.redirect(`${url.origin}${AUTH_LOGIN_PATH}?error=token_exchange_failed`, 302);
92
+ return Response.redirect(`${url.origin}${identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN)}?error=token_exchange_failed`, 302);
92
93
  }
93
94
 
94
95
  const profile = await fetchIamProfile({
@@ -97,7 +98,7 @@ export async function iamPlatformOAuthCallback(request, env, adapter, identity)
97
98
  });
98
99
  const normalized = profile ? normalizeIamIdentity(profile) : null;
99
100
  if (!normalized?.subject || !normalized?.email) {
100
- return Response.redirect(`${url.origin}${AUTH_LOGIN_PATH}?error=userinfo_failed`, 302);
101
+ return Response.redirect(`${url.origin}${identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN)}?error=userinfo_failed`, 302);
101
102
  }
102
103
 
103
104
  const result = await identity.provisionOAuthUser({
@@ -109,7 +110,7 @@ export async function iamPlatformOAuthCallback(request, env, adapter, identity)
109
110
 
110
111
  const redirectTo = identity.resolvePostLoginPath(saved.redirect_to);
111
112
  const res = identity.buildLoginSuccessResponse(request, result.sessionId, redirectTo);
112
- const globeUrl = `${url.origin}${AUTH_LOGIN_PATH}?globe_exit=1&next=${encodeURIComponent(redirectTo)}`;
113
+ const globeUrl = `${url.origin}${identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN)}?globe_exit=1&next=${encodeURIComponent(redirectTo)}`;
113
114
  return new Response(null, {
114
115
  status: 302,
115
116
  headers: {