@flowdular/sandbox 0.3.1 → 0.4.0

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 (72) hide show
  1. package/internal/coding-agent/src/drivers/byok.ts +16 -1
  2. package/internal/coding-agent/src/index.ts +1 -0
  3. package/internal/coding-agent/src/roles/defaults.ts +9 -0
  4. package/internal/coding-agent/src/roles/skills.ts +1 -0
  5. package/internal/coding-agent/src/types.ts +11 -0
  6. package/package.json +2 -2
  7. package/public/og.png +0 -0
  8. package/src/App.tsrx +77 -56
  9. package/src/client/DiffPane.tsrx +3 -7
  10. package/src/client/EjectModal.tsrx +7 -9
  11. package/src/client/GitHubSettingsModal.tsrx +8 -8
  12. package/src/client/ModelSettingsModal.tsrx +5 -5
  13. package/src/client/NewSessionForm.tsrx +1 -1
  14. package/src/client/PendingQuestionsCard.tsrx +1 -1
  15. package/src/client/SessionPicker.tsrx +49 -36
  16. package/src/client/SpecEditor.tsrx +1 -1
  17. package/src/client/locales/en.json +30 -16
  18. package/src/client/locales/pl.json +78 -28
  19. package/src/server/delivery/configuration.ts +16 -0
  20. package/src/server/delivery/git-pr.ts +51 -22
  21. package/src/server/delivery/types.ts +2 -0
  22. package/src/server/gates.ts +34 -8
  23. package/src/server/preview-modules.ts +43 -0
  24. package/src/server/preview-revision.ts +1 -0
  25. package/src/server/preview-runtime.ts +41 -4
  26. package/src/server/preview-seed.ts +115 -0
  27. package/src/server/recorded-adapters.ts +132 -0
  28. package/src/server/sample-data.ts +403 -0
  29. package/src/server/turns.ts +16 -1
  30. package/dist/client/assets/App-ClMOrpyg.js +0 -11
  31. package/dist/client/assets/PreviewHost-BHiHrfbQ.js +0 -1
  32. package/dist/client/assets/auto-buGEq6de.js +0 -3
  33. package/dist/client/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  34. package/dist/client/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  35. package/dist/client/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  36. package/dist/client/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  37. package/dist/client/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  38. package/dist/client/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  39. package/dist/client/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  40. package/dist/client/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  41. package/dist/client/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  42. package/dist/client/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  43. package/dist/client/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  44. package/dist/client/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  45. package/dist/client/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  46. package/dist/client/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  47. package/dist/client/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  48. package/dist/client/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  49. package/dist/client/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  50. package/dist/client/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  51. package/dist/client/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  52. package/dist/client/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  53. package/dist/client/assets/ibm-plex-sans-cyrillic-ext-wght-normal-d45eAU9y.woff2 +0 -0
  54. package/dist/client/assets/ibm-plex-sans-cyrillic-wght-normal-BAAhND-U.woff2 +0 -0
  55. package/dist/client/assets/ibm-plex-sans-greek-wght-normal-CmyJS8uq.woff2 +0 -0
  56. package/dist/client/assets/ibm-plex-sans-latin-ext-wght-normal-CIII54If.woff2 +0 -0
  57. package/dist/client/assets/ibm-plex-sans-latin-wght-normal-IvpUvPa2.woff2 +0 -0
  58. package/dist/client/assets/ibm-plex-sans-vietnamese-wght-normal-Dg1JeJN0.woff2 +0 -0
  59. package/dist/client/assets/index-CvwhlQ87.js +0 -4
  60. package/dist/client/assets/styles-BPWaGf55.js +0 -3
  61. package/dist/client/assets/styles-Dx1_KOag.css +0 -1
  62. package/dist/client/favicon.svg +0 -1
  63. package/dist/server/assets/auto-CVGNYyb3.js +0 -12785
  64. package/dist/server/assets/chunk-DDJLRBDX-3OUNo51g.js +0 -4125
  65. package/dist/server/assets/nodefs-DVSI7kG0.js +0 -24
  66. package/dist/server/assets/opfs-ahp-D56iokt1.js +0 -388
  67. package/dist/server/assets/token-Bvaq7Anz.js +0 -55
  68. package/dist/server/assets/token-util-BqSvdIPQ.js +0 -3
  69. package/dist/server/assets/token-util-CktFFT4z.js +0 -494
  70. package/dist/server/entry.js +0 -91671
  71. package/dist/server/favicon.svg +0 -1
  72. package/dist/server/index.html +0 -161
@@ -91,7 +91,8 @@
91
91
  "dashboard.costNote": "USD amounts reported by providers, not an invoice. Subscription fees are not included.",
92
92
  "dashboard.incomplete": "Some usage is not reported: {cost} turns without a price and {tokens} without token totals. ? means unknown; + means a partial total.",
93
93
  "dashboard.legacyRow": "Local history, owner unknown",
94
- "dashboard.legacyNote": "{count} older sessions have no recorded owner. They are visible only on this device and excluded from your totals.",
94
+ "dashboard.legacyNote.one": "{count} older session has no recorded owner. It is visible only on this device and excluded from your totals.",
95
+ "dashboard.legacyNote.other": "{count} older sessions have no recorded owner. They are visible only on this device and excluded from your totals.",
95
96
  "dashboard.sessions": "Work sessions",
96
97
  "dashboard.search": "Find an idea or module…",
97
98
  "dashboard.filters": "Filters",
