@spacefast/common 0.0.24 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/brand-assets/LICENSE-Haskoy.txt +93 -0
  2. package/brand-assets/haskoy-latin-variable.woff2 +0 -0
  3. package/brand-assets/spacefast-favicon.svg +4 -4
  4. package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
  5. package/brand-assets/spacefast-wordmark.svg +2 -4
  6. package/dist/agents/connect-targets.d.ts +6 -146
  7. package/dist/agents/connect-targets.js +63 -295
  8. package/dist/agents/private-key-oauth.d.ts +31 -2
  9. package/dist/agents/private-key-oauth.js +166 -92
  10. package/dist/agents/registry/core.d.ts +292 -0
  11. package/dist/agents/registry/core.js +1388 -0
  12. package/dist/agents/registry/deeplink-probes.d.ts +23 -0
  13. package/dist/agents/registry/deeplink-probes.js +18 -0
  14. package/dist/agents/registry/node.d.ts +56 -0
  15. package/dist/agents/registry/node.js +339 -0
  16. package/dist/brand-assets-build.js +3 -6
  17. package/dist/brand-assets.d.ts +5 -15
  18. package/dist/brand-assets.js +12 -14
  19. package/dist/brand.d.ts +17 -0
  20. package/dist/brand.js +24 -4
  21. package/dist/config/domains.d.ts +79 -12
  22. package/dist/config/domains.js +128 -29
  23. package/dist/config/index.js +1 -2
  24. package/dist/contracts/abuse.js +9 -24
  25. package/dist/contracts/access-profiles.d.ts +86 -0
  26. package/dist/contracts/access-profiles.js +89 -0
  27. package/dist/contracts/access.d.ts +24 -30
  28. package/dist/contracts/access.js +33 -64
  29. package/dist/contracts/activity.d.ts +18 -1
  30. package/dist/contracts/activity.js +72 -26
  31. package/dist/contracts/analytics.js +8 -10
  32. package/dist/contracts/api-keys.d.ts +77 -52
  33. package/dist/contracts/api-keys.js +44 -24
  34. package/dist/contracts/application-journal.d.ts +216 -0
  35. package/dist/contracts/application-journal.js +167 -0
  36. package/dist/contracts/auth.d.ts +90 -17
  37. package/dist/contracts/auth.js +86 -17
  38. package/dist/contracts/beta.d.ts +9 -4
  39. package/dist/contracts/beta.js +20 -16
  40. package/dist/contracts/billing.d.ts +2 -1
  41. package/dist/contracts/billing.js +37 -11
  42. package/dist/contracts/bootstrap.d.ts +791 -0
  43. package/dist/contracts/bootstrap.js +51 -0
  44. package/dist/contracts/builds.d.ts +209 -18
  45. package/dist/contracts/builds.js +93 -27
  46. package/dist/contracts/channels.d.ts +43 -0
  47. package/dist/contracts/channels.js +25 -9
  48. package/dist/contracts/cli.d.ts +7 -0
  49. package/dist/contracts/cli.js +6 -0
  50. package/dist/contracts/collab-public.d.ts +1 -1
  51. package/dist/contracts/collab-public.js +5 -8
  52. package/dist/contracts/comments.d.ts +44 -34
  53. package/dist/contracts/comments.js +107 -77
  54. package/dist/contracts/commerce.d.ts +165 -0
  55. package/dist/contracts/commerce.js +146 -0
  56. package/dist/contracts/common.d.ts +3 -3
  57. package/dist/contracts/common.js +20 -31
  58. package/dist/contracts/content-contract-verification.d.ts +521 -0
  59. package/dist/contracts/content-contract-verification.js +320 -0
  60. package/dist/contracts/content-platform-fixture.d.ts +1278 -0
  61. package/dist/contracts/content-platform-fixture.js +44 -0
  62. package/dist/contracts/content-program.d.ts +994 -0
  63. package/dist/contracts/content-program.js +658 -0
  64. package/dist/contracts/content-sync.d.ts +290 -0
  65. package/dist/contracts/content-sync.js +167 -0
  66. package/dist/contracts/content.d.ts +432 -0
  67. package/dist/contracts/content.js +303 -0
  68. package/dist/contracts/continuation.d.ts +17 -1
  69. package/dist/contracts/continuation.js +23 -23
  70. package/dist/contracts/countries.js +3 -4
  71. package/dist/contracts/crons.d.ts +52 -0
  72. package/dist/contracts/crons.js +60 -0
  73. package/dist/contracts/dashboard-prefs.d.ts +56 -0
  74. package/dist/contracts/dashboard-prefs.js +51 -0
  75. package/dist/contracts/device-auth.d.ts +234 -37
  76. package/dist/contracts/device-auth.js +267 -53
  77. package/dist/contracts/docs.d.ts +1 -5
  78. package/dist/contracts/docs.js +7 -15
  79. package/dist/contracts/domains.d.ts +501 -93
  80. package/dist/contracts/domains.js +193 -55
  81. package/dist/contracts/email-preferences.d.ts +3 -3
  82. package/dist/contracts/email-preferences.js +6 -7
  83. package/dist/contracts/enums.d.ts +27 -26
  84. package/dist/contracts/enums.js +68 -61
  85. package/dist/contracts/error-code-meta.d.ts +283 -7
  86. package/dist/contracts/error-code-meta.js +97 -5
  87. package/dist/contracts/error-codes.d.ts +5 -5
  88. package/dist/contracts/error-codes.js +82 -17
  89. package/dist/contracts/events.d.ts +100 -20
  90. package/dist/contracts/events.js +107 -20
  91. package/dist/contracts/execution.d.ts +48 -64
  92. package/dist/contracts/execution.js +52 -62
  93. package/dist/contracts/feature-lifecycle.d.ts +73 -27
  94. package/dist/contracts/feature-lifecycle.js +86 -26
  95. package/dist/contracts/features.d.ts +68 -19
  96. package/dist/contracts/features.js +42 -41
  97. package/dist/contracts/fixtures/content-platform-v1.json +1108 -0
  98. package/dist/contracts/frame-session.d.ts +192 -0
  99. package/dist/contracts/frame-session.js +164 -0
  100. package/dist/contracts/functions.d.ts +338 -164
  101. package/dist/contracts/functions.js +367 -198
  102. package/dist/contracts/git.d.ts +27 -33
  103. package/dist/contracts/git.js +10 -11
  104. package/dist/contracts/grant-copy.d.ts +43 -0
  105. package/dist/contracts/grant-copy.js +44 -0
  106. package/dist/contracts/grants.d.ts +62 -54
  107. package/dist/contracts/grants.js +124 -23
  108. package/dist/contracts/ids.d.ts +4 -1
  109. package/dist/contracts/ids.js +6 -3
  110. package/dist/contracts/internal.d.ts +8 -18
  111. package/dist/contracts/internal.js +13 -14
  112. package/dist/contracts/mcp.d.ts +28 -0
  113. package/dist/contracts/mcp.js +28 -0
  114. package/dist/contracts/me.d.ts +6 -1
  115. package/dist/contracts/me.js +12 -6
  116. package/dist/contracts/notifications.d.ts +27 -29
  117. package/dist/contracts/notifications.js +9 -4
  118. package/dist/contracts/oauth-resources.d.ts +112 -10
  119. package/dist/contracts/oauth-resources.js +116 -11
  120. package/dist/contracts/oauth-scope-actions.d.ts +90 -0
  121. package/dist/contracts/oauth-scope-actions.js +126 -0
  122. package/dist/contracts/operations.d.ts +27 -4
  123. package/dist/contracts/operations.js +12 -6
  124. package/dist/contracts/pages.d.ts +31 -7
  125. package/dist/contracts/pages.js +22 -5
  126. package/dist/contracts/partner-notifications.d.ts +3 -0
  127. package/dist/contracts/partner-notifications.js +65 -0
  128. package/dist/contracts/{platform.d.ts → partner.d.ts} +29 -42
  129. package/dist/contracts/{platform.js → partner.js} +27 -40
  130. package/dist/contracts/plan-policy.js +1 -1
  131. package/dist/contracts/principal-assertion.d.ts +43 -0
  132. package/dist/contracts/principal-assertion.js +67 -0
  133. package/dist/contracts/principals.d.ts +73 -0
  134. package/dist/contracts/principals.js +59 -0
  135. package/dist/contracts/privacy.d.ts +11 -0
  136. package/dist/contracts/privacy.js +17 -0
  137. package/dist/contracts/problem-document.js +3 -4
  138. package/dist/contracts/publish-archive.d.ts +269 -161
  139. package/dist/contracts/publish-archive.js +11 -1
  140. package/dist/contracts/push-new.d.ts +8 -8
  141. package/dist/contracts/quotas.js +5 -5
  142. package/dist/contracts/realtime.d.ts +52 -0
  143. package/dist/contracts/realtime.js +54 -0
  144. package/dist/contracts/repository-connections.d.ts +44 -31
  145. package/dist/contracts/repository-connections.js +17 -5
  146. package/dist/contracts/resources.d.ts +105 -14
  147. package/dist/contracts/resources.js +150 -26
  148. package/dist/contracts/route-inventory.d.ts +358 -0
  149. package/dist/contracts/route-inventory.js +141 -0
  150. package/dist/contracts/runtime-api.d.ts +206 -82
  151. package/dist/contracts/runtime-api.js +131 -70
  152. package/dist/contracts/runtime-app.d.ts +13 -23
  153. package/dist/contracts/runtime-app.js +39 -24
  154. package/dist/contracts/runtime-components.d.ts +143 -0
  155. package/dist/contracts/runtime-components.js +199 -0
  156. package/dist/contracts/runtime-db.d.ts +49 -23
  157. package/dist/contracts/runtime-db.js +87 -22
  158. package/dist/contracts/runtime-purge.d.ts +8 -0
  159. package/dist/contracts/runtime-purge.js +5 -0
  160. package/dist/contracts/runtime-services.d.ts +43 -75
  161. package/dist/contracts/runtime-services.js +29 -49
  162. package/dist/contracts/runtime-storage.d.ts +15 -14
  163. package/dist/contracts/runtime-storage.js +30 -14
  164. package/dist/contracts/sf-config-v1.d.ts +48 -14
  165. package/dist/contracts/slugs.js +13 -4
  166. package/dist/contracts/space-config.d.ts +283 -19
  167. package/dist/contracts/space-config.js +430 -80
  168. package/dist/contracts/spaces.d.ts +376 -290
  169. package/dist/contracts/spaces.js +200 -135
  170. package/dist/contracts/superadmin-activity.d.ts +3 -43
  171. package/dist/contracts/superadmin-activity.js +6 -30
  172. package/dist/contracts/superadmin-emails.d.ts +1 -0
  173. package/dist/contracts/superadmin-emails.js +17 -1
  174. package/dist/contracts/superadmin-runtime.d.ts +199 -0
  175. package/dist/contracts/superadmin-runtime.js +141 -0
  176. package/dist/contracts/superadmin-search.d.ts +22 -2
  177. package/dist/contracts/superadmin-search.js +14 -6
  178. package/dist/contracts/superadmin-spaces.d.ts +438 -189
  179. package/dist/contracts/superadmin-spaces.js +131 -25
  180. package/dist/contracts/superadmin-teams.d.ts +5 -2
  181. package/dist/contracts/superadmin-teams.js +1 -2
  182. package/dist/contracts/superadmin-tenants.d.ts +627 -12
  183. package/dist/contracts/superadmin-tenants.js +32 -13
  184. package/dist/contracts/superadmin.d.ts +232 -37
  185. package/dist/contracts/superadmin.js +106 -22
  186. package/dist/contracts/tags.d.ts +85 -58
  187. package/dist/contracts/tags.js +47 -37
  188. package/dist/contracts/teams.d.ts +86 -26
  189. package/dist/contracts/teams.js +90 -31
  190. package/dist/contracts/tenants.d.ts +135 -0
  191. package/dist/contracts/tenants.js +120 -0
  192. package/dist/contracts/test-triggers.d.ts +70 -0
  193. package/dist/contracts/test-triggers.js +116 -0
  194. package/dist/contracts/theme-json.js +3 -8
  195. package/dist/contracts/transfers.d.ts +1 -0
  196. package/dist/contracts/transfers.js +3 -2
  197. package/dist/contracts/usage.d.ts +209 -0
  198. package/dist/contracts/usage.js +124 -12
  199. package/dist/contracts/variables.d.ts +2 -2
  200. package/dist/contracts/variables.js +9 -13
  201. package/dist/contracts/webhooks.d.ts +13 -5
  202. package/dist/contracts/webhooks.js +3 -1
  203. package/dist/contracts/zero.d.ts +72 -81
  204. package/dist/contracts/zero.js +80 -78
  205. package/dist/dashboard-paths/index.d.ts +24 -21
  206. package/dist/dashboard-paths/index.js +35 -47
  207. package/dist/dashboard-paths/route-reservations.generated.d.ts +2 -0
  208. package/dist/dashboard-paths/route-reservations.generated.js +36 -0
  209. package/dist/docs/agent-arrival.d.ts +92 -0
  210. package/dist/docs/agent-arrival.js +227 -0
  211. package/dist/docs/agent-output-policy.d.ts +6 -0
  212. package/dist/docs/agent-output-policy.js +22 -0
  213. package/dist/docs/agent-prose.d.ts +12 -18
  214. package/dist/docs/agent-prose.js +73 -51
  215. package/dist/docs/agent-setup.d.ts +72 -41
  216. package/dist/docs/agent-setup.js +339 -436
  217. package/dist/docs/catalog.d.ts +153 -95
  218. package/dist/docs/catalog.js +163 -48
  219. package/dist/docs/error-docs.d.ts +2098 -1
  220. package/dist/docs/error-docs.js +421 -144
  221. package/dist/docs/index-build.d.ts +0 -1
  222. package/dist/docs/index-build.js +2 -3
  223. package/dist/docs/page-context-prompt.d.ts +77 -0
  224. package/dist/docs/page-context-prompt.js +131 -0
  225. package/dist/docs/reference-urls.d.ts +46 -0
  226. package/dist/docs/reference-urls.js +79 -0
  227. package/dist/docs/search.d.ts +6 -8
  228. package/dist/docs/search.js +10 -13
  229. package/dist/docs/skill-distribution.d.ts +4 -1
  230. package/dist/docs/skill-distribution.js +10 -5
  231. package/dist/docs/skills.d.ts +120 -120
  232. package/dist/docs/skills.js +121 -73
  233. package/dist/docs/start-prompts.d.ts +45 -0
  234. package/dist/docs/start-prompts.js +62 -0
  235. package/dist/domain-exploration.js +9 -13
  236. package/dist/og-template.d.ts +56 -0
  237. package/dist/og-template.js +56 -0
  238. package/dist/slug-policy/index.js +5 -11
  239. package/dist/test-helpers/fetch-stub.js +1 -0
  240. package/dist/utils/browser-credential.d.ts +13 -6
  241. package/dist/utils/browser-credential.js +14 -7
  242. package/dist/utils/build-settings.d.ts +21 -1
  243. package/dist/utils/build-settings.js +133 -15
  244. package/dist/utils/canonical-json.d.ts +3 -5
  245. package/dist/utils/canonical-json.js +4 -5
  246. package/dist/utils/cast-transport.d.ts +5 -17
  247. package/dist/utils/cast-transport.js +6 -19
  248. package/dist/utils/claim-token.js +1 -2
  249. package/dist/utils/comment-avatars.js +4 -6
  250. package/dist/utils/concurrency.js +3 -3
  251. package/dist/utils/content-type.d.ts +7 -6
  252. package/dist/utils/content-type.js +73 -51
  253. package/dist/utils/credential-policy.d.ts +50 -6
  254. package/dist/utils/credential-policy.js +126 -59
  255. package/dist/utils/dns-instructions.js +9 -9
  256. package/dist/utils/egress-policy.fixtures.json +1 -1
  257. package/dist/utils/error-code.js +1 -0
  258. package/dist/utils/generate-space-name.d.ts +3 -8
  259. package/dist/utils/generate-space-name.js +3 -8
  260. package/dist/utils/grant-decision.fixtures.json +2 -2
  261. package/dist/utils/grants.d.ts +0 -1
  262. package/dist/utils/grants.js +7 -8
  263. package/dist/utils/gravatar.d.ts +2 -4
  264. package/dist/utils/gravatar.js +6 -12
  265. package/dist/utils/id-hints.js +14 -23
  266. package/dist/utils/idempotency.d.ts +3 -3
  267. package/dist/utils/idempotency.js +6 -7
  268. package/dist/utils/local-space-state.d.ts +23 -26
  269. package/dist/utils/local-space-state.js +46 -46
  270. package/dist/utils/oauth-signed-query.d.ts +3 -5
  271. package/dist/utils/oauth-signed-query.js +6 -9
  272. package/dist/utils/one-shot-replay.d.ts +7 -6
  273. package/dist/utils/one-shot-replay.js +3 -5
  274. package/dist/utils/page-colors.d.ts +15 -19
  275. package/dist/utils/page-colors.js +23 -45
  276. package/dist/utils/page-fonts.d.ts +1 -1
  277. package/dist/utils/page-fonts.js +8 -20
  278. package/dist/utils/page-preview.d.ts +0 -1
  279. package/dist/utils/page-preview.js +0 -1
  280. package/dist/utils/pages.d.ts +33 -3
  281. package/dist/utils/pages.js +83 -30
  282. package/dist/utils/privacy-regions.d.ts +19 -0
  283. package/dist/utils/privacy-regions.js +91 -0
  284. package/dist/utils/publish-form-data.d.ts +4 -5
  285. package/dist/utils/publish-form-data.js +2 -3
  286. package/dist/utils/publish-policy.d.ts +11 -17
  287. package/dist/utils/publish-policy.fixtures.json +11 -5
  288. package/dist/utils/publish-policy.js +65 -110
  289. package/dist/utils/query-client.d.ts +2 -0
  290. package/dist/utils/query-client.js +10 -1
  291. package/dist/utils/runtime-paths.d.ts +4 -4
  292. package/dist/utils/runtime-paths.js +6 -8
  293. package/dist/utils/runtime-upload.d.ts +9 -0
  294. package/dist/utils/runtime-upload.js +12 -0
  295. package/dist/utils/secure-local-file.d.ts +3 -3
  296. package/dist/utils/secure-local-file.js +23 -40
  297. package/dist/utils/space-config.d.ts +12 -15
  298. package/dist/utils/space-config.js +13 -16
  299. package/dist/utils/static-runtime-policy.d.ts +10 -7
  300. package/dist/utils/static-runtime-policy.fixtures.json +38 -41
  301. package/dist/utils/static-runtime-policy.generated.d.ts +16 -0
  302. package/dist/utils/static-runtime-policy.generated.js +70 -0
  303. package/dist/utils/static-runtime-policy.js +31 -132
  304. package/dist/utils/storage-policy.js +1 -2
  305. package/dist/utils/upload-session.d.ts +4 -1
  306. package/dist/utils/upload-session.js +41 -19
  307. package/dist/utils/version-path.js +11 -19
  308. package/dist/utils/wpcom-auth-redirects.d.ts +5 -14
  309. package/dist/utils/wpcom-auth-redirects.js +1 -4
  310. package/dist/vocabulary.d.ts +13 -13
  311. package/dist/vocabulary.js +52 -27
  312. package/package.json +15 -18
  313. package/dist/agents/client-registry.d.ts +0 -22
  314. package/dist/agents/client-registry.js +0 -41
  315. package/dist/docs/agent-handoff-document.d.ts +0 -15
  316. package/dist/docs/agent-handoff-document.js +0 -180
  317. package/dist/docs/agent-solutions.d.ts +0 -110
  318. package/dist/docs/agent-solutions.js +0 -183
  319. package/dist/utils/query-keys.d.ts +0 -84
  320. package/dist/utils/query-keys.js +0 -108
