@12ui/design 0.2.13 → 0.2.15

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 (188) hide show
  1. package/README.md +35 -9
  2. package/dist/bounded-concurrency.d.ts +13 -0
  3. package/dist/bounded-concurrency.d.ts.map +1 -0
  4. package/dist/bounded-concurrency.js +43 -0
  5. package/dist/bounded-concurrency.js.map +1 -0
  6. package/dist/branch-client.d.ts +155 -0
  7. package/dist/branch-client.d.ts.map +1 -0
  8. package/dist/branch-client.js +179 -0
  9. package/dist/branch-client.js.map +1 -0
  10. package/dist/branch-completion.d.ts +37 -0
  11. package/dist/branch-completion.d.ts.map +1 -0
  12. package/dist/branch-completion.js +137 -0
  13. package/dist/branch-completion.js.map +1 -0
  14. package/dist/branch-dispatch-loop.d.ts +84 -0
  15. package/dist/branch-dispatch-loop.d.ts.map +1 -0
  16. package/dist/branch-dispatch-loop.js +206 -0
  17. package/dist/branch-dispatch-loop.js.map +1 -0
  18. package/dist/branch-downloads.d.ts +53 -0
  19. package/dist/branch-downloads.d.ts.map +1 -0
  20. package/dist/branch-downloads.js +114 -0
  21. package/dist/branch-downloads.js.map +1 -0
  22. package/dist/branch-execution-types.d.ts +54 -0
  23. package/dist/branch-execution-types.d.ts.map +1 -0
  24. package/dist/branch-execution-types.js +2 -0
  25. package/dist/branch-execution-types.js.map +1 -0
  26. package/dist/branch-execution.d.ts +20 -0
  27. package/dist/branch-execution.d.ts.map +1 -0
  28. package/dist/branch-execution.js +380 -0
  29. package/dist/branch-execution.js.map +1 -0
  30. package/dist/branch-identity.d.ts +71 -0
  31. package/dist/branch-identity.d.ts.map +1 -0
  32. package/dist/branch-identity.js +72 -0
  33. package/dist/branch-identity.js.map +1 -0
  34. package/dist/branch-materialize.d.ts +33 -0
  35. package/dist/branch-materialize.d.ts.map +1 -0
  36. package/dist/branch-materialize.js +64 -0
  37. package/dist/branch-materialize.js.map +1 -0
  38. package/dist/branch-page-conversion.d.ts +127 -0
  39. package/dist/branch-page-conversion.d.ts.map +1 -0
  40. package/dist/branch-page-conversion.js +382 -0
  41. package/dist/branch-page-conversion.js.map +1 -0
  42. package/dist/branch-plan.d.ts +63 -0
  43. package/dist/branch-plan.d.ts.map +1 -0
  44. package/dist/branch-plan.js +95 -0
  45. package/dist/branch-plan.js.map +1 -0
  46. package/dist/branch-progress.d.ts +99 -0
  47. package/dist/branch-progress.d.ts.map +1 -0
  48. package/dist/branch-progress.js +188 -0
  49. package/dist/branch-progress.js.map +1 -0
  50. package/dist/branch-run-record.d.ts +164 -0
  51. package/dist/branch-run-record.d.ts.map +1 -0
  52. package/dist/branch-run-record.js +94 -0
  53. package/dist/branch-run-record.js.map +1 -0
  54. package/dist/branch-schedule.d.ts +74 -0
  55. package/dist/branch-schedule.d.ts.map +1 -0
  56. package/dist/branch-schedule.js +130 -0
  57. package/dist/branch-schedule.js.map +1 -0
  58. package/dist/branch-status.d.ts +11 -0
  59. package/dist/branch-status.d.ts.map +1 -0
  60. package/dist/branch-status.js +118 -0
  61. package/dist/branch-status.js.map +1 -0
  62. package/dist/branch-viewport-normalization.d.ts +74 -0
  63. package/dist/branch-viewport-normalization.d.ts.map +1 -0
  64. package/dist/branch-viewport-normalization.js +120 -0
  65. package/dist/branch-viewport-normalization.js.map +1 -0
  66. package/dist/cli-arguments.d.ts.map +1 -1
  67. package/dist/cli-arguments.js +5 -0
  68. package/dist/cli-arguments.js.map +1 -1
  69. package/dist/cli-branch-command.d.ts +55 -0
  70. package/dist/cli-branch-command.d.ts.map +1 -0
  71. package/dist/cli-branch-command.js +475 -0
  72. package/dist/cli-branch-command.js.map +1 -0
  73. package/dist/cli-capabilities.d.ts +51 -0
  74. package/dist/cli-capabilities.d.ts.map +1 -1
  75. package/dist/cli-capabilities.js +52 -1
  76. package/dist/cli-capabilities.js.map +1 -1
  77. package/dist/cli-create-command.d.ts +21 -0
  78. package/dist/cli-create-command.d.ts.map +1 -0
  79. package/dist/cli-create-command.js +319 -0
  80. package/dist/cli-create-command.js.map +1 -0
  81. package/dist/cli-detach.d.ts +62 -0
  82. package/dist/cli-detach.d.ts.map +1 -0
  83. package/dist/cli-detach.js +123 -0
  84. package/dist/cli-detach.js.map +1 -0
  85. package/dist/cli-draft-command.d.ts +63 -0
  86. package/dist/cli-draft-command.d.ts.map +1 -0
  87. package/dist/cli-draft-command.js +220 -0
  88. package/dist/cli-draft-command.js.map +1 -0
  89. package/dist/cli-export-command.d.ts.map +1 -1
  90. package/dist/cli-export-command.js +2 -3
  91. package/dist/cli-export-command.js.map +1 -1
  92. package/dist/cli-image-batch-command.d.ts +21 -0
  93. package/dist/cli-image-batch-command.d.ts.map +1 -1
  94. package/dist/cli-image-batch-command.js +65 -4
  95. package/dist/cli-image-batch-command.js.map +1 -1
  96. package/dist/cli-image-batch-resume.d.ts +7 -0
  97. package/dist/cli-image-batch-resume.d.ts.map +1 -1
  98. package/dist/cli-image-batch-resume.js +19 -2
  99. package/dist/cli-image-batch-resume.js.map +1 -1
  100. package/dist/cli-package-command.d.ts +29 -0
  101. package/dist/cli-package-command.d.ts.map +1 -0
  102. package/dist/cli-package-command.js +128 -0
  103. package/dist/cli-package-command.js.map +1 -0
  104. package/dist/cli-progress.d.ts +137 -0
  105. package/dist/cli-progress.d.ts.map +1 -1
  106. package/dist/cli-progress.js +156 -0
  107. package/dist/cli-progress.js.map +1 -1
  108. package/dist/cli-select-command.d.ts +12 -0
  109. package/dist/cli-select-command.d.ts.map +1 -0
  110. package/dist/cli-select-command.js +66 -0
  111. package/dist/cli-select-command.js.map +1 -0
  112. package/dist/cli.js +150 -7
  113. package/dist/cli.js.map +1 -1
  114. package/dist/conversion-receipt.d.ts +127 -0
  115. package/dist/conversion-receipt.d.ts.map +1 -0
  116. package/dist/conversion-receipt.js +184 -0
  117. package/dist/conversion-receipt.js.map +1 -0
  118. package/dist/create-identity.d.ts +25 -0
  119. package/dist/create-identity.d.ts.map +1 -0
  120. package/dist/create-identity.js +0 -0
  121. package/dist/create-identity.js.map +1 -0
  122. package/dist/draft-identity.d.ts +30 -0
  123. package/dist/draft-identity.d.ts.map +1 -0
  124. package/dist/draft-identity.js +0 -0
  125. package/dist/draft-identity.js.map +1 -0
  126. package/dist/draft-prompts.d.ts +35 -0
  127. package/dist/draft-prompts.d.ts.map +1 -0
  128. package/dist/draft-prompts.js +51 -0
  129. package/dist/draft-prompts.js.map +1 -0
  130. package/dist/draft-run.d.ts +32 -0
  131. package/dist/draft-run.d.ts.map +1 -0
  132. package/dist/draft-run.js +152 -0
  133. package/dist/draft-run.js.map +1 -0
  134. package/dist/draft-workspace.d.ts +90 -0
  135. package/dist/draft-workspace.d.ts.map +1 -0
  136. package/dist/draft-workspace.js +64 -0
  137. package/dist/draft-workspace.js.map +1 -0
  138. package/dist/image-batch-status.d.ts +42 -0
  139. package/dist/image-batch-status.d.ts.map +1 -0
  140. package/dist/image-batch-status.js +142 -0
  141. package/dist/image-batch-status.js.map +1 -0
  142. package/dist/image-dimensions.d.ts +12 -0
  143. package/dist/image-dimensions.d.ts.map +1 -0
  144. package/dist/image-dimensions.js +14 -0
  145. package/dist/image-dimensions.js.map +1 -0
  146. package/dist/image-generation-resume.d.ts +13 -0
  147. package/dist/image-generation-resume.d.ts.map +1 -1
  148. package/dist/image-generation-resume.js +13 -0
  149. package/dist/image-generation-resume.js.map +1 -1
  150. package/dist/index.d.ts +6 -1
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/index.js +5 -1
  153. package/dist/index.js.map +1 -1
  154. package/dist/legacy-skill-catalog.d.ts.map +1 -1
  155. package/dist/legacy-skill-catalog.js +140 -0
  156. package/dist/legacy-skill-catalog.js.map +1 -1
  157. package/dist/package-client.d.ts +28 -0
  158. package/dist/package-client.d.ts.map +1 -1
  159. package/dist/package-client.js +21 -1
  160. package/dist/package-client.js.map +1 -1
  161. package/dist/package-manifest-file.d.ts +21 -0
  162. package/dist/package-manifest-file.d.ts.map +1 -0
  163. package/dist/package-manifest-file.js +119 -0
  164. package/dist/package-manifest-file.js.map +1 -0
  165. package/dist/package-manifest-template.d.ts +23 -0
  166. package/dist/package-manifest-template.d.ts.map +1 -0
  167. package/dist/package-manifest-template.js +60 -0
  168. package/dist/package-manifest-template.js.map +1 -0
  169. package/dist/package-source-refusal.d.ts +17 -0
  170. package/dist/package-source-refusal.d.ts.map +1 -0
  171. package/dist/package-source-refusal.js +61 -0
  172. package/dist/package-source-refusal.js.map +1 -0
  173. package/dist/package-submission.d.ts +134 -0
  174. package/dist/package-submission.d.ts.map +1 -0
  175. package/dist/package-submission.js +196 -0
  176. package/dist/package-submission.js.map +1 -0
  177. package/dist/selection-record.d.ts +95 -0
  178. package/dist/selection-record.d.ts.map +1 -0
  179. package/dist/selection-record.js +121 -0
  180. package/dist/selection-record.js.map +1 -0
  181. package/package.json +1 -1
  182. package/skills/design/SKILL.md +43 -266
  183. package/skills/design-branch/SKILL.md +37 -116
  184. package/skills/design-convert/SKILL.md +62 -148
  185. package/skills/design-draft/SKILL.md +71 -135
  186. package/skills/design-search/SKILL.md +29 -78
  187. package/skills/design/references/continuation-contract.md +0 -60
  188. package/skills/design/references/selection-contract.md +0 -112