@@ -103,7 +104,8 @@
103
104
  "dashboard.noMatchHint": "Change the search or filters.",
104
105
  "dashboard.open": "Open",
105
106
  "dashboard.details": "Summary",
106
- "dashboard.scopeNote": "Totals cover your recorded work in this workspace, including archived sessions and {count} deleted sessions with retained history. Filters change the list only. Sending a change for review does not mean it is merged or deployed.",
107
+ "dashboard.scopeNote.one": "Totals cover your recorded work in this workspace, including archived sessions and {count} deleted session with retained history. Filters change the list only. Sending a change for review does not mean it is merged or deployed.",
108
+ "dashboard.scopeNote.other": "Totals cover your recorded work in this workspace, including archived sessions and {count} deleted sessions with retained history. Filters change the list only. Sending a change for review does not mean it is merged or deployed.",
107
109
  "dashboard.newHint": "Describe who needs the feature and what it should help them do. You approve the plan before implementation.",
108
110
  "dashboard.detailHint": "Progress, AI usage and decisions for this idea.",
109
111
  "dashboard.moduleApprovals": "Currently approved module plans: {approved} of {total}.",
@@ -178,11 +180,13 @@
178
180
  "connect.token.label": "API token",
179
181
  "connect.token.help": "Needs sandbox.access.use, and sandbox.preview.data for live data.",
180
182
  "connect.token.placeholder": "clat_…",
181
- "connect.roles": "Workspace agents: {count} roles",
183
+ "connect.roles.one": "Workspace agents: {count} role",
184
+ "connect.roles.other": "Workspace agents: {count} roles",
182
185
  "connect.connecting": "Connecting…",
183
186
  "connect.action": "Connect",
184
187
  "session.back": "Sessions",
185
- "session.modules": "{count} modules",
188
+ "session.modules.one": "{count} module",
189
+ "session.modules.other": "{count} modules",
186
190
  "session.module.visible": "Module shown in the panes",
187
191
  "session.module.all": "All modules",
188
192
  "session.module.addLabel": "Module to add to this session",
@@ -213,7 +217,7 @@
213
217
  "sessions.start": "Start session",
214
218
  "sessions.empty.title": "No sessions yet",
215
219
  "sessions.empty.description": "Describe a module or change above to start the first session.",
216
- "sessions.archived.one": "1 archived session",
220
+ "sessions.archived.one": "{count} archived session",
217
221
  "sessions.archived.other": "{count} archived sessions",
218
222
  "sessions.archived.show": "Show archived",
219
223
  "sessions.archived.hide": "Hide archived",
@@ -226,7 +230,8 @@
226
230
  "sessions.time.now": "just now",
227
231
  "sessions.time.minutes": "{count}m ago",
228
232
  "sessions.time.hours": "{count}h ago",
229
- "sessions.time.days": "{count}d ago",
233
+ "sessions.time.days.one": "{count}d ago",
234
+ "sessions.time.days.other": "{count}d ago",
230
235
  "sessions.actions": "Session actions",
231
236
  "sessions.archive": "Archive",
232
237
  "sessions.delete.confirm": "Delete this session? {delivery}",
@@ -240,7 +245,8 @@
240
245
  "attachments.uploading": "Uploading…",
241
246
  "attachments.uploadFailed": "Could not upload the attachment.",
242
247
  "attachments.removeFailed": "Could not remove the attachment.",
243
- "attachments.limit": "You can attach up to {count} files.",
248
+ "attachments.limit.one": "You can attach up to {count} file.",
249
+ "attachments.limit.other": "You can attach up to {count} files.",
244
250
  "attachments.type": "{name} is not an accepted file type. Allowed: {types}.",
245
251
  "attachments.empty": "{name} is empty.",
246
252
  "attachments.tooLarge": "{name} is larger than {size} MB.",
@@ -250,7 +256,8 @@
250
256
  "chat.event.started": "Started {role} with {driver}",
251
257
  "chat.event.ok": "completed",
252
258
  "chat.event.failed": "failed",
253
- "chat.event.finished": "Finished ({count} tokens)",
259
+ "chat.event.finished.one": "Finished ({count} token)",
260
+ "chat.event.finished.other": "Finished ({count} tokens)",
254
261
  "chat.restore.confirm": "Restore this point? Current draft files will be replaced.",
255
262
  "chat.restore.restoring": "Restoring…",
256
263
  "chat.restore.start": "Restore session start",
@@ -276,7 +283,7 @@
276
283
  "chat.delivered": "Delivered",
277
284
  "diff.empty.title": "No changes yet",
278
285
  "diff.empty.hint": "Files the agent writes in this session appear here as a reviewable diff.",
279
- "diff.file.one": "1 file",
286
+ "diff.file.one": "{count} file",
280
287
  "diff.file.other": "{count} files",
281
288
  "diff.change.created": "Created",
282
289
  "diff.change.modified": "Modified",
@@ -388,13 +395,19 @@
388
395
  "preview.empty.title": "Preview unavailable",
389
396
  "preview.empty.description": "The draft module needs a client contribution before it can render here.",
390
397
  "preview.draftModule": "Draft module",
391
- "eject.moreModules": " and {count} more module(s)",
392
- "eject.lead.git": "This will deliver {count} changed files to {branch}, based on {base}, and push to {target}{others}.",
398
+ "eject.moreModules.one": " and {count} more module",
399
+ "eject.moreModules.other": " and {count} more modules",
400
+ "eject.lead.git.one": "This will deliver {count} changed file to {branch}, based on {base}, and push to {target}{others}.",
401
+ "eject.lead.git.other": "This will deliver {count} changed files to {branch}, based on {base}, and push to {target}{others}.",
393
402
  "eject.lead.multi": "This will deliver {files} changed files across {modules} modules.",