@@ -0,0 +1,227 @@
1
+ import { z } from "zod";
2
+ import { ANONYMOUS_AGENT_HANDOFF_TTL_MS, AnonymousAgentHandoffSchema, AnonymousAgentHandoffStatusSchema, } from "../contracts/device-auth.js";
3
+ import { buildAnonymousAgentHandoff, parseAgentHandoffUrl } from "./agent-setup.js";
4
+ /*
5
+ * Agent arrival, signed out: the whole flow behind "your agent picked the
6
+ * prompt up", framework-free so the dashboard and the marketing site run one
7
+ * implementation instead of two that drift.
8
+ *
9
+ * The shape of the flow: mint an anonymous handoff, compose the copied prompt
10
+ * around its one-use URL, then poll that handoff's own fetch state until the
11
+ * counter leaves zero. The handoff carries no team, no Space, and nothing to
12
+ * redeem — its only job is to make the fetch observable — so the visitor learns
13
+ * their agent arrived without signing up first.
14
+ *
15
+ * Everything here is a plain function: no React, no query client, no clock but
16
+ * the one the caller passes. Cadence and reuse are policy, so they live here
17
+ * with the numbers rather than in whichever UI happens to render the wait.
18
+ */
19
+ const ANONYMOUS_ARRIVAL_PATH = "/v1/auth/agent-handoffs/anonymous";
20
+ export const AGENT_HANDOFF_REQUEST_FAILED_MESSAGE = "Request failed.";
21
+ export const AGENT_HANDOFF_INVALID_RESPONSE_MESSAGE = "Invalid agent handoff response.";
22
+ /**
23
+ * The messages that say nothing a person can act on. A UI showing an error
24
+ * detail line suppresses these, so it never renders "failed: Request failed."
25
+ */
26
+ export const AGENT_HANDOFF_GENERIC_MESSAGES = new Set([
27
+ AGENT_HANDOFF_REQUEST_FAILED_MESSAGE,
28
+ AGENT_HANDOFF_INVALID_RESPONSE_MESSAGE,
29
+ ]);
30
+ export class AgentHandoffRequestError extends Error {
31
+ code;
32
+ constructor(message, code = null) {
33
+ super(message);
34
+ this.name = "AgentHandoffRequestError";
35
+ this.code = code;
36
+ }
37
+ }
38
+ /**
39
+ * The parts of an RFC 9457 problem document that may reach a person. Anything
40
+ * else the server sent is dropped here rather than carried on an error: a
41
+ * malformed field costs the reader a detail line, never a leaked credential.
42
+ */
43
+ const AgentHandoffProblemSchema = z.object({
44
+ code: z.string().optional().catch(undefined),
45
+ detail: z.string().optional().catch(undefined),
46
+ });
47
+ // Codes are lowercase snake tokens; anything else (including a secret-shaped
48
+ // blob smuggled into the field) is dropped rather than carried on the error.
49
+ const SAFE_ERROR_CODE = /^[a-z][a-z0-9_.-]{0,63}$/;
50
+ function safeProblemDetail(detail) {
51
+ if (detail === undefined || !detail.trim())
52
+ return AGENT_HANDOFF_REQUEST_FAILED_MESSAGE;
53
+ // Error copy must never echo a handoff URL or a secret-shaped opaque credential.
54
+ if (/https?:\/\/|[A-Za-z0-9_-]{16,}/i.test(detail))
55
+ return AGENT_HANDOFF_REQUEST_FAILED_MESSAGE;
56
+ return detail;
57
+ }
58
+ /** The failure a non-2xx answer becomes, sanitized at the boundary that read it. */
59
+ async function agentHandoffErrorFromResponse(response) {
60
+ const parsed = AgentHandoffProblemSchema.safeParse(await response.json().catch(() => null));
61
+ const problem = parsed.success ? parsed.data : {};
62
+ const code = problem.code !== undefined && SAFE_ERROR_CODE.test(problem.code) ? problem.code : null;
63
+ return new AgentHandoffRequestError(safeProblemDetail(problem.detail), code);
64
+ }
65
+ function invalidResponse() {
66
+ return new AgentHandoffRequestError(AGENT_HANDOFF_INVALID_RESPONSE_MESSAGE);
67
+ }
68
+ /*
69
+ * The success envelope, parsed as one shape. `{ data }` is the API's contract
70
+ * and the payload schema is the handoff's, so a response that is neither never
71
+ * reaches a caller as a half-parsed object.
72
+ */
73
+ const MintEnvelopeSchema = z.object({ data: AnonymousAgentHandoffSchema });
74
+ const StatusEnvelopeSchema = z.object({ data: AnonymousAgentHandoffStatusSchema });
75
+ function endpoint(config, path) {
76
+ return `${config.apiOrigin.replace(/\/+$/, "")}${path}`;
77
+ }
78
+ function transport(config) {
79
+ return config.fetch ?? globalThis.fetch;
80
+ }
81
+ function clock(config) {
82
+ return config.now ? config.now() : Date.now();
83
+ }
84
+ /**
85
+ * Mints the signed-out handoff a copied prompt carries.
86
+ *
87
+ * The anonymous surface is anonymous by contract, so this deliberately sends no
88
+ * credentials: it works signed out, and a mint that carried a session would
89
+ * answer differently for gated accounts.
90
+ */
91
+ export async function mintAnonymousArrival(config) {
92
+ const response = await transport(config)(endpoint(config, ANONYMOUS_ARRIVAL_PATH), {
93
+ method: "POST",
94
+ credentials: "omit",
95
+ headers: { accept: "application/json" },
96
+ });
97
+ if (!response.ok)
98
+ throw await agentHandoffErrorFromResponse(response);
99
+ const parsed = MintEnvelopeSchema.safeParse(await response.json().catch(() => null));
100
+ if (!parsed.success)
101
+ throw invalidResponse();
102
+ const data = parsed.data.data;
103
+ let handoffUrl = null;
104
+ let secret = null;
105
+ for (const origin of config.allowedOrigins ?? [config.apiOrigin]) {
106
+ try {
107
+ ({ handoffUrl, secret } = parseAgentHandoffUrl(data.url, origin));
108
+ break;
109
+ }
110
+ catch {
111
+ // Try the next explicitly allowed origin.
112
+ }
113
+ }
114
+ // Sanitization boundary: the ledger id is rendered and polled on, so it must
115
+ // be provably free of the bearer whatever the server fed us.
116
+ if (handoffUrl === null || secret === null || data.id.includes(secret)) {
117
+ throw invalidResponse();
118
+ }
119
+ const url = handoffUrl;
120
+ const bearer = secret;
121
+ return {
122
+ id: data.id,
123
+ expiresAt: Date.parse(data.expiresAt),
124
+ promptFor: (context) => {
125
+ // The caller's text is authored before the mint, so it can only contain
126
+ // the bearer if something upstream is already broken.
127
+ if (context.includes(bearer))
128
+ throw invalidResponse();
129
+ return buildAnonymousAgentHandoff({
130
+ expectedOrigin: new URL(url).origin,
131
+ url,
132
+ context,
133
+ }).prompt;
134
+ },
135
+ };
136
+ }
137
+ /** Reads one anonymous handoff's fetch state by its non-secret ledger id. */
138
+ export async function readAnonymousArrivalStatus(id, config) {
139
+ const path = `${ANONYMOUS_ARRIVAL_PATH}/${encodeURIComponent(id)}`;
140
+ const response = await transport(config)(endpoint(config, path), {
141
+ method: "GET",
142
+ credentials: "omit",
143
+ headers: { accept: "application/json" },
144
+ });
145
+ if (!response.ok)
146
+ throw await agentHandoffErrorFromResponse(response);
147
+ const parsed = StatusEnvelopeSchema.safeParse(await response.json().catch(() => null));
148
+ if (!parsed.success)
149
+ throw invalidResponse();
150
+ return parsed.data.data;
151
+ }
152
+ /*
153
+ * Cadence, owned in one place with its numbers.
154
+ *
155
+ * The answer being waited on is one counter that leaves zero exactly once, and
156
+ * it changes from outside this browser, so polling is the whole mechanism.
157
+ * Eager while an agent is plausibly still landing, relaxed for the rest of the
158
+ * handoff's life, stopped once nothing can change.
159
+ */
160
+ export const AGENT_ARRIVAL_EAGER_POLL_MS = 2_000;
161
+ export const AGENT_ARRIVAL_EAGER_WINDOW_MS = 2 * 60_000;
162
+ export const AGENT_ARRIVAL_RELAXED_POLL_MS = 10_000;
163
+ /**
164
+ * The handoff's own TTL is the clock: with no `since` to carry, "how much
165
+ * runway is left" says how long ago it was minted.
166
+ */
167
+ export const AGENT_ARRIVAL_RELAXED_AFTER_MS = ANONYMOUS_AGENT_HANDOFF_TTL_MS - AGENT_ARRIVAL_EAGER_WINDOW_MS;
168
+ /**
169
+ * One live handoff per visit is reused while it still has this much runway.
170
+ * Every copy gesture minting its own would spend rows against the caller's
171
+ * budget for a signal that only has to flip once.
172
+ */
173
+ export const AGENT_ARRIVAL_REFRESH_RUNWAY_MS = 2 * 60_000;
174
+ /** How long to wait before the next status read, or `false` to stop polling. */
175
+ export function anonymousArrivalPollDelayMs(status, now = Date.now()) {
176
+ if (status === undefined)
177
+ return AGENT_ARRIVAL_EAGER_POLL_MS;
178
+ if (hasAgentArrived(status))
179
+ return false;
180
+ const remaining = Date.parse(status.expiresAt) - now;
181
+ if (remaining <= 0)
182
+ return false;
183
+ return remaining > AGENT_ARRIVAL_RELAXED_AFTER_MS
184
+ ? AGENT_ARRIVAL_EAGER_POLL_MS
185
+ : AGENT_ARRIVAL_RELAXED_POLL_MS;
186
+ }
187
+ /** The arrival itself: one fetch of the private URL is the whole signal. */
188
+ export function hasAgentArrived(status) {
189
+ return (status?.fetchCount ?? 0) > 0;
190
+ }
191
+ /**
192
+ * One visit's arrival flow. Hold one of these for as long as the visitor is on
193
+ * the surface: the reuse discipline lives in the instance, so re-copies keep
194
+ * the same handoff instead of piling fresh rows behind one signal.
195
+ */
196
+ export function createAnonymousArrival(config) {
197
+ let inflight = null;
198
+ const fresh = () => {
199
+ const pending = mintAnonymousArrival(config);
200
+ inflight = pending;
201
+ // A mint that failed is not the visit's handoff; the next copy mints anew.
202
+ void pending.catch(() => {
203
+ if (inflight === pending)
204
+ inflight = null;
205
+ });
206
+ return pending;
207
+ };
208
+ const ensureMinted = () => {
209
+ const cached = inflight;
210
+ if (!cached)
211
+ return fresh();
212
+ return cached.then((minted) => minted.expiresAt - clock(config) > AGENT_ARRIVAL_REFRESH_RUNWAY_MS ? minted : fresh());
213
+ };
214
+ return {
215
+ ensureMinted,
216
+ promptFor: (context) => ensureMinted().then((minted) => ({
217
+ id: minted.id,
218
+ // Composed from THIS copy's context, never the one the handoff was
219
+ // first minted for: one handoff spans the visit, the task does not.
220
+ prompt: minted.promptFor(context),
221
+ expiresAt: minted.expiresAt,
222
+ })),
223
+ readStatus: (id) => readAnonymousArrivalStatus(id, config),
224
+ nextPollDelayMs: (status) => anonymousArrivalPollDelayMs(status, clock(config)),
225
+ hasArrived: hasAgentArrived,
226
+ };
227
+ }
@@ -0,0 +1,6 @@
1
+ export type AgentOutput = {
2
+ readonly source: string;
3
+ readonly text: string;
4
+ };
5
+ /** Reject commercial plan promotion in text that an agent can receive. */
6
+ export declare function assertNoCommercialUpsell(outputs: readonly AgentOutput[]): void;
@@ -0,0 +1,22 @@
1
+ const COMMERCIAL_UPSELL_PATTERNS = [
2
+ /\bpricing\b/i,
3
+ /\bpaid plans?\b/i,
4
+ /\bupgrade (?:your|the|this) (?:account|plan|subscription)\b/i,
5
+ /\bupgrade to (?:a )?(?:paid|premium|pro)\b/i,
6
+ /\bsubscribe now\b/i,
7
+ /\bstart (?:a )?(?:free )?trial\b/i,
8
+ /\bbuy now\b/i,
9
+ /\bcontact sales\b/i,
10
+ /\bcheckout\b/i,
11
+ ];
12
+ /** Reject commercial plan promotion in text that an agent can receive. */
13
+ export function assertNoCommercialUpsell(outputs) {
14
+ for (const output of outputs) {
15
+ for (const pattern of COMMERCIAL_UPSELL_PATTERNS) {
16
+ const match = pattern.exec(output.text);
17
+ if (match) {
18
+ throw new Error(`${output.source} contains commercial upsell text: ${match[0]}`);
19
+ }
20
+ }
21
+ }
22
+ }
@@ -1,24 +1,21 @@
1
1
  export declare const ARCHIVE_SECRET_GLOBS: readonly [".env*", ".npmrc", ".netrc", "*/.git/*", "*/.spacefast/*", "*/.ssh/*", "*/.aws/*", "*/.kube/*", "*/.docker/*", "credentials.json", "*.pem", "*.key", "*.p12", "*.pfx", "*.crt", "*id_rsa*"];
