experimental-a2 0.19.0 → 0.21.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.
- package/CHANGELOG.md +50 -0
- package/README.md +1 -1
- package/dist/{actor-C7b-1arn.d.ts → actor-HnGmrOZZ.d.ts} +5 -5
- package/dist/{actor-C7b-1arn.d.ts.map → actor-HnGmrOZZ.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +2 -2
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +5 -5
- package/dist/{actor-shared-t7w8xIKP.js → actor-shared-DFET5q16.js} +3 -3
- package/dist/{actor-shared-t7w8xIKP.js.map → actor-shared-DFET5q16.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +54 -43
- package/dist/actor.js.map +1 -1
- package/dist/{ai-Cz3hqVhk.d.ts → ai-CvEnqVfo.d.ts} +8 -8
- package/dist/{ai-Cz3hqVhk.d.ts.map → ai-CvEnqVfo.d.ts.map} +1 -1
- package/dist/{ai-mMiWul1T.js → ai-CzE672Q3.js} +9 -9
- package/dist/ai-CzE672Q3.js.map +1 -0
- package/dist/ai-client.d.ts +2 -2
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +1 -1
- package/dist/ai-fx.d.ts.map +1 -1
- package/dist/ai-fx.js +36 -9
- package/dist/ai-fx.js.map +1 -1
- package/dist/{ai-harness-CV6rXbB3.d.ts → ai-harness-Bfk-Lycn.d.ts} +2 -2
- package/dist/ai-harness-Bfk-Lycn.d.ts.map +1 -0
- package/dist/{ai-harness-BuuC-gJ3.js → ai-harness-dQeJaR1t.js} +17 -4
- package/dist/ai-harness-dQeJaR1t.js.map +1 -0
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-pi.js +4 -4
- package/dist/ai-pi.js.map +1 -1
- package/dist/ai-react.d.ts +5 -5
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +14 -17
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +84 -75
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BhHLoNKF.js → client-9IeTsiAs.js} +5 -5
- package/dist/{client-BhHLoNKF.js.map → client-9IeTsiAs.js.map} +1 -1
- package/dist/{client-h_DKFAjj.d.ts → client-CUMbS_38.d.ts} +3 -3
- package/dist/{client-h_DKFAjj.d.ts.map → client-CUMbS_38.d.ts.map} +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/{contract-BYKASlCV.js → contract-CWukt01d.js} +2 -2
- package/dist/contract-CWukt01d.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/{initial-snapshot-Bx-c0pDP.d.ts → initial-snapshot-D3Lve43o.d.ts} +4 -4
- package/dist/initial-snapshot-D3Lve43o.d.ts.map +1 -0
- package/dist/{internal-CscSY7K_.js → internal-Bc4PvfHk.js} +10 -3
- package/dist/internal-Bc4PvfHk.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +1 -1
- package/dist/otel.js.map +1 -1
- package/dist/{react-BBXgHWFc.d.ts → react-BoHvC3v9.d.ts} +4 -4
- package/dist/{react-BBXgHWFc.d.ts.map → react-BoHvC3v9.d.ts.map} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{reducer-CqA59Q6U.d.ts → reducer-CYPHsSmd.d.ts} +2 -2
- package/dist/{reducer-CqA59Q6U.d.ts.map → reducer-CYPHsSmd.d.ts.map} +1 -1
- package/dist/{reducer-BAXc8JyF.js → reducer-CjmvZSAb.js} +2 -2
- package/dist/{reducer-BAXc8JyF.js.map → reducer-CjmvZSAb.js.map} +1 -1
- package/dist/runtime-consumer-BWmht60z.js +12 -0
- package/dist/runtime-consumer-BWmht60z.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +7 -7
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/scheduler-task-X1SToDjP.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +5 -4
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-DYAATFuT.d.ts → server-BshFZ0eL.d.ts} +108 -41
- package/dist/server-BshFZ0eL.d.ts.map +1 -0
- package/dist/{server-D-Y4pqwu.js → server-DvIB8kj5.js} +423 -135
- package/dist/server-DvIB8kj5.js.map +1 -0
- package/dist/server.browser.js +1 -1
- package/dist/server.browser.js.map +1 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{session-clear-C-QwTXV1.d.ts → session-clear-Bk_29eEw.d.ts} +2 -2
- package/dist/session-clear-Bk_29eEw.d.ts.map +1 -0
- package/dist/{session-clear-DGI4jTQ5.js → session-clear-wHd5GCpZ.js} +5 -4
- package/dist/session-clear-wHd5GCpZ.js.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-Z2qjw50n.d.ts +523 -0
- package/dist/store-Z2qjw50n.d.ts.map +1 -0
- package/dist/store-codec-DwrWlgQx.js +65 -0
- package/dist/store-codec-DwrWlgQx.js.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +393 -210
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-7WJV0YWP.js → store-polling-DYNyxCev.js} +2 -2
- package/dist/{store-polling-7WJV0YWP.js.map → store-polling-DYNyxCev.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +1595 -673
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-B1ixeqIS.js → store-presence-polling-CzQ0wdtM.js} +3 -3
- package/dist/{store-presence-polling-B1ixeqIS.js.map → store-presence-polling-CzQ0wdtM.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +4 -4
- package/dist/store-redis-notify-IiVdlYHc.js +2919 -0
- package/dist/store-redis-notify-IiVdlYHc.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +2 -2
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +517 -287
- package/dist/store-sqlite.js.map +1 -1
- package/dist/{telemetry-Cyfqo6Qt.d.ts → telemetry-CWwsh-Gx.d.ts} +3 -3
- package/dist/telemetry-CWwsh-Gx.d.ts.map +1 -0
- package/dist/{wire-BKxH1vbP.js → wire-CZGqNZRx.js} +5 -4
- package/dist/wire-CZGqNZRx.js.map +1 -0
- package/docs/01-quickstart.mdx +28 -19
- package/docs/02-examples.mdx +1 -1
- package/docs/actors/01-introduction.mdx +9 -1
- package/docs/actors/02-concurrency.mdx +1 -1
- package/docs/actors/meta.ts +1 -1
- package/docs/agents/01-introduction.mdx +1 -1
- package/docs/agents/02-tools.mdx +13 -12
- package/docs/agents/03-conversations.mdx +31 -21
- package/docs/agents/04-harnesses.mdx +7 -3
- package/docs/agents/meta.ts +1 -1
- package/docs/client/03-presence.mdx +18 -14
- package/docs/client/meta.ts +1 -1
- package/docs/contract/01-events.mdx +2 -2
- package/docs/contract/02-state.mdx +18 -14
- package/docs/contract/03-sessions.mdx +32 -25
- package/docs/index.mdx +31 -26
- package/docs/reference/01-core.mdx +12 -12
- package/docs/reference/02-server.mdx +247 -192
- package/docs/reference/04-ai.mdx +29 -20
- package/docs/reference/05-telemetry.mdx +146 -0
- package/docs/reference/06-errors.mdx +24 -18
- package/docs/reference/meta.ts +1 -1
- package/docs/schedulers/01-overview.mdx +112 -0
- package/docs/schedulers/02-vercel-queues.mdx +87 -0
- package/docs/schedulers/03-qstash.mdx +123 -0
- package/docs/schedulers/meta.ts +1 -0
- package/docs/server/01-consumers.mdx +415 -0
- package/docs/server/02-timers.mdx +33 -23
- package/docs/server/03-cancellation.mdx +52 -44
- package/docs/server/04-routes.mdx +4 -1
- package/docs/server/05-sql-projections.mdx +391 -0
- package/docs/server/06-guarantees.mdx +60 -50
- package/docs/stores/01-overview.mdx +37 -0
- package/docs/stores/02-redis.mdx +57 -0
- package/docs/stores/03-redis-http.mdx +56 -0
- package/docs/stores/04-postgres.mdx +172 -0
- package/docs/stores/05-sqlite.mdx +36 -0
- package/docs/stores/06-memory.mdx +22 -0
- package/docs/stores/meta.ts +1 -0
- package/docs/upgrades/01-overview.mdx +30 -0
- package/docs/upgrades/79-0-21.mdx +165 -0
- package/docs/upgrades/80-0-20.mdx +43 -0
- package/docs/upgrades/81-0-19.mdx +33 -0
- package/docs/upgrades/82-0-18.mdx +33 -0
- package/docs/upgrades/83-0-17.mdx +69 -0
- package/docs/upgrades/meta.ts +3 -0
- package/examples/playground/DEPLOY.md +48 -11
- package/examples/playground/app/a0/apps/[appId]/route.ts +6 -10
- package/examples/playground/app/a0/chat.test.tsx +421 -330
- package/examples/playground/app/a0/sandbox-log.ts +17 -15
- package/examples/playground/app/a0/server.ts +4 -4
- package/examples/playground/app/a0/shell.tsx +1 -1
- package/examples/playground/app/a0/uploads/route.test.ts +59 -0
- package/examples/playground/app/a2slack/a2slack.css +1067 -0
- package/examples/playground/app/a2slack/c/[channel]/page.tsx +20 -0
- package/examples/playground/app/a2slack/consumers/agent.ts +63 -0
- package/examples/playground/app/a2slack/consumers/feed.ts +164 -0
- package/examples/playground/app/a2slack/consumers/notify.ts +116 -0
- package/examples/playground/app/a2slack/consumers/search.ts +37 -0
- package/examples/playground/app/a2slack/consumers/triage.ts +31 -0
- package/examples/playground/app/a2slack/consumers/welcome.ts +25 -0
- package/examples/playground/app/a2slack/db/a2.ts +206 -0
- package/examples/playground/app/a2slack/db/index.ts +73 -0
- package/examples/playground/app/a2slack/db/migrations/0000_extensions.sql +4 -0
- package/examples/playground/app/a2slack/db/migrations/0001_workspace.sql +74 -0
- package/examples/playground/app/a2slack/db/migrations/0002_a2.sql +215 -0
- package/examples/playground/app/a2slack/db/migrations/0003_a2_batch.sql +67 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0000_snapshot.json +18 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0001_snapshot.json +500 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0002_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0003_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +34 -0
- package/examples/playground/app/a2slack/db/queries.ts +263 -0
- package/examples/playground/app/a2slack/db/schema.ts +145 -0
- package/examples/playground/app/a2slack/drills/a2slack.drill.ts +708 -0
- package/examples/playground/app/a2slack/drills/harness.ts +244 -0
- package/examples/playground/app/a2slack/events/route.ts +36 -0
- package/examples/playground/app/a2slack/identity.ts +44 -0
- package/examples/playground/app/a2slack/inbox/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/data/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/page.tsx +101 -0
- package/examples/playground/app/a2slack/lag.ts +73 -0
- package/examples/playground/app/a2slack/layout.tsx +43 -0
- package/examples/playground/app/a2slack/messages/route.ts +17 -0
- package/examples/playground/app/a2slack/model.test.ts +208 -0
- package/examples/playground/app/a2slack/model.ts +369 -0
- package/examples/playground/app/a2slack/models.test.ts +72 -0
- package/examples/playground/app/a2slack/page.tsx +10 -0
- package/examples/playground/app/a2slack/scheduler/route.ts +5 -0
- package/examples/playground/app/a2slack/search/route.ts +40 -0
- package/examples/playground/app/a2slack/search-index.ts +108 -0
- package/examples/playground/app/a2slack/server.test.ts +315 -0
- package/examples/playground/app/a2slack/server.ts +55 -0
- package/examples/playground/app/a2slack/sessions.ts +50 -0
- package/examples/playground/app/a2slack/thread/route.ts +8 -0
- package/examples/playground/app/a2slack/ui/ChannelView.tsx +87 -0
- package/examples/playground/app/a2slack/ui/Composer.tsx +117 -0
- package/examples/playground/app/a2slack/ui/DotsShadow.tsx +42 -0
- package/examples/playground/app/a2slack/ui/Feed.tsx +157 -0
- package/examples/playground/app/a2slack/ui/InboxContext.tsx +63 -0
- package/examples/playground/app/a2slack/ui/InboxView.tsx +82 -0
- package/examples/playground/app/a2slack/ui/Menu.tsx +128 -0
- package/examples/playground/app/a2slack/ui/MessageRow.tsx +314 -0
- package/examples/playground/app/a2slack/ui/NameGate.tsx +35 -0
- package/examples/playground/app/a2slack/ui/SearchContext.tsx +146 -0
- package/examples/playground/app/a2slack/ui/SearchResults.tsx +97 -0
- package/examples/playground/app/a2slack/ui/Shell.tsx +34 -0
- package/examples/playground/app/a2slack/ui/Sidebar.tsx +199 -0
- package/examples/playground/app/a2slack/ui/ThreadPanel.tsx +134 -0
- package/examples/playground/app/a2slack/ui/useInbox.ts +71 -0
- package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +116 -90
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +107 -76
- package/examples/playground/app/agent/ingress.test.ts +41 -17
- package/examples/playground/app/agent/model-input.test.tsx +92 -43
- package/examples/playground/app/agent/server.ts +3 -3
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.test.tsx +2 -2
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -1
- package/examples/playground/app/chat/server.test.ts +38 -65
- package/examples/playground/app/chat/server.ts +45 -35
- package/examples/playground/app/components/activity-history.test.tsx +60 -18
- package/examples/playground/app/components/demo-icons.tsx +7 -0
- package/examples/playground/app/components/demos.tsx +7 -0
- package/examples/playground/app/components/fast-link.test.tsx +3 -5
- package/examples/playground/app/initial-state.test.tsx +86 -45
- package/examples/playground/app/orders/[orderId]/order-client.tsx +2 -2
- package/examples/playground/app/orders/page.tsx +1 -1
- package/examples/playground/app/orders/server.ts +49 -30
- package/examples/playground/app/recovery/[recoveryId]/recovery-client.tsx +14 -15
- package/examples/playground/app/recovery/page.tsx +4 -4
- package/examples/playground/app/recovery/server.ts +23 -32
- package/examples/playground/drizzle.config.ts +14 -0
- package/examples/playground/next.config.ts +6 -1
- package/examples/playground/package.json +13 -1
- package/examples/playground/scripts/migrate.mjs +31 -0
- package/examples/playground/test/agent.ts +35 -0
- package/examples/playground/test/model.ts +149 -0
- package/examples/playground/test/serve.ts +184 -0
- package/examples/playground/test/server-only.ts +1 -0
- package/examples/playground/vercel.json +9 -0
- package/examples/playground/vitest.config.ts +27 -0
- package/examples/playground/vitest.drills.config.ts +32 -0
- package/package.json +7 -2
- package/sql/postgres/drizzle-schema.ts +206 -0
- package/sql/postgres/notifications.sql +24 -0
- package/sql/postgres/schema.sql +276 -0
- package/src/actor.ts +101 -71
- package/src/ai-agent.ts +154 -115
- package/src/ai-control-server.ts +8 -8
- package/src/ai-fx.ts +41 -9
- package/src/ai-harness.ts +28 -2
- package/src/ai-pi.ts +2 -2
- package/src/ai-server.ts +2 -2
- package/src/ai.ts +7 -7
- package/src/contract.ts +1 -1
- package/src/deterministic-id.ts +26 -3
- package/src/errors.ts +9 -6
- package/src/idempotent-replay.ts +0 -1
- package/src/index.ts +1 -1
- package/src/internal.ts +17 -6
- package/src/otel.ts +1 -1
- package/src/postgres-notifications.ts +0 -22
- package/src/postgres-resources.ts +6 -9
- package/src/postgres-schema.ts +121 -0
- package/src/push-envelope.ts +20 -0
- package/src/runtime-consumer.ts +10 -0
- package/src/scheduler-qstash.ts +12 -5
- package/src/scheduler-task.ts +2 -2
- package/src/scheduler-vercel.ts +10 -4
- package/src/server.browser.ts +1 -1
- package/src/server.ts +839 -238
- package/src/session-socket.ts +1 -1
- package/src/store-codec.ts +66 -0
- package/src/store-memory.ts +581 -255
- package/src/store-postgres.ts +1984 -943
- package/src/store-redis-core.ts +1624 -1152
- package/src/store-redis-notify.ts +2 -0
- package/src/store-sqlite.ts +891 -486
- package/src/store.ts +227 -97
- package/src/telemetry.ts +2 -1
- package/src/wire.ts +1 -0
- package/dist/ai-harness-BuuC-gJ3.js.map +0 -1
- package/dist/ai-harness-CV6rXbB3.d.ts.map +0 -1
- package/dist/ai-mMiWul1T.js.map +0 -1
- package/dist/contract-BYKASlCV.js.map +0 -1
- package/dist/idempotent-replay-JdgMQRRm.js +0 -21
- package/dist/idempotent-replay-JdgMQRRm.js.map +0 -1
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +0 -1
- package/dist/internal-CscSY7K_.js.map +0 -1
- package/dist/server-D-Y4pqwu.js.map +0 -1
- package/dist/server-DYAATFuT.d.ts.map +0 -1
- package/dist/session-clear-C-QwTXV1.d.ts.map +0 -1
- package/dist/session-clear-DGI4jTQ5.js.map +0 -1
- package/dist/store-CyLUy86v.d.ts +0 -404
- package/dist/store-CyLUy86v.d.ts.map +0 -1
- package/dist/store-codec-DTG0Ftek.js +0 -10
- package/dist/store-codec-DTG0Ftek.js.map +0 -1
- package/dist/store-redis-notify-CM0AfLY0.js +0 -2544
- package/dist/store-redis-notify-CM0AfLY0.js.map +0 -1
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +0 -1
- package/dist/wire-BKxH1vbP.js.map +0 -1
- package/docs/reference/05-adapters.mdx +0 -351
- package/docs/server/01-handlers.mdx +0 -200
- package/docs/server/05-production.mdx +0 -323
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import 'server-only'
|
|
2
2
|
import { contract } from 'experimental-a2'
|
|
3
3
|
import { vercelQueues } from 'experimental-a2/scheduler-vercel'
|
|
4
|
-
import { createServer } from 'experimental-a2/server'
|
|
4
|
+
import { consumer, createServer } from 'experimental-a2/server'
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
import { store } from '@/lib/store'
|
|
7
7
|
import { isAppId } from './apps'
|
|
@@ -31,24 +31,26 @@ export const sandboxScheduler =
|
|
|
31
31
|
? vercelQueues({ topic: 'a2-a0-sandboxes' })
|
|
32
32
|
: undefined
|
|
33
33
|
|
|
34
|
+
const deleteIdleSandbox = consumer({
|
|
35
|
+
contract: sandboxLog,
|
|
36
|
+
name: 'delete-idle-sandbox',
|
|
37
|
+
types: ['sandbox.idle.checked'],
|
|
38
|
+
}).run(async ({ event }) => {
|
|
39
|
+
const { appId } = event.payload
|
|
40
|
+
// A prompt since the sandbox opened makes it a real app's, whichever
|
|
41
|
+
// harness's agent got it.
|
|
42
|
+
const reads = await Promise.all(
|
|
43
|
+
Object.values(agentServers).map((server) => server.session(appId).state()),
|
|
44
|
+
)
|
|
45
|
+
if (reads.every(({ state }) => state.messages.length === 0))
|
|
46
|
+
await deleteAppSandbox({ appId })
|
|
47
|
+
})
|
|
48
|
+
|
|
34
49
|
export const sandboxServer = createServer({
|
|
35
50
|
...(store ? { store } : {}),
|
|
36
51
|
...(sandboxScheduler ? { scheduler: sandboxScheduler } : {}),
|
|
37
52
|
contract: sandboxLog,
|
|
38
|
-
|
|
39
|
-
'sandbox.idle.checked': async ({ event }) => {
|
|
40
|
-
const { appId } = event.payload
|
|
41
|
-
// A prompt since the sandbox opened makes it a real app's, whichever
|
|
42
|
-
// harness's agent got it.
|
|
43
|
-
const reads = await Promise.all(
|
|
44
|
-
Object.values(agentServers).map((server) =>
|
|
45
|
-
server.session(appId).state(),
|
|
46
|
-
),
|
|
47
|
-
)
|
|
48
|
-
if (reads.every(({ state }) => state.messages.length === 0))
|
|
49
|
-
await deleteAppSandbox({ appId })
|
|
50
|
-
},
|
|
51
|
-
},
|
|
53
|
+
consumers: [deleteIdleSandbox],
|
|
52
54
|
})
|
|
53
55
|
|
|
54
56
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import 'server-only'
|
|
2
2
|
import { waitUntil } from '@vercel/functions'
|
|
3
3
|
import { tool } from 'ai'
|
|
4
|
-
import {
|
|
4
|
+
import { consumerContext } from 'experimental-a2/ai'
|
|
5
5
|
import { fx } from 'experimental-a2/ai/fx'
|
|
6
6
|
import { pi } from 'experimental-a2/ai/pi'
|
|
7
7
|
import { agent } from 'experimental-a2/ai/server'
|
|
@@ -55,7 +55,7 @@ const tools = {
|
|
|
55
55
|
execute: ({ path }) =>
|
|
56
56
|
timed(async () =>
|
|
57
57
|
previewUrl({
|
|
58
|
-
sandbox: await appSandbox({ appId:
|
|
58
|
+
sandbox: await appSandbox({ appId: consumerContext().session.id }),
|
|
59
59
|
path,
|
|
60
60
|
}),
|
|
61
61
|
),
|
|
@@ -68,7 +68,7 @@ const tools = {
|
|
|
68
68
|
timeoutSeconds: z.number().int().min(1).max(240).default(120),
|
|
69
69
|
}),
|
|
70
70
|
execute: ({ command, timeoutSeconds }) => {
|
|
71
|
-
const context =
|
|
71
|
+
const context = consumerContext()
|
|
72
72
|
return timed(async () =>
|
|
73
73
|
bash({
|
|
74
74
|
sandbox: await appSandbox({ appId: context.session.id }),
|
|
@@ -190,7 +190,7 @@ export const listServer = createServer({
|
|
|
190
190
|
contract: appList,
|
|
191
191
|
})
|
|
192
192
|
|
|
193
|
-
/** The dev tools' crash logs, one per app. They have no
|
|
193
|
+
/** The dev tools' crash logs, one per app. They have no consumers. */
|
|
194
194
|
export const crashServer = createServer({
|
|
195
195
|
...(store ? { store } : {}),
|
|
196
196
|
contract: crashLog,
|
|
@@ -106,7 +106,7 @@ export function A0Shell({
|
|
|
106
106
|
const remove = async (target: App): Promise<void> => {
|
|
107
107
|
if (
|
|
108
108
|
!confirm(
|
|
109
|
-
`Delete ${target.name}? This deletes its sandbox, with everything the agent built in it.`,
|
|
109
|
+
`Delete ${target.name}? This deletes its conversation and its sandbox, with everything the agent built in it.`,
|
|
110
110
|
)
|
|
111
111
|
)
|
|
112
112
|
return
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
|
2
|
+
import { POST } from './route'
|
|
3
|
+
|
|
4
|
+
const appId = crypto.randomUUID()
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.stubEnv('BLOB_READ_WRITE_TOKEN', 'vercel_blob_rw_teststore_secret')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.unstubAllEnvs()
|
|
12
|
+
vi.restoreAllMocks()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
/** What the browser's `upload()` asks its `handleUploadUrl` for first. */
|
|
16
|
+
const tokenRequest = (pathname: string, clientPayload: string | null) =>
|
|
17
|
+
POST(
|
|
18
|
+
new Request('http://test/a0/uploads', {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: { 'content-type': 'application/json' },
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
type: 'blob.generate-client-token',
|
|
23
|
+
payload: { pathname, clientPayload, multipart: false },
|
|
24
|
+
}),
|
|
25
|
+
}),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
/** The upload a client token allows, as Blob reads it from the token. */
|
|
29
|
+
const allowed = (clientToken: string): unknown => {
|
|
30
|
+
const [, encoded] = /^vercel_blob_client_teststore_(.+)$/.exec(clientToken)!
|
|
31
|
+
const signed = Buffer.from(encoded!, 'base64').toString()
|
|
32
|
+
const payload = signed.slice(signed.indexOf('.') + 1)
|
|
33
|
+
return JSON.parse(Buffer.from(payload, 'base64').toString())
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
it("signs an upload into the app's own folder, without a request of its own", async () => {
|
|
37
|
+
const fetch = vi.spyOn(globalThis, 'fetch')
|
|
38
|
+
const response = await tokenRequest(`a0/${appId}/shot.png`, appId)
|
|
39
|
+
expect(response.status).toBe(200)
|
|
40
|
+
const body = (await response.json()) as { type: string; clientToken: string }
|
|
41
|
+
expect(body.type).toBe('blob.generate-client-token')
|
|
42
|
+
expect(allowed(body.clientToken)).toMatchObject({
|
|
43
|
+
pathname: `a0/${appId}/shot.png`,
|
|
44
|
+
addRandomSuffix: true,
|
|
45
|
+
})
|
|
46
|
+
expect(fetch).not.toHaveBeenCalled()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it.each([
|
|
50
|
+
['another app', `a0/${crypto.randomUUID()}/shot.png`, appId],
|
|
51
|
+
['no app', `a0/${appId}/shot.png`, null],
|
|
52
|
+
['something other than an app', 'a0/list/shot.png', 'list'],
|
|
53
|
+
])('refuses an upload for %s', async (_, pathname, clientPayload) => {
|
|
54
|
+
const response = await tokenRequest(pathname, clientPayload)
|
|
55
|
+
expect(response.status).toBe(400)
|
|
56
|
+
expect(await response.json()).toEqual({
|
|
57
|
+
error: "An attachment goes in its app's folder",
|
|
58
|
+
})
|
|
59
|
+
})
|