394
- "eject.detail.filesLand": "{count} files will land in {path}",
395
- "eject.detail.files": "{count} files",
396
- "eject.detail.overwritten": "{count} overwritten",
397
- "eject.detail.removed": "{count} removed",
403
+ "eject.detail.filesLand.one": "{count} file will land in {path}",
404
+ "eject.detail.filesLand.other": "{count} files will land in {path}",
405
+ "eject.detail.files.one": "{count} file",
406
+ "eject.detail.files.other": "{count} files",
407
+ "eject.detail.overwritten.one": "{count} overwritten",
408
+ "eject.detail.overwritten.other": "{count} overwritten",
409
+ "eject.detail.removed.one": "{count} removed",
410
+ "eject.detail.removed.other": "{count} removed",
398
411
  "eject.detail.packages": "new packages: {packages}",
399
412
  "eject.detail.enable": "enabled in the platform",
400
413
  "eject.explanation.git": "The sandbox will prepare a clean worktree, validate it, then push a session branch for review.",
@@ -431,7 +444,8 @@
431
444
  "eject.step.gate": "Run {gate}",
432
445
  "eject.step.fetch": "Fetch {base} and create {branch}",
433
446
  "eject.step.copy": "Copy the module files",
434
- "eject.step.remove": "Remove {count} deleted files",
447
+ "eject.step.remove.one": "Remove {count} deleted file",
448
+ "eject.step.remove.other": "Remove {count} deleted files",
435
449
  "eject.step.enable": "Enable {module} in the platform",
436
450
  "eject.step.grant": "Grant scopes to workspace owners",
437
451
  "eject.step.typecheck": "Typecheck the platform",
@@ -91,7 +91,10 @@
91
91
  "dashboard.costNote": "Kwoty w USD zgłoszone przez dostawców, nie faktura. Nie obejmują opłat za subskrypcje.",
92
92
  "dashboard.incomplete": "Tury bez podanej ceny: {cost}. Tury bez liczby tokenów: {tokens}. ? oznacza brak danych, a + oznacza niepełną sumę.",
93
93
  "dashboard.legacyRow": "Historia lokalna, właściciel nieznany",
94
- "dashboard.legacyNote": "Starsze sesje bez zapisanego właściciela: {count}. widoczne tylko lokalnie i nie wliczają się do Twoich podsumowań.",
94
+ "dashboard.legacyNote.one": "{count} starsza sesja nie ma zapisanego właściciela. Jest widoczna tylko lokalnie i nie wlicza się do Twoich podsumowań.",
95
+ "dashboard.legacyNote.few": "{count} starsze sesje nie mają zapisanego właściciela. Są widoczne tylko lokalnie i nie wliczają się do Twoich podsumowań.",
96
+ "dashboard.legacyNote.many": "{count} starszych sesji nie ma zapisanego właściciela. Są widoczne tylko lokalnie i nie wliczają się do Twoich podsumowań.",
97
+ "dashboard.legacyNote.other": "{count} starszej sesji nie ma zapisanego właściciela. Jest widoczna tylko lokalnie i nie wlicza się do Twoich podsumowań.",
95
98
  "dashboard.sessions": "Sesje pracy",
96
99
  "dashboard.search": "Znajdź pomysł lub moduł…",
97
100
  "dashboard.filters": "Filtry",
@@ -103,7 +106,10 @@
103
106
  "dashboard.noMatchHint": "Zmień wyszukiwanie lub filtry.",
104
107
  "dashboard.open": "Otwórz",
105
108
  "dashboard.details": "Podsumowanie",
106
- "dashboard.scopeNote": "Sumy obejmują Twoją zapisaną pracę w tej organizacji, także archiwum i usunięte sesje z zachowaną historią ({count}). Filtry zmieniają tylko listę. Wysłanie do przeglądu nie oznacza scalenia ani wdrożenia.",
109
+ "dashboard.scopeNote.one": "Sumy obejmują Twoją zapisaną pracę w tej organizacji, także archiwum i {count} usuniętą sesję z zachowaną historią. Filtry zmieniają tylko listę. Wysłanie do przeglądu nie oznacza scalenia ani wdrożenia.",
110
+ "dashboard.scopeNote.few": "Sumy obejmują Twoją zapisaną pracę w tej organizacji, także archiwum i {count} usunięte sesje z zachowaną historią. Filtry zmieniają tylko listę. Wysłanie do przeglądu nie oznacza scalenia ani wdrożenia.",
111
+ "dashboard.scopeNote.many": "Sumy obejmują Twoją zapisaną pracę w tej organizacji, także archiwum i {count} usuniętych sesji z zachowaną historią. Filtry zmieniają tylko listę. Wysłanie do przeglądu nie oznacza scalenia ani wdrożenia.",
112
+ "dashboard.scopeNote.other": "Sumy obejmują Twoją zapisaną pracę w tej organizacji, także archiwum i {count} usuniętej sesji z zachowaną historią. Filtry zmieniają tylko listę. Wysłanie do przeglądu nie oznacza scalenia ani wdrożenia.",
107
113
  "dashboard.newHint": "Napisz, kto potrzebuje tej funkcji i co ma dzięki niej zrobić. Przed realizacją zatwierdzisz plan.",