2
- /**
3
- * The credential rules every surface must repeat. Treat tokens as secrets, keep
4
- * them out of logs and transcripts, and never echo them back.
5
- */
2
+ /** The credential rules every surface must repeat. */
6
3
  export declare const SECRET_RULES_PROSE: string;
7
4
  export declare const POSITIONING_PROSE: string;
8
5
  export declare const EXPLAIN_TO_USER_PROSE: string;
6
+ export declare const FEEDBACK_PROSE = "With user approval, send feedback with `sf feedback`, MCP `execute` (search `send feedback`), or `POST /v1/feedback`. Never include credentials or private links.";
9
7
  export declare const FAILURE_CONDUCT_PROSE: string;
10
8
  export declare const LANE_PICKER_PROSE: string;
11
9
  export declare const HOW_TO_RESEARCH_PROSE: string;
12
- /** Curl/API skill lane: docs URLs only no MCP or CLI tools required. */
10
+ /** Curl/API skill lane: docs URLs only, no MCP or CLI tools required. */
13
11
  export declare const HOW_TO_RESEARCH_API_PROSE: string;
14
12
  /** MCP plugin skill lane: prefer skills for workflows and execute for the OpenAPI catalog. */
15
13
  export declare const HOW_TO_RESEARCH_MCP_PROSE: string;