@@ -0,0 +1,137 @@
1
+ import { BRANCH_WORKFLOW_REVISION, branchConceptDigest } from './branch-identity.js';
2
+ import { branchBlockedSlots } from './branch-schedule.js';
3
+ import { branchPlanPath, } from './branch-run-record.js';
4
+ /**
5
+ * The completion sentinel and the exit disposition: the two things that decide
6
+ * what a caller believes happened. Every screen is accounted for as succeeded,
7
+ * failed, or blocked-by-a-failure, so a run always reaches a terminal story
8
+ * rather than leaving a screen that will never move looking merely pending.
9
+ */
10
+ export const buildCompletion = (args) => {
11
+ const blocked = new Map(branchBlockedSlots(args.plan.screens.map((screen) => ({
12
+ slot: screen.slot,
13
+ status: screen.status,
14
+ footer: screen.footer,
15
+ dependsOnSlot: screen.dependsOnSlot,
16
+ }))).map((entry) => [entry.slot, entry]));
17
+ const pageIdByIndex = new Map(args.plan.pages.map((page) => [page.index, page.id]));
18
+ const screens = args.plan.screens.map((screen) => {
19
+ const downloaded = args.screens.get(screen.slot);
20
+ const block = blocked.get(screen.slot);
21
+ const status = screen.status === 'succeeded' && downloaded
22
+ ? 'succeeded'
23
+ : block
24
+ ? 'blocked'
25
+ : 'failed';
26
+ return {
27
+ slot: screen.slot,
28
+ pageIndex: screen.pageIndex,
29
+ pageId: pageIdByIndex.get(screen.pageIndex) ?? `p${screen.pageIndex}`,
30
+ viewportOrdinal: screen.viewportOrdinal,
31
+ status,
32
+ direction: screen.direction,
33
+ ...(downloaded
34
+ ? {
35
+ path: downloaded.path,
36
+ sha256: downloaded.sha256,
37
+ bytes: downloaded.bytes,
38
+ width: downloaded.width,
39
+ height: downloaded.height,
40
+ }
41
+ : {}),
42
+ ...(screen.dependsOnSlot ? { dependsOnSlot: screen.dependsOnSlot } : {}),
43
+ ...(screen.footer === true ? { footer: true } : {}),
44
+ ...(block ? { blockedBy: block.waitingOn, blockedReason: block.reason } : {}),
45
+ ...(screen.error ? { error: screen.error } : {}),
46
+ };
47
+ });
48
+ const succeededCount = screens.filter((screen) => screen.status === 'succeeded').length;
49
+ const blockedCount = screens.filter((screen) => screen.status === 'blocked').length;
50
+ const failedCount = screens.length - succeededCount - blockedCount;
51
+ const pages = args.plan.pages.map((page) => ({
52
+ index: page.index,
53
+ id: page.id,
54
+ kind: page.kind,
55
+ title: page.title,
56
+ slots: page.slots,
57
+ startsFromWinner: page.startsFromWinner,
58
+ ...(args.conversions.has(page.id) ? { conversion: args.conversions.get(page.id) } : {}),
59
+ }));
60
+ const notes = [];
61
+ if (blockedCount > 0) {
62
+ notes.push(`${blockedCount} screen(s) can never run: their dependency failed, so the run is terminal`
63
+ + ' rather than still waiting. Redraw the failed screen to unblock them.');
64
+ }
65
+ if (args.stopped)
66
+ notes.push(`Dispatch stopped early: ${args.stopped.code} — ${args.stopped.message}`);
67
+ const conversionsAttempted = [...args.conversions.values()];
68
+ const conversionFailed = conversionsAttempted.some((conversion) => conversion?.status === 'failed');
69
+ const status = succeededCount === 0 && screens.length > 0
70
+ ? 'failed'
71
+ : (failedCount > 0 || blockedCount > 0 || args.stopped || conversionFailed)
72
+ ? 'partial'
73
+ : 'succeeded';
74
+ return {
75
+ version: 1,
76
+ status,
77
+ runId: args.plan.runId,
78
+ runKey: args.runKey,
79
+ workflowRevision: BRANCH_WORKFLOW_REVISION,
80
+ scope: args.inputs.scope,
81
+ concept: args.inputs.concept,
82
+ conceptSha256: branchConceptDigest(args.inputs.concept),
83
+ origin: args.origin,
84
+ outDir: args.outDir,
85
+ attemptPath: args.attemptFile,
86
+ planPath: branchPlanPath(args.outDir),
87
+ startedAt: args.startedAt,
88
+ completedAt: new Date().toISOString(),
89
+ screenCount: screens.length,
90
+ succeededCount,
91
+ failedCount,
92
+ blockedCount,
93
+ ...(args.winner
94
+ ? {
95
+ winner: {
96
+ path: args.winner.path,
97
+ sha256: args.winner.sha256,
98
+ bytes: args.winner.bytes,
99
+ width: args.winner.width,
100
+ height: args.winner.height,
101
+ ...(args.inputs.convert?.sourceConversionId
102
+ ? { sourceConversionId: args.inputs.convert.sourceConversionId }
103
+ : {}),
104
+ },
105
+ }
106
+ : {}),
107
+ screens,
108
+ pages,
109
+ ...(notes.length > 0 ? { notes } : {}),
110
+ };
111
+ };
112
+ /**
113
+ * Park or fail, per CLAUDE.md §5. A run that lost some screens still delivered
114
+ * the pages that are whole, so it exits 0 with the gaps named. A run that lost
115
+ * everything, was cut off by quota or funds, or converted nothing it attempted
116
+ * has no artifact to hand over and says so.
117
+ */
118
+ export const terminalFailure = (args) => {
119
+ if (args.stopped) {
120
+ return new Error(`Branch run ${args.completion.runId} stopped: ${args.stopped.code} — ${args.stopped.message}`
121
+ + ` Whatever settled is materialized. Evidence: ${args.completionFile}`);
122
+ }
123
+ if (args.completion.screenCount > 0 && args.completion.succeededCount === 0) {
124
+ return new Error(`Branch run ${args.completion.runId} produced no screen. Evidence: ${args.completionFile}`);
125
+ }
126
+ if (args.convert) {
127
+ const attempted = args.completion.pages
128
+ .map((page) => page.conversion)
129
+ .filter((conversion) => conversion && conversion.status !== 'skipped');
130
+ if (attempted.length > 0 && attempted.every((conversion) => conversion.status === 'failed')) {
131
+ return new Error(`Branch run ${args.completion.runId} generated its screens but converted no page.`
132
+ + ` The screens are on disk. Evidence: ${args.completionFile}`);
133
+ }
134
+ }
135
+ return null;
136
+ };
137
+ //# sourceMappingURL=branch-completion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-completion.js","sourceRoot":"","sources":["../src/branch-completion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { type TwelveUiBranchClient } from './branch-client.js';
2
+ import { type BranchNormalizedPlan } from './branch-plan.js';
3
+ /**
4
+ * The dispatch driver: the scheduler decides WHICH screens may run, this decides
5
+ * WHEN to ask again. It is `apps/web/src/pages/branch/useBranchRun.ts` with the
6
+ * React state machine removed and its three intervals kept as the same
7
+ * constants, because they are the cadence the server was tuned against.
8
+ *
9
+ * Spending rules, unchanged from the board:
10
+ * - automatic dispatch sends NO idempotency key, so any replay is free;
11
+ * - a settled slot is never re-offered; a redraw is an explicit action;
12
+ * - a gate refusal is not an error — the slot is held and offered again.
13
+ */
14
+ export declare const BRANCH_POLL_INTERVAL_MS = 4000;
15
+ export declare const BRANCH_PUMP_INTERVAL_MS = 2000;
16
+ export declare const BRANCH_GATE_BACKOFF_MS = 4000;
17
+ /** A screen's own server budget is 150 s; a whole run is bounded far above it. */
18
+ export declare const BRANCH_RUN_TIMEOUT_MS: number;
19
+ export type BranchDispatchEvent = {
20
+ kind: 'dispatched';
21
+ slot: string;
22
+ } | {
23
+ kind: 'succeeded';
24
+ slot: string;
25
+ } | {
26
+ kind: 'failed';
27
+ slot: string;
28
+ code: string;
29
+ message: string;
30
+ } | {
31
+ kind: 'held';
32
+ slot: string;
33
+ code: string;
34
+ waitingOn?: string;
35
+ } | {
36
+ kind: 'in-flight';
37
+ slot: string;
38
+ } | {
39
+ kind: 'blocked';
40
+ slot: string;
41
+ waitingOn: string;
42
+ reason: string;
43
+ } | {
44
+ kind: 'stopped';
45
+ code: string;
46
+ message: string;
47
+ };
48
+ export type BranchDispatchStop = {
49
+ code: string;
50
+ message: string;
51
+ };
52
+ export type BranchDispatchResult = {
53
+ plan: BranchNormalizedPlan;
54
+ stopped: BranchDispatchStop | null;
55
+ };
56
+ /** Everything a status surface needs to narrate motion, taken at one instant. */
57
+ export type BranchDispatchSnapshot = {
58
+ plan: BranchNormalizedPlan;
59
+ dispatchedAt: Readonly<Record<string, string>>;
60
+ };
61
+ /** Cadence and clock overrides. Tests drive these; the CLI never sets them. */
62
+ export type BranchDispatchTuning = {
63
+ now?: () => number;
64
+ sleep?: (milliseconds: number) => Promise<void>;
65
+ pollIntervalMs?: number;
66
+ pumpIntervalMs?: number;
67
+ gateBackoffMs?: number;
68
+ timeoutMs?: number;
69
+ };
70
+ export declare const runBranchDispatch: (args: {
71
+ client: TwelveUiBranchClient;
72
+ plan: BranchNormalizedPlan;
73
+ onEvent?: (event: BranchDispatchEvent) => void;
74
+ /** Called on every loop turn, changed or not, so a poller always sees motion. */
75
+ onSnapshot?: (snapshot: BranchDispatchSnapshot) => void;
76
+ now?: () => number;
77
+ sleep?: (milliseconds: number) => Promise<void>;
78
+ pollIntervalMs?: number;
79
+ pumpIntervalMs?: number;
80
+ gateBackoffMs?: number;
81
+ timeoutMs?: number;
82
+ signal?: AbortSignal;
83
+ }) => Promise<BranchDispatchResult>;
84
+ //# sourceMappingURL=branch-dispatch-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-dispatch-loop.d.ts","sourceRoot":"","sources":["../src/branch-dispatch-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ import { BranchApiError } from './branch-client.js';
2
+ import { branchBlockedSlots, branchDispatchableSlots, branchRunSettled, } from './branch-schedule.js';
3
+ import { applyBranchEnvelope } from './branch-plan.js';
4
+ /**
5
+ * The dispatch driver: the scheduler decides WHICH screens may run, this decides
6
+ * WHEN to ask again. It is `apps/web/src/pages/branch/useBranchRun.ts` with the
7
+ * React state machine removed and its three intervals kept as the same
8
+ * constants, because they are the cadence the server was tuned against.
9
+ *
10
+ * Spending rules, unchanged from the board:
11
+ * - automatic dispatch sends NO idempotency key, so any replay is free;
12
+ * - a settled slot is never re-offered; a redraw is an explicit action;
13
+ * - a gate refusal is not an error — the slot is held and offered again.
14
+ */
15
+ export const BRANCH_POLL_INTERVAL_MS = 4_000;
16
+ export const BRANCH_PUMP_INTERVAL_MS = 2_000;
17
+ export const BRANCH_GATE_BACKOFF_MS = 4_000;
18
+ /** A screen's own server budget is 150 s; a whole run is bounded far above it. */
19
+ export const BRANCH_RUN_TIMEOUT_MS = 60 * 60_000;
20
+ /** Refusals that end the run rather than holding one screen back. */
21
+ const TERMINAL_CODES = new Set([
22
+ 'quota_exhausted',
23
+ 'insufficient_funds',
24
+ 'expired',
25
+ 'rate_limited',
26
+ 'not_found',
27
+ 'unauthorized',
28
+ 'forbidden',
29
+ ]);
30
+ const scheduleView = (plan) => (plan.screens.map((screen) => ({
31
+ slot: screen.slot,
32
+ status: screen.status,
33
+ footer: screen.footer,
34
+ dependsOnSlot: screen.dependsOnSlot,
35
+ })));
36
+ const defaultSleep = async (milliseconds) => (new Promise((resolve) => { setTimeout(resolve, milliseconds); }));
37
+ export const runBranchDispatch = async (args) => {
38
+ const now = args.now ?? Date.now;
39
+ const sleep = args.sleep ?? defaultSleep;
40
+ const pollIntervalMs = args.pollIntervalMs ?? BRANCH_POLL_INTERVAL_MS;
41
+ const pumpIntervalMs = args.pumpIntervalMs ?? BRANCH_PUMP_INTERVAL_MS;
42
+ const gateBackoffMs = args.gateBackoffMs ?? BRANCH_GATE_BACKOFF_MS;
43
+ const emit = args.onEvent ?? (() => undefined);
44
+ const deadline = now() + (args.timeoutMs ?? BRANCH_RUN_TIMEOUT_MS);
45
+ let plan = args.plan;
46
+ let stopped = null;
47
+ const inFlight = new Set();
48
+ const heldUntil = {};
49
+ const active = new Set();
50
+ const announcedBlocked = new Set();
51
+ const dispatchedAt = {};
52
+ const snapshot = () => args.onSnapshot?.({ plan, dispatchedAt });
53
+ const markDispatched = (slot) => {
54
+ plan = {
55
+ ...plan,
56
+ screens: plan.screens.map((screen) => (screen.slot === slot && screen.status === 'pending'
57
+ ? { ...screen, status: 'dispatched' }
58
+ : screen)),
59
+ };
60
+ };
61
+ const markPending = (slot) => {
62
+ plan = {
63
+ ...plan,
64
+ screens: plan.screens.map((screen) => (screen.slot === slot ? { ...screen, status: 'pending' } : screen)),
65
+ };
66
+ };
67
+ const markFailed = (slot, code, message) => {
68
+ plan = {
69
+ ...plan,
70
+ screens: plan.screens.map((screen) => (screen.slot === slot
71
+ ? { ...screen, status: 'failed', error: { code, message } }
72
+ : screen)),
73
+ };
74
+ };
75
+ const announceBlocked = () => {
76
+ for (const blocked of branchBlockedSlots(scheduleView(plan))) {
77
+ if (announcedBlocked.has(blocked.slot))
78
+ continue;
79
+ announcedBlocked.add(blocked.slot);
80
+ emit({
81
+ kind: 'blocked',
82
+ slot: blocked.slot,
83
+ waitingOn: blocked.waitingOn,
84
+ reason: blocked.reason,
85
+ });
86
+ }
87
+ };
88
+ const dispatch = async (slot) => {
89
+ inFlight.add(slot);
90
+ markDispatched(slot);
91
+ dispatchedAt[slot] = new Date(now()).toISOString();
92
+ emit({ kind: 'dispatched', slot });
93
+ snapshot();
94
+ try {
95
+ // No idempotency key: an unkeyed repeat replays the settled screen free,
96
+ // while a new key would buy another image.
97
+ const envelope = await args.client.dispatchScreen(plan.runId, slot, {
98
+ ...(args.signal ? { signal: args.signal } : {}),
99
+ });
100
+ plan = applyBranchEnvelope(plan, envelope);
101
+ const resolved = plan.screens.find((screen) => screen.slot === slot);
102
+ if (resolved?.status === 'succeeded')
103
+ emit({ kind: 'succeeded', slot });
104
+ if (resolved?.status === 'failed') {
105
+ emit({
106
+ kind: 'failed',
107
+ slot,
108
+ code: resolved.error?.code ?? 'failed',
109
+ message: resolved.error?.message ?? 'This screen could not be generated.',
110
+ });
111
+ }
112
+ }
113
+ catch (error) {
114
+ const code = error instanceof BranchApiError ? error.code : 'error';
115
+ const message = error instanceof Error ? error.message : String(error);
116
+ if (code === 'dependency_pending' || code === 'footer_reference_pending') {
117
+ // The worker knows more than this client did. Hold the slot and let the
118
+ // scheduler offer it again once its reference lands.
119
+ heldUntil[slot] = now() + gateBackoffMs;
120
+ markPending(slot);
121
+ emit({
122
+ kind: 'held',
123
+ slot,
124
+ code,
125
+ ...(error instanceof BranchApiError && error.dependsOn
126
+ ? { waitingOn: error.dependsOn }
127
+ : {}),
128
+ });
129
+ }
130
+ else if (code === 'candidate_in_flight') {
131
+ // Another caller owns the paid call. Leave the slot dispatched; the run
132
+ // poll reports its result. Note the FLAT error shape here.
133
+ emit({ kind: 'in-flight', slot });
134
+ }
135
+ else if (TERMINAL_CODES.has(code) || (error instanceof BranchApiError && error.status >= 500)) {
136
+ markPending(slot);
137
+ stopped ??= { code, message };
138
+ }
139
+ else {
140
+ markFailed(slot, code, message);
141
+ emit({ kind: 'failed', slot, code, message });
142
+ }
143
+ }
144
+ finally {
145
+ inFlight.delete(slot);
146
+ snapshot();
147
+ }
148
+ };
149
+ const poll = async () => {
150
+ try {
151
+ plan = applyBranchEnvelope(plan, await args.client.getRun(plan.runId, args.signal));
152
+ }
153
+ catch (error) {
154
+ const code = error instanceof BranchApiError ? error.code : 'error';
155
+ if (TERMINAL_CODES.has(code))
156
+ stopped ??= { code, message: error.message };
157
+ }
158
+ };
159
+ const track = (promise) => {
160
+ const tracked = promise.finally(() => { active.delete(tracked); });
161
+ active.add(tracked);
162
+ };
163
+ while (plan.screens.length > 0 && !branchRunSettled(scheduleView(plan))) {
164
+ if (stopped)
165
+ break;
166
+ if (now() >= deadline) {
167
+ stopped = {
168
+ code: 'run_timeout',
169
+ message: `Branch run ${plan.runId} did not settle within the run budget.`,
170
+ };
171
+ break;
172
+ }
173
+ announceBlocked();
174
+ for (const slot of branchDispatchableSlots(scheduleView(plan), {
175
+ inFlight,
176
+ heldUntil,
177
+ now: now(),
178
+ })) {
179
+ track(dispatch(slot));
180
+ }
181
+ // Every turn publishes a snapshot, changed or not: a poller that sees only
182
+ // "nothing yet" is the one that gives up and improvises.
183
+ snapshot();
184
+ if (active.size > 0) {
185
+ await Promise.race([...active, sleep(pumpIntervalMs)]);
186
+ continue;
187
+ }
188
+ // Nothing of ours is running. Either a sibling caller owns a slot, or every
189
+ // remaining screen is inside its gate backoff; both resolve by waiting and
190
+ // re-reading rather than by dispatching again.
191
+ await poll();
192
+ if (stopped || branchRunSettled(scheduleView(plan)))
193
+ break;
194
+ const nextOffer = Math.min(...Object.values(heldUntil).filter((until) => until > now()), now() + pollIntervalMs);
195
+ await sleep(Math.max(pumpIntervalMs, Math.min(pollIntervalMs, nextOffer - now())));
196
+ }
197
+ // Whatever ended the loop, in-flight work still owns paid calls; let it land
198
+ // so its result reaches the sentinel instead of being lost.
199
+ await Promise.allSettled([...active]);
200
+ announceBlocked();
201
+ snapshot();
202
+ if (stopped)
203
+ emit({ kind: 'stopped', ...stopped });
204
+ return { plan, stopped };
205
+ };
206
+ //# sourceMappingURL=branch-dispatch-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-dispatch-loop.js","sourceRoot":"","sources":["../src/branch-dispatch-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { TwelveUiBranchClient } from './branch-client.js';
2
+ /**
3
+ * Screens are written into a hidden staging directory and moved into
4
+ * `screens/` in one pass once the run is terminal, exactly as
5
+ * `image generate-batch` materializes its candidates. A caller listing
6
+ * `screens/` therefore never sees a half-written run and mistake it for
7
+ * failure; the directory goes from empty to complete.
8
+ *
9
+ * The staging directory is a child of the destination on purpose: a
10
+ * same-directory rename is same-filesystem, so materialization is atomic and
11
+ * can never fail with EXDEV.
12
+ */
13
+ export declare const BRANCH_STAGING_DIRECTORY_NAME = ".12ui-branch-staging";
14
+ export type BranchDownloadedScreen = {
15
+ slot: string;
16
+ path: string;
17
+ sha256: string;
18
+ bytes: number;
19
+ width: number;
20
+ height: number;
21
+ /** Where the bytes live until the run materializes them. */
22
+ stagedPath?: string;
23
+ };
24
+ export declare const branchScreenPath: (screensDirectory: string, slot: string) => string;
25
+ /** Bytes already on disk are already paid for; a resume never re-downloads. */
26
+ export declare const readMaterializedScreen: (screensDirectory: string, slot: string) => Promise<BranchDownloadedScreen | null>;
27
+ export declare const stageBranchScreenBytes: (args: {
28
+ screensDirectory: string;
29
+ slot: string;
30
+ bytes: Uint8Array;
31
+ }) => Promise<BranchDownloadedScreen>;
32
+ /**
33
+ * A screen is delivered in two phases — staged while the run is still going,
34
+ * moved into `screens/` in one atomic pass at the end — and a page conversion
35
+ * that starts as soon as its own screens land reads across that move. Both
36
+ * names address the same delivered bytes, so a reader tries the staged name and
37
+ * falls through to the final one when the move has already happened.
38
+ */
39
+ export declare const readBranchScreenBytes: (screen: BranchDownloadedScreen) => Promise<Uint8Array>;
40
+ export declare const stageBranchScreen: (args: {
41
+ client: TwelveUiBranchClient;
42
+ runId: string;
43
+ slot: string;
44
+ screensDirectory: string;
45
+ signal?: AbortSignal;
46
+ }) => Promise<BranchDownloadedScreen>;
47
+ /**
48
+ * Deliver every staged screen at once. Anything already materialized — a
49
+ * resumed run's earlier screens — stays where it is rather than being
50
+ * overwritten with bytes that would have to be proved identical first.
51
+ */
52
+ export declare const materializeBranchScreens: (screensDirectory: string, slots: readonly string[]) => Promise<void>;
53
+ //# sourceMappingURL=branch-downloads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-downloads.d.ts","sourceRoot":"","sources":["../src/branch-downloads.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAG/D;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,6BAA6B,yBAAyB,CAAC;AAEpE,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,kBAAkB,MAAM,EAAE,MAAM,MAAM,KAAG,MAEzE,CAAC;AAgBF,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,GACjC,kBAAkB,MAAM,EACxB,MAAM,MAAM,KACX,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAiBvC,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAU,MAAM;IACjD,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;CACnB,KAAG,OAAO,CAAC,sBAAsB,CAcjC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,sBAAsB,KAC7B,OAAO,CAAC,UAAU,CASpB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAC5C,MAAM,EAAE,oBAAoB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAG,OAAO,CAAC,sBAAsB,CAOjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,kBAAkB,MAAM,EACxB,OAAO,SAAS,MAAM,EAAE,KACvB,OAAO,CAAC,IAAI,CAgBd,CAAC"}
@@ -0,0 +1,114 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, rename, rm, rmdir, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { readImageDimensions } from './image-dimensions.js';
5
+ /**
6
+ * Screens are written into a hidden staging directory and moved into
7
+ * `screens/` in one pass once the run is terminal, exactly as
8
+ * `image generate-batch` materializes its candidates. A caller listing
9
+ * `screens/` therefore never sees a half-written run and mistake it for
10
+ * failure; the directory goes from empty to complete.
11
+ *
12
+ * The staging directory is a child of the destination on purpose: a
13
+ * same-directory rename is same-filesystem, so materialization is atomic and
14
+ * can never fail with EXDEV.
15
+ */
16
+ export const BRANCH_STAGING_DIRECTORY_NAME = '.12ui-branch-staging';
17
+ export const branchScreenPath = (screensDirectory, slot) => (path.join(screensDirectory, `${slot}.png`));
18
+ const stagedScreenPath = (screensDirectory, slot) => (path.join(screensDirectory, BRANCH_STAGING_DIRECTORY_NAME, `${slot}.png.part`));
19
+ const pathExists = async (target) => {
20
+ try {
21
+ await readFile(target);
22
+ return true;
23
+ }
24
+ catch (error) {
25
+ if (error.code === 'ENOENT')
26
+ return false;
27
+ throw error;
28
+ }
29
+ };
30
+ /** Bytes already on disk are already paid for; a resume never re-downloads. */
31
+ export const readMaterializedScreen = async (screensDirectory, slot) => {
32
+ const file = branchScreenPath(screensDirectory, slot);
33
+ let bytes;
34
+ try {
35
+ bytes = await readFile(file);
36
+ }
37
+ catch (error) {
38
+ if (error.code === 'ENOENT')
39
+ return null;
40
+ throw error;
41
+ }
42
+ const dimensions = await readImageDimensions(bytes);
43
+ return {
44
+ slot,
45
+ path: file,
46
+ sha256: createHash('sha256').update(bytes).digest('hex'),
47
+ bytes: bytes.byteLength,
48
+ ...dimensions,
49
+ };
50
+ };
51
+ export const stageBranchScreenBytes = async (args) => {
52
+ const staged = stagedScreenPath(args.screensDirectory, args.slot);
53
+ await mkdir(path.dirname(staged), { recursive: true });
54
+ await rm(staged, { force: true });
55
+ await writeFile(staged, args.bytes, { flag: 'wx', mode: 0o600 });
56
+ const dimensions = await readImageDimensions(args.bytes);
57
+ return {
58
+ slot: args.slot,
59
+ path: branchScreenPath(args.screensDirectory, args.slot),
60
+ stagedPath: staged,
61
+ sha256: createHash('sha256').update(args.bytes).digest('hex'),
62
+ bytes: args.bytes.byteLength,
63
+ ...dimensions,
64
+ };
65
+ };
66
+ /**
67
+ * A screen is delivered in two phases — staged while the run is still going,
68
+ * moved into `screens/` in one atomic pass at the end — and a page conversion
69
+ * that starts as soon as its own screens land reads across that move. Both
70
+ * names address the same delivered bytes, so a reader tries the staged name and
71
+ * falls through to the final one when the move has already happened.
72
+ */
73
+ export const readBranchScreenBytes = async (screen) => {
74
+ if (screen.stagedPath) {
75
+ try {
76
+ return new Uint8Array(await readFile(screen.stagedPath));
77
+ }
78
+ catch (error) {
79
+ if (error.code !== 'ENOENT')
80
+ throw error;
81
+ }
82
+ }
83
+ return new Uint8Array(await readFile(screen.path));
84
+ };
85
+ export const stageBranchScreen = async (args) => {
86
+ const downloaded = await args.client.downloadScreen(args.runId, args.slot, args.signal);
87
+ return stageBranchScreenBytes({
88
+ screensDirectory: args.screensDirectory,
89
+ slot: args.slot,
90
+ bytes: downloaded.bytes,
91
+ });
92
+ };
93
+ /**
94
+ * Deliver every staged screen at once. Anything already materialized — a
95
+ * resumed run's earlier screens — stays where it is rather than being
96
+ * overwritten with bytes that would have to be proved identical first.
97
+ */
98
+ export const materializeBranchScreens = async (screensDirectory, slots) => {
99
+ await Promise.all(slots.map(async (slot) => {
100
+ const staged = stagedScreenPath(screensDirectory, slot);
101
+ if (!(await pathExists(staged)))
102
+ return;
103
+ const destination = branchScreenPath(screensDirectory, slot);
104
+ if (await pathExists(destination)) {
105
+ throw new Error(`Refusing to overwrite an existing screen at ${destination} with staged bytes from`
106
+ + ` ${staged}; inspect both before continuing.`);
107
+ }
108
+ await rename(staged, destination);
109
+ }));
110
+ // rmdir refuses a non-empty directory, so this both cleans up and proves
111
+ // nothing was left undelivered.
112
+ await rmdir(path.join(screensDirectory, BRANCH_STAGING_DIRECTORY_NAME)).catch(() => undefined);
113
+ };
114
+ //# sourceMappingURL=branch-downloads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-downloads.js","sourceRoot":"","sources":["../src/branch-downloads.ts"],"names":[],"mappings":"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"}