108
114
  "dashboard.detailHint": "Postęp, zużycie AI i decyzje dotyczące tego pomysłu.",
109
115
  "dashboard.moduleApprovals": "Aktualnie zatwierdzone plany modułów: {approved} z {total}.",
@@ -149,7 +155,7 @@
149
155
  "app.gates.none": "Nie uruchomiono żadnej bramki.",
150
156
  "app.github.saved": "Zapisano integrację z GitHub.",
151
157
  "app.seo.title": "Sandbox",
152
- "app.seo.description": "Lokalny obszar roboczy do bezpiecznego planowania, tworzenia, przeglądania i dostarczania modułów Flowdular.",
158
+ "app.seo.description": "Lokalna przestrzeń robocza do bezpiecznego planowania, tworzenia, przeglądania i dostarczania modułów Flowdular.",
153
159
  "app.seo.imageAlt": "Sandbox Flowdular",
154
160
  "app.askAbout": "O pliku {path}: ",
155
161
  "app.error.openSession": "Nie można otworzyć sesji.",
@@ -178,11 +184,17 @@
178
184
  "connect.token.label": "Token API",
179
185
  "connect.token.help": "Wymaga sandbox.access.use oraz sandbox.preview.data dla danych na żywo.",
180
186
  "connect.token.placeholder": "clat_…",
181
- "connect.roles": "Agenci obszaru roboczego: {count} ról",
187
+ "connect.roles.one": "Agenci przestrzeni roboczej: {count} rola",
188
+ "connect.roles.few": "Agenci przestrzeni roboczej: {count} role",
189
+ "connect.roles.many": "Agenci przestrzeni roboczej: {count} ról",
190
+ "connect.roles.other": "Agenci przestrzeni roboczej: {count} roli",
182
191
  "connect.connecting": "Łączenie…",
183
192
  "connect.action": "Połącz",
184
193
  "session.back": "Sesje",
185
- "session.modules": "{count} modułów",
194
+ "session.modules.one": "{count} moduł",
195
+ "session.modules.few": "{count} moduły",
196
+ "session.modules.many": "{count} modułów",
197
+ "session.modules.other": "{count} modułu",
186
198
  "session.module.visible": "Moduł widoczny w panelach",
187
199
  "session.module.all": "Wszystkie moduły",
188
200
  "session.module.addLabel": "Moduł do dodania do tej sesji",
@@ -213,8 +225,10 @@
213
225
  "sessions.start": "Uruchom sesję",
214
226
  "sessions.empty.title": "Nie ma jeszcze sesji",
215
227
  "sessions.empty.description": "Opisz powyżej moduł lub zmianę, aby uruchomić pierwszą sesję.",
216
- "sessions.archived.one": "1 zarchiwizowana sesja",
217
- "sessions.archived.other": "{count} zarchiwizowanych sesji",
228
+ "sessions.archived.one": "{count} zarchiwizowana sesja",
229
+ "sessions.archived.few": "{count} zarchiwizowane sesje",
230
+ "sessions.archived.many": "{count} zarchiwizowanych sesji",
231
+ "sessions.archived.other": "{count} zarchiwizowanej sesji",
218
232
  "sessions.archived.show": "Pokaż zarchiwizowane",
219
233
  "sessions.archived.hide": "Ukryj zarchiwizowane",
220
234
  "sessions.kind.new": "Nowy moduł",
@@ -226,7 +240,10 @@
226
240
  "sessions.time.now": "przed chwilą",
227
241
  "sessions.time.minutes": "{count} min temu",
228
242
  "sessions.time.hours": "{count} godz. temu",
229
- "sessions.time.days": "{count} dni temu",
243
+ "sessions.time.days.one": "{count} dzień temu",
244
+ "sessions.time.days.few": "{count} dni temu",
245
+ "sessions.time.days.many": "{count} dni temu",
246
+ "sessions.time.days.other": "{count} dnia temu",
230
247
  "sessions.actions": "Działania sesji",
231
248
  "sessions.archive": "Archiwizuj",
232
249
  "sessions.delete.confirm": "Usunąć tę sesję? {delivery}",
@@ -240,7 +257,10 @@
240
257
  "attachments.uploading": "Przesyłanie…",
241
258
  "attachments.uploadFailed": "Nie udało się przesłać załącznika.",
242
259
  "attachments.removeFailed": "Nie udało się usunąć załącznika.",
243
- "attachments.limit": "Możesz dołączyć maksymalnie {count} plików.",
260
+ "attachments.limit.one": "Możesz dołączyć maksymalnie {count} plik.",
261
+ "attachments.limit.few": "Możesz dołączyć maksymalnie {count} pliki.",
262
+ "attachments.limit.many": "Możesz dołączyć maksymalnie {count} plików.",
263
+ "attachments.limit.other": "Możesz dołączyć maksymalnie {count} pliku.",
244
264
  "attachments.type": "{name} nie jest obsługiwanym typem pliku. Dozwolone: {types}.",
245
265
  "attachments.empty": "Plik {name} jest pusty.",
246
266
  "attachments.tooLarge": "Plik {name} jest większy niż {size} MB.",
@@ -250,7 +270,10 @@
250
270
  "chat.event.started": "Uruchomiono rolę {role} z agentem {driver}",
251
271
  "chat.event.ok": "ukończono",
252
272
  "chat.event.failed": "niepowodzenie",