16
14
  export declare const ENV_NOTES_PROSE: string;
17
15
  export declare const ENV_NOTES_API_PROSE: string;
18
16
  /**
19
- * The CHECK verb: before ever creating a space, look for the one the project
20
- * already has. This is what keeps a returning agent from pushing a duplicate
21
- * anonymous space instead of a new version.
17
+ * The CHECK verb: look for an existing space before creating one, so a returning
18
+ * agent publishes a new version instead of a duplicate anonymous space.
22
19
  */
23
20
  export declare const CHECK_BEFORE_PUBLISH_PROSE: string;
24
21
  export declare const CHECK_BEFORE_PUBLISH_API_PROSE: string;
@@ -37,6 +34,7 @@ export declare const SAVE_STATE_MCP_PROSE: string;
37
34
  * The UPDATE verb: a new version to the SAME space. Never re-run the
38
35
  * first-publish recipe when saved state exists.
39
36
  */
37
+ export declare const PUSH_TO_DEPLOY_PROSE: string;
40
38
  export declare const UPDATE_SPACE_PROSE: string;
41
39
  /**
42
40
  * The CLAIM / CONTINUE verb: show the claim link, and after the user claims,
@@ -45,7 +43,7 @@ export declare const UPDATE_SPACE_PROSE: string;
45
43
  export declare const CONTINUE_AFTER_CLAIM_PROSE: string;
46
44
  /**
47
45
  * The zero-install publish sequence: a replay-safe curl loop for a file, a
48
- * secret-preflighted zip for a folder. This is the load-bearing rung 0 — an
46
+ * secret-preflighted zip for a folder. This is the load-bearing rung 0. An
49
47
  * agent that stops here must still succeed.
50
48
  */