253
- "chat.event.finished": "Ukończono ({count} tokenów)",
273
+ "chat.event.finished.one": "Ukończono ({count} token)",
274
+ "chat.event.finished.few": "Ukończono ({count} tokeny)",
275
+ "chat.event.finished.many": "Ukończono ({count} tokenów)",
276
+ "chat.event.finished.other": "Ukończono ({count} tokena)",
254
277
  "chat.restore.confirm": "Przywrócić ten punkt? Bieżące pliki robocze zostaną zastąpione.",
255
278
  "chat.restore.restoring": "Przywracanie…",
256
279
  "chat.restore.start": "Przywróć początek sesji",
@@ -276,8 +299,10 @@
276
299
  "chat.delivered": "Dostarczono",
277
300
  "diff.empty.title": "Nie ma jeszcze zmian",
278
301
  "diff.empty.hint": "Pliki zapisane przez agenta w tej sesji pojawią się tutaj jako diff do przeglądu.",
279
- "diff.file.one": "1 plik",
280
- "diff.file.other": "{count} plików",
302
+ "diff.file.one": "{count} plik",
303
+ "diff.file.few": "{count} pliki",
304
+ "diff.file.many": "{count} plików",
305
+ "diff.file.other": "{count} pliku",
281
306
  "diff.change.created": "Utworzono",
282
307
  "diff.change.modified": "Zmieniono",
283
308
  "diff.change.deleted": "Usunięto",
@@ -293,7 +318,7 @@
293
318
  "workspace.github.title": "Integracja z GitHub",
294
319
  "workspace.github.enabled": "Dostarczanie przez pull request jest włączone.",
295
320
  "workspace.github.configure": "Skonfiguruj dostarczanie przez pull request.",
296
- "workspace.change": "Zmień obszar roboczy lub token",
321
+ "workspace.change": "Zmień przestrzeń roboczą lub token",
297
322
  "workspace.disconnect": "Odłącz ten sandbox",
298
323
  "workspace.language": "Język",
299
324
  "workspace.language.en": "English",
@@ -380,7 +405,7 @@
380
405
  "preview.sessionUnavailable": "Sesja podglądu jest niedostępna.",
381
406
  "preview.identityFailed": "Nie można utworzyć tożsamości podglądu.",
382
407
  "preview.identityName": "Podgląd sandboxa",
383
- "preview.workspace": "Obszar roboczy podglądu",
408
+ "preview.workspace": "Przestrzeń robocza podglądu",
384
409
  "preview.missingExport": " nie eksportuje createClientContribution.",
385
410
  "preview.noClient": "Ten moduł roboczy nie ma wkładu klienta.",
386
411
  "preview.loadFailed": "Nie można wczytać podglądu modułu roboczego.",
@@ -388,25 +413,47 @@
388
413
  "preview.empty.title": "Podgląd niedostępny",
389
414
  "preview.empty.description": "Moduł roboczy potrzebuje wkładu klienta, aby można go było tu wyświetlić.",
390
415
  "preview.draftModule": "Moduł roboczy",
391
- "eject.moreModules": " oraz {count} kolejnych modułów",
392
- "eject.lead.git": "Zostanie dostarczonych {count} zmienionych plików do gałęzi {branch}, opartej na {base}, a następnie wysłanych do {target}{others}.",
416
+ "eject.moreModules.one": " oraz {count} kolejny moduł",
417
+ "eject.moreModules.few": " oraz {count} kolejne moduły",
418
+ "eject.moreModules.many": " oraz {count} kolejnych modułów",
419
+ "eject.moreModules.other": " oraz {count} kolejnego modułu",
420
+ "eject.lead.git.one": "Zostanie dostarczony {count} zmieniony plik do gałęzi {branch}, opartej na {base}, a następnie wysłany do {target}{others}.",
421
+ "eject.lead.git.few": "Zostaną dostarczone {count} zmienione pliki do gałęzi {branch}, opartej na {base}, a następnie wysłane do {target}{others}.",
422
+ "eject.lead.git.many": "Zostanie dostarczonych {count} zmienionych plików do gałęzi {branch}, opartej na {base}, a następnie wysłanych do {target}{others}.",
423
+ "eject.lead.git.other": "Zostanie dostarczone {count} zmienionego pliku do gałęzi {branch}, opartej na {base}, a następnie wysłane do {target}{others}.",
393
424
  "eject.lead.multi": "Zostanie dostarczonych {files} zmienionych plików w {modules} modułach.",
394
- "eject.detail.filesLand": "{count} plików trafi do {path}",
395
- "eject.detail.files": "{count} plików",
396
- "eject.detail.overwritten": "{count} nadpisanych",
397
- "eject.detail.removed": "{count} usuniętych",
425
+ "eject.detail.filesLand.one": "{count} plik trafi do {path}",
426
+ "eject.detail.filesLand.few": "{count} pliki trafią do {path}",
427
+ "eject.detail.filesLand.many": "{count} plików trafi do {path}",
428
+ "eject.detail.filesLand.other": "{count} pliku trafi do {path}",
429
+ "eject.detail.files.one": "{count} plik",
430
+ "eject.detail.files.few": "{count} pliki",
431
+ "eject.detail.files.many": "{count} plików",
432
+ "eject.detail.files.other": "{count} pliku",
433
+ "eject.detail.overwritten.one": "{count} nadpisany",
434
+ "eject.detail.overwritten.few": "{count} nadpisane",
435
+ "eject.detail.overwritten.many": "{count} nadpisanych",
436
+ "eject.detail.overwritten.other": "{count} nadpisanego",
437
+ "eject.detail.removed.one": "{count} usunięty",
438
+ "eject.detail.removed.few": "{count} usunięte",
439
+ "eject.detail.removed.many": "{count} usuniętych",
440
+ "eject.detail.removed.other": "{count} usuniętego",
398
441
  "eject.detail.packages": "nowe pakiety: {packages}",
399
442
  "eject.detail.enable": "włączony na platformie",
400
443
  "eject.explanation.git": "Sandbox przygotuje czysty worktree, zweryfikuje go, a następnie wyśle gałąź sesji do przeglądu.",
401
444
  "eject.explanation.enable": "Sandbox skopiuje moduły, włączy je, przyzna właścicielom zakresy i sprawdzi typy platformy.",
402
445
  "eject.explanation.copy": "Sandbox skopiuje moduły, przyzna właścicielom zakresy i sprawdzi typy platformy.",
403
446
  "eject.deliveredVerb.one": "dostarczono",
447
+ "eject.deliveredVerb.few": "dostarczono",
448
+ "eject.deliveredVerb.many": "dostarczono",
404
449
  "eject.deliveredVerb.other": "dostarczono",
405
450
  "eject.delivered": "Dostarczono",
406
451
  "eject.title.one": "Dostarcz {module}",
407
- "eject.title.other": "Dostarcz {count} modułów",
452
+ "eject.title.few": "Dostarcz {count} moduły",
453
+ "eject.title.many": "Dostarcz {count} modułów",
454
+ "eject.title.other": "Dostarcz {count} modułu",
408
455
  "eject.target.label": "Cel dostarczenia",
409
- "eject.target.workspace": "Obszar roboczy",
456
+ "eject.target.workspace": "Przestrzeń robocza",
410
457
  "eject.target.gitPr": "Pull request GitHub",
411
458
  "eject.target.unavailable": "Cel {target} jest niedostępny: {reason}",
412
459
  "eject.unknownReason": "Nie podano powodu.",
@@ -420,7 +467,7 @@
420
467
  "eject.git.changedFilesDetail": "{changed} z {allowed} dozwolonych",
421
468
  "eject.git.newPackages": "Nowe pakiety",
422
469
  "eject.git.packagesDetail": "{packages} ({allowed} dozwolonych)",
423
- "eject.git.owners": "Właściciele obszaru roboczego",
470
+ "eject.git.owners": "Właściciele przestrzeni roboczej",
424
471
  "eject.git.noOwners": "Nie znaleziono właścicieli",
425
472
  "eject.git.reviewers": "Recenzenci",
426
473
  "eject.git.pullRequest": "Pull request",
@@ -431,16 +478,19 @@
431
478
  "eject.step.gate": "Uruchom {gate}",
432
479
  "eject.step.fetch": "Pobierz {base} i utwórz {branch}",
433
480
  "eject.step.copy": "Skopiuj pliki modułu",
434
- "eject.step.remove": "Usuń {count} usuniętych plików",
481
+ "eject.step.remove.one": "Usuń {count} usunięty plik",
482
+ "eject.step.remove.few": "Usuń {count} usunięte pliki",
483
+ "eject.step.remove.many": "Usuń {count} usuniętych plików",
484
+ "eject.step.remove.other": "Usuń {count} usuniętego pliku",
435
485
  "eject.step.enable": "Włącz {module} na platformie",
436
- "eject.step.grant": "Przyznaj zakresy właścicielom obszaru roboczego",
486
+ "eject.step.grant": "Przyznaj zakresy właścicielom przestrzeni roboczej",
437
487
  "eject.step.typecheck": "Sprawdź typy platformy",
438
488
  "eject.step.guardrails": "Sprawdź kontrolki dostarczenia",
439
489
  "eject.step.push": "Wyślij {branch} do {remote}",
440
490
  "eject.step.openPr": "Otwórz pull request",
441
491
  "eject.step.compare": "Pokaż link porównania",
442
492
  "eject.step.restart": "Uruchom ponownie lokalną aplikację",
443
- "eject.step.deploy": "Wdróż obszar roboczy",
493
+ "eject.step.deploy": "Wdróż przestrzeń roboczą",
444
494
  "eject.packagesWarning": "Zostaną podłączone nowe pakiety: {packages}",
445
495
  "eject.previous": "Tę sesję dostarczono {date}{branch}.",
446
496
  "eject.previousBranch": " z gałęzi {branch}",
@@ -451,12 +501,12 @@
451
501
  "eject.done.summary": "Dostarczono {files} plików. {removed} {enabled}",
452
502
  "eject.done.removed": "Usunięto: {count}.",
453
503
  "eject.done.enabled": "Moduł jest włączony.",
454
- "eject.thisWorkspace": "tego obszaru roboczego",
504
+ "eject.thisWorkspace": "tej przestrzeni roboczej",
455
505
  "eject.openPr": "Otwórz pull request",
456
506
  "eject.openCompare": "Otwórz porównanie",
457
507
  "eject.branchPushed": "Wysłano gałąź sesji.",
458
508
  "eject.restartNote": "Uruchom ponownie aplikację, aby wczytać nową kompozycję.",
459
- "eject.deployNote": "Wdróż obszar roboczy, aby zmiana dotarła do połączonej aplikacji.",
509
+ "eject.deployNote": "Wdróż przestrzeń roboczą, aby zmiana dotarła do połączonej aplikacji.",
460
510
  "eject.confirm": "Dostarcz",
461
511
  "eject.confirmPr": "Utwórz pull request",