51
49
  export declare const PUBLISH_SEQUENCE_PROSE: string;
@@ -53,19 +51,15 @@ export declare const PUBLISH_SEQUENCE_PROSE: string;
53
51
  * What the JSON publish receipt contains and how to read it. The receipt is the
54
52
  * source of truth; never invent a separate success signal.
55
53
  */
56
- export declare const RECEIPT_SHAPE_PROSE: string;
54
+ export declare const RECEIPT_CONTRACT_PROSE: string;
57
55
  /**
58
56
  * The large/cloud-agent path. This keeps file bytes out of MCP JSON and chat
59
57
  * transcripts while still giving agents a way to upload arbitrary artifacts.
60
58
  */
61
59
  export declare const SIGNED_UPLOAD_PROSE: string;
62
60
  /**
63
- * Named fragments for the skill-template generator. The templates carry
64
- * `{{prose.secretRules}}`, `{{prose.publishSequence}}`,
65
- * `{{prose.receiptShape}}`, `{{prose.signedUpload}}`,
66
- * `{{prose.checkBeforePublish}}`, `{{prose.saveState}}`,
67
- * `{{prose.updateSpace}}`, and `{{prose.continueAfterClaim}}` placeholders;
68
- * generate-skill.mjs substitutes these.
61
+ * Named fragments for the skill-template generator: the templates carry
62
+ * `{{prose.<key>}}` placeholders and generate-skill.mjs substitutes these.
69
63
  */
70
64
  export declare const AGENT_PROSE: {
71
65
  readonly positioning: string;
@@ -73,7 +67,7 @@ export declare const AGENT_PROSE: {
73
67
  readonly explainToUser: string;
74
68
  readonly failureConduct: string;
75
69
  readonly publishSequence: string;
76
- readonly receiptShape: string;
70
+ readonly receiptContract: string;
77
71
  readonly signedUpload: string;
78
72
  readonly checkBeforePublish: string;
79
73
  readonly checkBeforePublishApi: string;
@@ -83,6 +77,7 @@ export declare const AGENT_PROSE: {
83
77
  readonly saveStateApi: string;
84
78
  readonly saveStateMcp: string;
85
79
  readonly updateSpace: string;
80
+ readonly pushToDeploy: string;
86
81
  readonly continueAfterClaim: string;
87
82
  readonly lanePicker: string;
88
83
  readonly howToResearch: string;
@@ -91,4 +86,3 @@ export declare const AGENT_PROSE: {
91
86
  readonly envNotes: string;
92
87
  readonly envNotesApi: string;
93
88
  };
94
- export type AgentProseKey = keyof typeof AGENT_PROSE;
@@ -1,25 +1,22 @@
1
+ import { SPACEFAST_POSITIONING } from "../brand.js";
1
2
  import { serviceOrigin } from "../config/domains.js";
2
- // Single home for the agent-facing prose that was triplicated across the three
3
- // surfaces that teach an agent how to publish:
3
+ // Single home for the agent-facing prose the three publish-teaching surfaces
4
+ // share:
4
5
  //
5
6
  // - packages/cli/skill.template.md (the SKILL.md router + copies)
6
7
  // - packages/common/src/docs/agent-setup.ts (the /ai and /ai.txt page body)
7
- // - apps/www/src/lib/llmo.ts (the agent-card / discovery prose)
8
+ // - spacefast/www (the agent-card / discovery prose)
8
9
  //
9
- // Each surface re-stated the publish sequence, the secret rules, and the receipt
10
- // shape in its own words, so they drifted. These fragments are the one source of
11
- // truth. They are plain Markdown strings (no surface-specific framing) so any
12
- // surface can splice them in: the skill template substitutes the `{{prose.*}}`
13
- // placeholders during generation, and the TS surfaces import the strings.
14
- //
15
- // Keep these grounded in the real API receipt and the real /v1/publish flow. If
16
- // the receipt shape or secret handling changes, change it here once.
10
+ // These are plain Markdown strings with no surface-specific framing: the skill
11
+ // template substitutes the `{{prose.*}}` placeholders during generation, and the
12
+ // TS surfaces import them. Keep them grounded in the real API receipt and the
13
+ // real /v1/publish flow; if either changes, change it here once.
17
14
  const API_ORIGIN = serviceOrigin("api", "prod");
18
15
  const PUBLISH_ENDPOINT = `${API_ORIGIN}/v1/publish`;
19
16
  const EXCHANGE_ENDPOINT = `${API_ORIGIN}/v1/claim/exchange`;
20
- // Centralized secret/credential patterns excluded from a publish archive. The
21
- // `find` preflight and the `zip -x` exclusions are derived from the same list so
22
- // they can never disagree, and the no-install / skill copies stay identical.
17
+ // Secret/credential patterns excluded from a publish archive. The `find`
18
+ // preflight and the `zip -x` exclusions both derive from this list, so the
19
+ // no-install and skill copies cannot disagree.
23
20
  export const ARCHIVE_SECRET_GLOBS = [
24
21
  ".env*",
25
22
  ".npmrc",
@@ -58,10 +55,7 @@ function findExpr(globs) {
58
55
  .map((glob) => (glob.includes("/") ? `-path "${glob}"` : `-name "${glob}"`))
59
56
  .join(" -o ");
60
57
  }
61
- /**
62
- * The credential rules every surface must repeat. Treat tokens as secrets, keep
63
- * them out of logs and transcripts, and never echo them back.
64
- */
58
+ /** The credential rules every surface must repeat. */
65
59
  export const SECRET_RULES_PROSE = [
66
60
  "Treat space keys, upload tokens, device codes, and API keys as credentials. Do not print",
67
61
  "them, paste them into chat, commit them, archive them, or include them in shared logs. Avoid",
@@ -70,14 +64,17 @@ export const SECRET_RULES_PROSE = [
70
64
  "clear them after use, and avoid durable auth unless the user explicitly asks.",
71
65
  ].join("\n");
72
66
  export const POSITIONING_PROSE = [
73
- "Spacefast is a static host for AI-made web artifacts: permanent live URLs, immutable version",
74
- "URLs, anonymous publish with a claim link, and agent continuation after claim. Spaces serve",
75
- "static files; app code runs only through a declared Zero or Functions runtime.",
67
+ // What the product is, before any claim about it. One constant, shared with
68
+ // the dashboard's copied page context (see docs/page-context-prompt.ts).
69
+ SPACEFAST_POSITIONING,
70
+ "",
71
+ "Spacefast is the best place to share AI artifacts.",
72
+ "Spaces serve files directly; app code runs only through a declared Zero or Functions runtime.",
76
73
  "",
77
74
  "Do not claim that no-account publish followed by claim is unique to Spacefast. here.now and",
78
75
  "Netlify offer native anonymous publish-and-claim flows; Cloudflare offers temporary accounts",
79
76
  "and claim deployments; Vercel lets agents and platforms transfer deployments to users.",
80
- "Spacefast's narrower lane is direct static HTTP publishing with separate Access and Claim",
77
+ "Spacefast's narrower lane is direct HTTP publishing with separate Access and Claim",
81
78
  "authority, immutable versions, rollback, and agent continuation after claim. GitHub Pages",
82
79
  "remains repository-native and has no equivalent anonymous ownership-handoff flow documented.",
83
80
  ].join("\n");
@@ -87,22 +84,26 @@ export const EXPLAIN_TO_USER_PROSE = [
87
84
  "Keep live and immutable Version addresses as receipt metadata. After claim, the agent key is named",
88
85
  "and revocable in the dashboard.",
89
86
  ].join("\n");
87
+ export const FEEDBACK_PROSE = "With user approval, send feedback with `sf feedback`, MCP `execute` (search `send feedback`), or `POST /v1/feedback`. Never include credentials or private links.";
90
88
  export const FAILURE_CONDUCT_PROSE = [
91
89
  "On failure, surface the problem document's `code`, `type`, and `requestId`, then stop.",
92
- "Do not invent undocumented endpoints. Do not read credentials out of auth files. Do not",
93
- "retry a one-shot operation without reusing its `Idempotency-Key`.",
90
+ "Do not invent undocumented endpoints. Do not read credentials out of auth files. Retrying a",
91
+ "failed publish with the same files is safe: it returns the Space the first attempt created.",
92
+ FEEDBACK_PROSE,
94
93
  ].join("\n");
95
94
  export const LANE_PICKER_PROSE = [
96
- "Pick the lane by environment: filesystem plus repeat work means CLI or On-Device MCP; no",
97
- "filesystem means hosted MCP; one-off publish means curl; CI means an agent account. After",
98
- "success, recommend the best next-time setup for the user's client.",
95
+ "Pick the lane by environment: a one-off publish means curl; a git checkout means the push.new",
96
+ "remote; repeat work with a filesystem means the CLI the best setup, `npx -y spacefast` runs it",
97
+ "with no install, and `sf plugins` installs your client's plugin; no filesystem means hosted MCP;",
98
+ "CI means a machine OAuth client. After success, recommend the best next-time setup for the",
99
+ "user's client.",
99
100
  ].join("\n");
100
101
  export const HOW_TO_RESEARCH_PROSE = [
101
102
  "For an error, fetch the problem document's `type` URL. For capability questions, search the OpenAPI catalog with `execute` or append",
102
103
  "`.md` to website docs URLs. For contracts, read `publish-spec.json` or OpenAPI. Never guess",
103
104
  "limits: publish, then read diagnostics.",
104
105
  ].join("\n");
105
- /** Curl/API skill lane: docs URLs only no MCP or CLI tools required. */
106
+ /** Curl/API skill lane: docs URLs only, no MCP or CLI tools required. */
106
107
  export const HOW_TO_RESEARCH_API_PROSE = [
107
108
  "For an error, fetch the problem document's `type` URL. For capability questions, append `.md` to website docs",
108
109
  "URLs (for example `https://spacefast.com/docs/agents.md`) or read",
@@ -111,8 +112,9 @@ export const HOW_TO_RESEARCH_API_PROSE = [
111
112
  ].join("\n");
112
113
  /** MCP plugin skill lane: prefer skills for workflows and execute for the OpenAPI catalog. */
113
114
  export const HOW_TO_RESEARCH_MCP_PROSE = [
114
- "For an error, fetch the problem document's `type` URL when present. For workflows, call the `skills` tool",
115
- "(list/search, then read by exact name). For capability questions, use `execute` with `tools.search`.",
115
+ "For an error, fetch the problem document's `type` URL when present. For workflows and how-to",
116
+ "recipes, call `search_docs`; the `skills` tool serves the `execute` guide, not workflow recipes.",
117
+ "For capability questions, use `execute` with `tools.search`.",
116
118
  "For contracts, read `publish-spec.json` or OpenAPI via docs. Never guess limits: publish,",
117
119
  "then inspect status or diagnostics through `execute`.",
118
120
  ].join("\n");
@@ -127,9 +129,8 @@ export const ENV_NOTES_API_PROSE = [
127
129
  "https://spacefast.com/docs/agents.",
128
130
  ].join("\n");
129
131
  /**
130
- * The CHECK verb: before ever creating a space, look for the one the project
131
- * already has. This is what keeps a returning agent from pushing a duplicate
132
- * anonymous space instead of a new version.
132
+ * The CHECK verb: look for an existing space before creating one, so a returning
133
+ * agent publishes a new version instead of a duplicate anonymous space.
133
134
  */
134
135
  export const CHECK_BEFORE_PUBLISH_PROSE = [
135
136
  "Before creating a space, check whether this project already has one:",
@@ -141,7 +142,7 @@ export const CHECK_BEFORE_PUBLISH_PROSE = [
141
142
  "",
142
143
  "If any of these finds a space, publish a new version to that `spaceId` (see the update",
143
144
  "instructions) instead of creating another space. Only create a new space when none exists",
144
- "and the user wants a new site.",
145
+ "and the user wants a new one.",
145
146
  ].join("\n");
146
147
  export const CHECK_BEFORE_PUBLISH_API_PROSE = [
147
148
  "Before creating a space, check whether this project already has one:",
@@ -157,7 +158,7 @@ export const CHECK_BEFORE_PUBLISH_API_PROSE = [
157
158
  "",
158
159
  "If any of these finds a space, publish a new version to that `spaceId` (see the update",
159
160
  "instructions) instead of creating another space. Only create a new space when none exists",
160
- "and the user wants a new site.",
161
+ "and the user wants a new one.",
161
162
  ].join("\n");
162
163
  export const CHECK_BEFORE_PUBLISH_MCP_PROSE = [
163
164
  "Before creating a space, check whether this project already has one:",
@@ -168,7 +169,7 @@ export const CHECK_BEFORE_PUBLISH_MCP_PROSE = [
168
169
  "- When authenticated, use `execute` to call the generated list-spaces operation.",
169
170
  "",
170
171
  "If any of these finds a space, publish a new version to that space instead of creating",
171
- "another. Only create a new space when none exists and the user wants a new site.",
172
+ "another. Only create a new space when none exists and the user wants a new one.",
172
173
  ].join("\n");
173
174
  /**
174
175
  * The SAVE verb: persist the space id and credential after a first publish so
@@ -219,6 +220,34 @@ export const SAVE_STATE_MCP_PROSE = [
219
220
  * The UPDATE verb: a new version to the SAME space. Never re-run the
220
221
  * first-publish recipe when saved state exists.
221
222
  */
223
+ /*
224
+ * Push to deploy. Written for an agent that is already in a checkout, where a
225
+ * push is fewer moves than an upload — and where the receipt comes back in the
226
+ * push output, so there is no second call to learn the URL.
227
+ *
228
+ * The GitHub note is not an aside: a space bound to a GitHub repository refuses
229
+ * direct pushes, and an agent that does not know that reads the refusal as a
230
+ * broken remote instead of as the answer it is.
231
+ */
232
+ export const PUSH_TO_DEPLOY_PROSE = [
233
+ "A space's git remote is a real remote, and pushing to it publishes. Every publish receipt",
234
+ "and the Space resource carry `git.remoteUrl`; authenticate as HTTP Basic user `t` with the",
235
+ "space key or an API key as the password. The same key you already have works — publishing",
236
+ "and pushing are one credential, not two.",
237
+ "",
238
+ "```bash",
239
+ "git remote add spacefast https://t:<key>@push.new/<space-slug>.git",
240
+ "git push spacefast main",
241
+ "```",
242
+ "",
243
+ "The push output is the receipt: it reports the live URL, the immutable version URL, and —",
244
+ "for an anonymous space — the claim link and its expiry, as `remote:` lines. Report those to",
245
+ "the user the same way you would report a publish receipt. A deploy that outruns the push",
246
+ "prints a status URL instead and finishes on its own; the push still exits 0.",
247
+ "",
248
+ "If `git.managedBy` is `github`, this space deploys from `git.repository` and a direct push is",
249
+ "refused. Push to that GitHub repository instead, or tell the user to disconnect GitHub first.",
250
+ ].join("\n");
222
251
  export const UPDATE_SPACE_PROSE = [
223
252
  "To publish a new version of an existing space, send the same `spaceId` with bearer auth —",
224
253
  "never re-run the first-publish recipe when saved state exists:",
@@ -255,7 +284,7 @@ export const CONTINUE_AFTER_CLAIM_PROSE = [
255
284
  "Save `data.credential.accessToken` into `.spacefast/state.json` (replace the `claimToken`",
256
285
  'field with `"accessToken"`), then retry the publish with the new bearer. The exchange works',
257
286
  "exactly once; if it returns `continuation_used` or `continuation_unavailable`, ask the user",
258
- "to create an API key in the dashboard (Account → API keys).",
287
+ "to create an API key in the dashboard (teamAgents → API keys).",
259
288
  ].join("\n");
260
289
  const LEXICAL_PUBLISH_PATH_PREFLIGHT = [
261
290
  'case "$publish_target" in /*) check_path="$publish_target" ;; *) check_path="$PWD/$publish_target" ;; esac',
@@ -321,7 +350,7 @@ const SECURE_ARCHIVE_RECIPE = [
321
350
  ];
322
351
  /**
323
352
  * The zero-install publish sequence: a replay-safe curl loop for a file, a
324
- * secret-preflighted zip for a folder. This is the load-bearing rung 0 — an
353
+ * secret-preflighted zip for a folder. This is the load-bearing rung 0. An
325
354
  * agent that stops here must still succeed.
326
355
  */
327
356
  export const PUBLISH_SEQUENCE_PROSE = [
@@ -340,12 +369,8 @@ export const PUBLISH_SEQUENCE_PROSE = [
340
369
  '[ -f "$publish_target" ] || { printf "error: publish file is not a regular file\\n" >&2; exit 2; }',
341
370
  'escaped_path="${publish_target//\\\\/\\\\\\\\}"',
342
371
  'escaped_path="${escaped_path//\\"/\\\\\\"}"',
343
- "idempotency_key=\"publish-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')\"",
344
- "idempotency_principal=\"$(od -An -N32 -tx1 /dev/urandom | tr -d ' \\n')\"",
345
372
  'api_origin="${SPACEFAST_API_ORIGIN:-https://api.spacefast.com}"',
346
373
  'curl -q -sS -F "files=@\\"$escaped_path\\"" \\',
347
- ' -H "Idempotency-Key: $idempotency_key" \\',
348
- ' -H "x-spacefast-idempotency-principal: $idempotency_principal" \\',
349
374
  ' "$api_origin/v1/publish"',
350
375
  "SPACEFAST_PUBLISH",
351
376
  "```",
@@ -379,7 +404,7 @@ export const PUBLISH_SEQUENCE_PROSE = [
379
404
  * What the JSON publish receipt contains and how to read it. The receipt is the
380
405
  * source of truth; never invent a separate success signal.
381
406
  */
382
- export const RECEIPT_SHAPE_PROSE = [
407
+ export const RECEIPT_CONTRACT_PROSE = [
383
408
  "The JSON receipt is the source of truth:",
384
409
  "",
385
410
  "- `data.space.liveUrl` — the live URL.",
@@ -425,18 +450,14 @@ export const SIGNED_UPLOAD_PROSE = [
425
450
  "For each `data.upload.targets[]`, send `target.method` to `target.url` with `target.headers`.",
426
451
  'If `target.body.kind` is `url`, send JSON `{ "url": target.body.url }`; otherwise send the',
427
452
  "local file bytes for `target.path`. Then call `data.links.finalize` and share the final receipt.",
428
- "If the session expires or only a page of files is returned, follow `data.links.resume`.",
453
+ "If the session expires or only a page of files is returned, follow `data.links.refresh`.",
429
454
  "",
430
455
  "A manifest entry may name a `sourceUrl`, so bytes that already live on a public HTTPS URL never",
431
456
  "have to round-trip through the agent. Do not base64 large archives into chat or MCP tool arguments.",
432
457
  ].join("\n");
433
458
  /**
434
- * Named fragments for the skill-template generator. The templates carry
435
- * `{{prose.secretRules}}`, `{{prose.publishSequence}}`,
436
- * `{{prose.receiptShape}}`, `{{prose.signedUpload}}`,
437
- * `{{prose.checkBeforePublish}}`, `{{prose.saveState}}`,
438
- * `{{prose.updateSpace}}`, and `{{prose.continueAfterClaim}}` placeholders;
439
- * generate-skill.mjs substitutes these.
459
+ * Named fragments for the skill-template generator: the templates carry
460
+ * `{{prose.<key>}}` placeholders and generate-skill.mjs substitutes these.
440
461
  */
441
462
  export const AGENT_PROSE = {
442
463
  positioning: POSITIONING_PROSE,
@@ -444,7 +465,7 @@ export const AGENT_PROSE = {
444
465
  explainToUser: EXPLAIN_TO_USER_PROSE,
445
466
  failureConduct: FAILURE_CONDUCT_PROSE,
446
467
  publishSequence: PUBLISH_SEQUENCE_PROSE,
447
- receiptShape: RECEIPT_SHAPE_PROSE,
468
+ receiptContract: RECEIPT_CONTRACT_PROSE,
448
469
  signedUpload: SIGNED_UPLOAD_PROSE,
449
470
  checkBeforePublish: CHECK_BEFORE_PUBLISH_PROSE,
450
471
  checkBeforePublishApi: CHECK_BEFORE_PUBLISH_API_PROSE,
@@ -454,6 +475,7 @@ export const AGENT_PROSE = {
454
475
  saveStateApi: SAVE_STATE_API_PROSE,
455
476
  saveStateMcp: SAVE_STATE_MCP_PROSE,
456
477
  updateSpace: UPDATE_SPACE_PROSE,
478
+ pushToDeploy: PUSH_TO_DEPLOY_PROSE,
457
479
  continueAfterClaim: CONTINUE_AFTER_CLAIM_PROSE,
458
480
  lanePicker: LANE_PICKER_PROSE,
459
481
  howToResearch: HOW_TO_RESEARCH_PROSE,