462
512
  "eject.confirmBranch": "Wyślij gałąź",
@@ -14,6 +14,8 @@ export interface GitDeliveryConfiguration {
14
14
  readonly mode: GitPushMode;
15
15
  readonly forkOwner: string | null;
16
16
  readonly reviewers: readonly string[];
17
+ /** Added to the pull request after creation; a missing label never fails a delivery. */
18
+ readonly labels: readonly string[];
17
19
  }
18
20
 
19
21
  /* The sandbox.delivery block of flowdular.json. Every field is optional there;
@@ -38,6 +40,7 @@ export const DEFAULT_DELIVERY_CONFIGURATION: DeliveryConfiguration = {
38
40
  mode: 'auto',
39
41
  forkOwner: null,
40
42
  reviewers: [],
43
+ labels: ['sandbox-delivery'],
41
44
  },
42
45
  maxChangedFiles: null,
43
46
  };
@@ -52,6 +55,8 @@ const EJECT_TARGETS: readonly EjectTarget[] = [
52
55
  const GIT_NAME = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
53
56
  const MAX_GIT_NAME_LENGTH = 120;
54
57
  const MAX_GITHUB_REVIEWERS = 20;
58
+ const MAX_LABELS = 10;
59
+ const GITHUB_LABEL = /^[A-Za-z0-9][A-Za-z0-9 ._:-]{0,49}$/;
55
60
  const GITHUB_REPOSITORY =
56
61
  /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9._-]{1,100}$/;
57
62
  const GITHUB_ACCOUNT = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
@@ -141,6 +146,16 @@ export function resolveDeliveryConfiguration(
141
146
  ) {
142
147
  throw invalid('git.reviewers must list account names.');
143
148
  }
149
+ const labels = git.labels ?? defaults.git.labels;
150
+ if (
151
+ !Array.isArray(labels) ||
152
+ labels.length > MAX_LABELS ||
153
+ !labels.every(
154
+ (label) => typeof label === 'string' && GITHUB_LABEL.test(label),
155
+ )
156
+ ) {
157
+ throw invalid('git.labels must list GitHub label names.');
158
+ }
144
159
  const maxChangedFiles = block.maxChangedFiles ?? null;
145
160
  if (
146
161
  maxChangedFiles !== null &&
@@ -168,6 +183,7 @@ export function resolveDeliveryConfiguration(
168
183
  mode,
169
184
  forkOwner: forkOwner as string | null,
170
185
  reviewers: reviewers as readonly string[],
186
+ labels: [...new Set(labels as readonly string[])],
171
187
  },
172
188
  maxChangedFiles: maxChangedFiles as number | null,
173
189
  };
@@ -657,11 +657,13 @@ function pullRequestBody(input: {
657
657
  .filter((part) => part !== '')
658
658
  .join(' ');
659
659
  const lines = [
660
- summary,
660
+ '## Problem',
661
+ '',
662
+ sentences(input.session.brief, 3, 600) || 'See the session brief.',
661
663
  '',
662
- `Session ${input.session.id}.`,
664
+ '## Solution',
663
665
  '',
664
- '## Specification',
666
+ summary,
665
667
  '',
666
668
  ...input.specs.flatMap((spec) => {
667
669
  const before = spec.base?.specVersion ?? 'new';
@@ -681,7 +683,7 @@ function pullRequestBody(input: {
681
683
  '',
682
684
  ];
683
685
  }),
684
- '## Gates',
686
+ '## Verification',
685
687
  '',
686
688
  '| Gate | Module | Result |',
687
689
  '| --- | --- | --- |',
@@ -689,7 +691,19 @@ function pullRequestBody(input: {
689
691
  (gate) => `| ${gate.id} | ${gate.module ?? ''} | ${gate.status} |`,
690
692
  ),
691
693
  '',
692
- `Verification: ${testGates.length} module test gate(s) passed. The platform typecheck passed in the delivery worktree.`,
694
+ `${testGates.length} module test gate(s) passed. The platform typecheck passed in the delivery worktree. Not run here: the production build and a browser look.`,
695
+ '',
696
+ '## Follow-ups',
697
+ '',
698
+ ...input.modules.map(
699
+ (module) =>
700
+ `- Post-merge: \`pnpm flowdular auth sync-scopes --module ${module.id} --apply\` against the deployment database.`,
701
+ ),
702
+ ...(input.requireReviewer && input.owners.length > 1
703
+ ? [
704
+ `- Cross-owner change (${input.owners.join(', ')}): .ai/policies/path-ownership.yaml asks for a reviewer from each owner.`,
705
+ ]
706
+ : []),
693
707
  '',
694
708
  ];
695
709
  const groups: readonly [string, readonly string[]][] = [
@@ -697,13 +711,6 @@ function pullRequestBody(input: {
697
711
  ['Modified', input.changes.modified],
698
712
  ['Removed', input.changes.removed],
699
713
  ];
700
- lines.push('## Files', '');
701
- for (const [label, files] of groups) {
702
- if (files.length === 0) continue;
703
- lines.push(`${label} (${files.length}):`);
704
- lines.push(...files.map((file) => `- ${file}`));
705
- lines.push('');
706
- }
707
714
  lines.push('## Risks', '');
708
715
  const migrations = [...input.changes.added, ...input.changes.modified].filter(
709
716
  (file) => /\/migrations\//.test(file),
@@ -728,17 +735,14 @@ function pullRequestBody(input: {
728
735
  ]),
729
736
  '',
730
737
  );
731
- for (const module of input.modules) {
732
- lines.push(
733
- `Post-merge: \`pnpm flowdular auth sync-scopes --module ${module.id} --apply\` against the deployment database.`,
734
- );
735
- }
736
- if (input.requireReviewer && input.owners.length > 1) {
737
- lines.push(
738
- '',
739
- `Cross-owner change (${input.owners.join(', ')}): .ai/policies/path-ownership.yaml asks for a reviewer from each owner.`,
740
- );
738
+ lines.push('## Files', '');
739
+ for (const [label, files] of groups) {
740
+ if (files.length === 0) continue;
741
+ lines.push(`${label} (${files.length}):`);
742
+ lines.push(...files.map((file) => `- ${file}`));
743
+ lines.push('');
741
744
  }
745
+ lines.push(`Session ${input.session.id}.`);
742
746
  return `${lines.join('\n')}\n`;
743
747
  }
744
748
 
@@ -910,6 +914,30 @@ async function openPullRequest(
910
914
  output: created.output,
911
915
  detail: url ?? '',
912
916
  });
917
+ if (url !== null && git.labels.length > 0) {
918
+ /* Labels are a courtesy of the repository, not a gate: a label the
919
+ repository lacks leaves the pull request open and unlabelled. */
920
+ const labelled = await run(
921
+ 'gh',
922
+ [
923
+ 'pr',
924
+ 'edit',
925
+ url,
926
+ ...repositoryArgs,
927
+ '--add-label',
928
+ git.labels.join(','),
929
+ ],
930
+ worktree,
931
+ );
932
+ recorder.record('labels', {
933
+ ok: labelled.code === 0,
934
+ output:
935
+ labelled.code === 0
936
+ ? `Labelled ${git.labels.join(', ')}.`
937
+ : `Labels ${git.labels.join(', ')} were not added: ${labelled.output.trim()}`,
938
+ detail: git.labels.join(','),
939
+ });
940
+ }
913
941
  return url;
914
942
  }
915
943
 
@@ -1093,6 +1121,7 @@ export function createGitPullRequestDeliveryTarget(): DeliveryTarget {
1093
1121
  owners,
1094
1122
  requireReviewer: ownership.requireReviewer,
1095
1123
  reviewers: config.git.reviewers,
1124
+ labels: config.git.labels,
1096
1125
  guardrails: evaluateGuardrails({
1097
1126
  offending: [],
1098
1127
  changedFiles,
@@ -68,6 +68,8 @@ export interface GitDeliveryPlan {
68
68
  readonly owners: readonly string[];
69
69
  readonly requireReviewer: boolean;
70
70
  readonly reviewers: readonly string[];
71
+ /** Added after the pull request is created; a missing label is reported, never fatal. */
72
+ readonly labels: readonly string[];
71
73
  readonly guardrails: DeliveryGuardrails;
72
74
  readonly provider: DeliveryProvider;
73
75
  readonly providerNote: string;
@@ -3,6 +3,10 @@ import { access } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { inspectAutoReview } from './auto-review.ts';
5
5
  import { checkDeclaredDependencies } from './dependencies.ts';
6
+ import {
7
+ liveAdapterRefusal,
8
+ readSessionAdapters,
9
+ } from './recorded-adapters.ts';
6
10
  import {
7
11
  modulePathOf,
8
12
  type SandboxSession,
@@ -45,6 +49,8 @@ interface GateDefinition {
45
49
  readonly passed: boolean;
46
50
  readonly output: string;
47
51
  }>;
52
+ /* A sandbox rule checked beside the command; a refusal fails the gate. */
53
+ refuse?(context: GateContext): Promise<string | null>;
48
54
  }
49
55
 
50
56
  interface GateContext {
@@ -92,6 +98,17 @@ const GATE_DEFINITIONS: readonly GateDefinition[] = [
92
98
  ],
93
99
  cwd: context.workspaceRoot,
94
100
  }),
101
+ refuse: async (context) =>
102
+ liveAdapterRefusal(
103
+ (
104
+ await readSessionAdapters(
105
+ context.session.modules.map((module) => ({
106
+ directory: module.directory,
107
+ path: modulePathOf(context.paths, module.directory),
108
+ })),
109
+ )
110
+ ).live,
111
+ ),
95
112
  },
96
113
  {
97
114
  id: 'module-schema',
@@ -372,15 +389,24 @@ export async function runGates(
372
389
  /* A stopped turn runs no further gates. The one already spawned is
373
390
  killed through the same signal. */
374
391
  if (input.signal?.aborted) return results;
392
+ const context: GateContext = {
393
+ workspaceRoot: input.workspaceRoot,
394
+ paths: input.paths,
395
+ session: input.session,
396
+ module,
397
+ modulePath: modulePathOf(input.paths, module.directory),
398
+ signal: input.signal,
399
+ };
400
+ const result = await runGate(definition, context);
401
+ const refusal = await definition.refuse?.(context);
375
402
  results.push(
376
- await runGate(definition, {
377
- workspaceRoot: input.workspaceRoot,
378
- paths: input.paths,
379
- session: input.session,
380
- module,
381
- modulePath: modulePathOf(input.paths, module.directory),
382
- signal: input.signal,
383
- }),
403
+ refusal
404
+ ? {
405
+ ...result,
406
+ status: 'failed',
407
+ output: [refusal, result.output].filter(Boolean).join('\n\n'),
408
+ }
409
+ : result,
384
410
  );
385
411
  }
386
412
  }