@cf-vibesdk/sdk 0.0.7 → 0.0.8

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/README.md CHANGED
@@ -96,13 +96,21 @@ const session = await client.build('Build a weather dashboard', {
96
96
 
97
97
  ### `client.connect(agentId)`
98
98
 
99
- Connect to an existing app session.
99
+ Connect to an existing app session. State is automatically restored from the agent, including:
100
+ - Phase timeline with completion status
101
+ - Generated files
102
+ - Agent metadata (query, projectName, behaviorType, etc.)
100
103
 
101
104
  ```ts
102
105
  const session = await client.connect('agent-id-here', {
103
106
  credentials: { ... }, // Optional
104
107
  });
105
108
  await session.connect();
109
+
110
+ // State is now seeded from the agent
111
+ console.log('Original query:', session.state.get().query);
112
+ console.log('Phases:', session.phases.list());
113
+ console.log('Files:', session.files.listPaths());
106
114
  ```
107
115
 
108
116
  ## App Management
@@ -215,6 +223,53 @@ const snapshot = session.files.snapshot(); // { 'src/App.tsx': '...', ... }
215
223
  const tree = session.files.tree(); // Nested file tree structure
216
224
  ```
217
225
 
226
+ ### Phase Timeline
227
+
228
+ Access the full phase timeline for phasic builds. The timeline is automatically seeded when connecting to an existing agent and updated as phases progress.
229
+
230
+ ```ts
231
+ // Get all phases
232
+ const phases = session.phases.list();
233
+ // [{ id: 'phase-0', name: 'Core Setup', status: 'completed', files: [...] }, ...]
234
+
235
+ // Get current active phase
236
+ const current = session.phases.current();
237
+ if (current) {
238
+ console.log(`Working on: ${current.name}`);
239
+ console.log(`Status: ${current.status}`); // 'generating' | 'implementing' | 'validating'
240
+ }
241
+
242
+ // Get completed phases
243
+ const done = session.phases.completed();
244
+ console.log(`Progress: ${done.length}/${session.phases.count()}`);
245
+
246
+ // Check if all phases are done
247
+ if (session.phases.allCompleted()) {
248
+ console.log('Build complete!');
249
+ }
250
+
251
+ // Get phase by ID
252
+ const phase = session.phases.get('phase-0');
253
+ ```
254
+
255
+ Each phase contains:
256
+
257
+ ```ts
258
+ type PhaseInfo = {
259
+ id: string; // 'phase-0', 'phase-1', etc.
260
+ name: string; // 'Core Setup', 'Authentication', etc.
261
+ description: string; // What the phase accomplishes
262
+ status: PhaseStatus; // 'pending' | 'generating' | 'implementing' | 'validating' | 'completed' | 'cancelled'
263
+ files: PhaseFile[]; // Files in this phase
264
+ };
265
+
266
+ type PhaseFile = {
267
+ path: string; // 'src/App.tsx'
268
+ purpose: string; // 'Main application component'
269
+ status: PhaseFileStatus; // 'pending' | 'generating' | 'completed' | 'cancelled'
270
+ };
271
+ ```
272
+
218
273
  ### State
219
274
 
220
275
  ```ts
@@ -226,6 +281,16 @@ console.log(state.phase); // { status: 'idle' | 'generating' | ... }
226
281
  console.log(state.preview); // Preview deployment state
227
282
  console.log(state.cloudflare); // Cloudflare deployment state
228
283
 
284
+ // Phase timeline (array of all phases)
285
+ console.log(state.phases); // [{ id, name, status, files }, ...]
286
+
287
+ // Agent metadata (seeded from agent_connected)
288
+ console.log(state.behaviorType); // 'phasic' | 'agentic'
289
+ console.log(state.projectType); // 'app' | 'workflow' | etc.
290
+ console.log(state.query); // Original user prompt
291
+ console.log(state.projectName); // Project name from blueprint
292
+ console.log(state.shouldBeGenerating); // Whether agent is actively generating
293
+
229
294
  // Subscribe to changes
230
295
  session.state.onChange((next, prev) => {
231
296
  console.log('State changed:', next);
@@ -324,10 +389,22 @@ All types are exported:
324
389
 
325
390
  ```ts
326
391
  import type {
392
+ // Client & Session
327
393
  VibeClientOptions,
328
394
  BuildOptions,
329
395
  BuildSession,
330
396
  SessionState,
397
+ SessionFiles,
398
+ SessionPhases,
399
+
400
+ // Phase Timeline
401
+ PhaseInfo,
402
+ PhaseFile,
403
+ PhaseStatus,
404
+ PhaseFileStatus,
405
+ PhaseEventType,
406
+
407
+ // API
331
408
  ApiResponse,
332
409
  AppDetails,
333
410
  Credentials,
package/dist/index.d.ts CHANGED
@@ -5613,6 +5613,50 @@ export type PhaseEventType = "phase_generating" | "phase_generated" | "phase_imp
5613
5613
  export type WaitForPhaseOptions = WaitOptions & {
5614
5614
  type: PhaseEventType;
5615
5615
  };
5616
+ /**
5617
+ * Status of a file within a phase.
5618
+ */
5619
+ export type PhaseFileStatus = "pending" | "generating" | "completed" | "cancelled";
5620
+ /**
5621
+ * A file concept within a phase, with its generation status.
5622
+ * Extends platform's FileConceptType with status tracking.
5623
+ */
5624
+ export type PhaseFile = Pick<FileConceptType, "path" | "purpose"> & {
5625
+ status: PhaseFileStatus;
5626
+ };
5627
+ /**
5628
+ * Status of a phase in the timeline.
5629
+ */
5630
+ export type PhaseStatus = "pending" | "generating" | "implementing" | "validating" | "completed" | "cancelled";
5631
+ /**
5632
+ * A phase in the build timeline with its files and status.
5633
+ * Extends platform's PhaseConceptType with SDK-specific fields.
5634
+ */
5635
+ export type PhaseInfo = Pick<PhaseConceptType, "name" | "description"> & {
5636
+ /** Unique identifier for this phase (e.g., "phase-0", "phase-1"). */
5637
+ id: string;
5638
+ /** Current status of the phase. */
5639
+ status: PhaseStatus;
5640
+ /** Files in this phase with their generation status. */
5641
+ files: PhaseFile[];
5642
+ };
5643
+ /**
5644
+ * High-level API for accessing phase timeline data.
5645
+ */
5646
+ export type SessionPhases = {
5647
+ /** Get all phases in the timeline. */
5648
+ list: () => PhaseInfo[];
5649
+ /** Get the currently active phase (first non-completed phase), or undefined. */
5650
+ current: () => PhaseInfo | undefined;
5651
+ /** Get all completed phases. */
5652
+ completed: () => PhaseInfo[];
5653
+ /** Get a phase by its id (e.g., "phase-0"). */
5654
+ get: (id: string) => PhaseInfo | undefined;
5655
+ /** Get the total count of phases. */
5656
+ count: () => number;
5657
+ /** Check if all phases are completed. */
5658
+ allCompleted: () => boolean;
5659
+ };
5616
5660
  export type SessionDeployable = {
5617
5661
  files: number;
5618
5662
  reason: "generation_complete" | "phase_validated";
@@ -5697,6 +5741,18 @@ export type SessionState = {
5697
5741
  preview: PreviewDeploymentState;
5698
5742
  cloudflare: CloudflareDeploymentState;
5699
5743
  lastError?: string;
5744
+ /** Full phase timeline with status and files for each phase. */
5745
+ phases: PhaseInfo[];
5746
+ /** Behavior type of the agent (phasic or agentic). */
5747
+ behaviorType?: BehaviorType$1;
5748
+ /** Project type (app, workflow, presentation, general). */
5749
+ projectType?: ProjectType$1;
5750
+ /** Original user query that started this build. */
5751
+ query?: string;
5752
+ /** Whether the agent should be actively generating. */
5753
+ shouldBeGenerating?: boolean;
5754
+ /** Project name from the blueprint. */
5755
+ projectName?: string;
5700
5756
  };
5701
5757
  export declare class SessionStateStore {
5702
5758
  private state;
@@ -5705,6 +5761,14 @@ export declare class SessionStateStore {
5705
5761
  onChange(cb: (next: SessionState, prev: SessionState) => void): () => void;
5706
5762
  setConnection(state: ConnectionState): void;
5707
5763
  applyWsMessage(msg: AgentWsServerMessage): void;
5764
+ /**
5765
+ * Update the status of a file in the phase timeline.
5766
+ */
5767
+ private updateFileStatus;
5768
+ /**
5769
+ * Update an existing phase or add a new one to the timeline.
5770
+ */
5771
+ private updateOrAddPhase;
5708
5772
  private setState;
5709
5773
  clear(): void;
5710
5774
  }
@@ -5775,6 +5839,24 @@ export declare class BuildSession {
5775
5839
  readonly workspace: WorkspaceStore;
5776
5840
  readonly state: SessionStateStore;
5777
5841
  readonly files: SessionFiles;
5842
+ /**
5843
+ * High-level API for accessing the phase timeline.
5844
+ * Phases are seeded from agent_connected and updated on phase events.
5845
+ */
5846
+ readonly phases: {
5847
+ /** Get all phases in the timeline. */
5848
+ list: () => PhaseInfo[];
5849
+ /** Get the currently active phase (first non-completed phase), or undefined. */
5850
+ current: () => PhaseInfo | undefined;
5851
+ /** Get all completed phases. */
5852
+ completed: () => PhaseInfo[];
5853
+ /** Get a phase by its id (e.g., "phase-0"). */
5854
+ get: (id: string) => PhaseInfo | undefined;
5855
+ /** Get the total count of phases. */
5856
+ count: () => number;
5857
+ /** Check if all phases are completed. */
5858
+ allCompleted: () => boolean;
5859
+ };
5778
5860
  readonly wait: {
5779
5861
  generationStarted: (options?: WaitOptions) => Promise<{
5780
5862
  type: "generation_started";
package/dist/index.js CHANGED
@@ -227,7 +227,8 @@ var INITIAL_STATE = {
227
227
  generation: { status: "idle" },
228
228
  phase: { status: "idle" },
229
229
  preview: { status: "idle" },
230
- cloudflare: { status: "idle" }
230
+ cloudflare: { status: "idle" },
231
+ phases: []
231
232
  };
232
233
  function extractPhaseInfo(msg) {
233
234
  const phase = msg?.phase;
@@ -236,6 +237,51 @@ function extractPhaseInfo(msg) {
236
237
  description: phase?.description
237
238
  };
238
239
  }
240
+ function extractPhaseFiles(msg) {
241
+ const phase = msg?.phase;
242
+ return phase?.files;
243
+ }
244
+ function isPhasicState(state) {
245
+ return state.behaviorType === "phasic" && "generatedPhases" in state;
246
+ }
247
+ function buildPhaseTimelineFromState(state, generatedFilesMap) {
248
+ if (!isPhasicState(state))
249
+ return [];
250
+ const isActivelyGenerating = state.shouldBeGenerating === true;
251
+ return state.generatedPhases.map((phase, index) => {
252
+ let status;
253
+ if (phase.completed) {
254
+ status = "completed";
255
+ } else if (!isActivelyGenerating) {
256
+ status = "cancelled";
257
+ } else {
258
+ status = "generating";
259
+ }
260
+ const files = phase.files.map((f) => {
261
+ const fileExists = f.path in generatedFilesMap;
262
+ let fileStatus;
263
+ if (fileExists) {
264
+ fileStatus = "completed";
265
+ } else if (!isActivelyGenerating) {
266
+ fileStatus = "cancelled";
267
+ } else {
268
+ fileStatus = "pending";
269
+ }
270
+ return {
271
+ path: f.path,
272
+ purpose: f.purpose,
273
+ status: fileStatus
274
+ };
275
+ });
276
+ return {
277
+ id: `phase-${index}`,
278
+ name: phase.name,
279
+ description: phase.description,
280
+ status,
281
+ files
282
+ };
283
+ });
284
+ }
239
285
 
240
286
  class SessionStateStore {
241
287
  state = INITIAL_STATE;
@@ -303,47 +349,90 @@ class SessionStateStore {
303
349
  }
304
350
  case "file_generating": {
305
351
  const m = msg;
306
- this.setState({ currentFile: m.filePath });
352
+ const phasesWithGenerating = this.updateFileStatus(m.filePath, "generating");
353
+ this.setState({ currentFile: m.filePath, phases: phasesWithGenerating });
307
354
  break;
308
355
  }
309
356
  case "file_generated": {
357
+ const m = msg;
358
+ const filePath = m.file?.filePath;
310
359
  const prev = this.state.generation;
360
+ const phasesWithCompleted = filePath ? this.updateFileStatus(filePath, "completed") : this.state.phases;
311
361
  if (prev.status === "running" || prev.status === "stopped") {
312
362
  this.setState({
313
363
  generation: { ...prev, filesGenerated: prev.filesGenerated + 1 },
314
- currentFile: undefined
364
+ currentFile: undefined,
365
+ phases: phasesWithCompleted
315
366
  });
367
+ } else {
368
+ this.setState({ phases: phasesWithCompleted, currentFile: undefined });
316
369
  }
317
370
  break;
318
371
  }
319
372
  case "phase_generating": {
320
373
  const m = msg;
321
- this.setState({ phase: { status: "generating", ...extractPhaseInfo(m) } });
374
+ const phaseInfo = extractPhaseInfo(m);
375
+ const phaseFiles = extractPhaseFiles(m);
376
+ const phases = this.updateOrAddPhase(phaseInfo, "generating", phaseFiles);
377
+ this.setState({
378
+ phase: { status: "generating", ...phaseInfo },
379
+ phases
380
+ });
322
381
  break;
323
382
  }
324
383
  case "phase_generated": {
325
384
  const m = msg;
326
- this.setState({ phase: { status: "generated", ...extractPhaseInfo(m) } });
385
+ const phaseInfo = extractPhaseInfo(m);
386
+ const phaseFiles = extractPhaseFiles(m);
387
+ const phases = this.updateOrAddPhase(phaseInfo, "implementing", phaseFiles);
388
+ this.setState({
389
+ phase: { status: "generated", ...phaseInfo },
390
+ phases
391
+ });
327
392
  break;
328
393
  }
329
394
  case "phase_implementing": {
330
395
  const m = msg;
331
- this.setState({ phase: { status: "implementing", ...extractPhaseInfo(m) } });
396
+ const phaseInfo = extractPhaseInfo(m);
397
+ const phaseFiles = extractPhaseFiles(m);
398
+ const phases = this.updateOrAddPhase(phaseInfo, "implementing", phaseFiles);
399
+ this.setState({
400
+ phase: { status: "implementing", ...phaseInfo },
401
+ phases
402
+ });
332
403
  break;
333
404
  }
334
405
  case "phase_implemented": {
335
406
  const m = msg;
336
- this.setState({ phase: { status: "implemented", ...extractPhaseInfo(m) } });
407
+ const phaseInfo = extractPhaseInfo(m);
408
+ const phaseFiles = extractPhaseFiles(m);
409
+ const phases = this.updateOrAddPhase(phaseInfo, "validating", phaseFiles);
410
+ this.setState({
411
+ phase: { status: "implemented", ...phaseInfo },
412
+ phases
413
+ });
337
414
  break;
338
415
  }
339
416
  case "phase_validating": {
340
417
  const m = msg;
341
- this.setState({ phase: { status: "validating", ...extractPhaseInfo(m) } });
418
+ const phaseInfo = extractPhaseInfo(m);
419
+ const phaseFiles = extractPhaseFiles(m);
420
+ const phases = this.updateOrAddPhase(phaseInfo, "validating", phaseFiles);
421
+ this.setState({
422
+ phase: { status: "validating", ...phaseInfo },
423
+ phases
424
+ });
342
425
  break;
343
426
  }
344
427
  case "phase_validated": {
345
428
  const m = msg;
346
- this.setState({ phase: { status: "validated", ...extractPhaseInfo(m) } });
429
+ const phaseInfo = extractPhaseInfo(m);
430
+ const phaseFiles = extractPhaseFiles(m);
431
+ const phases = this.updateOrAddPhase(phaseInfo, "completed", phaseFiles);
432
+ this.setState({
433
+ phase: { status: "validated", ...phaseInfo },
434
+ phases
435
+ });
347
436
  break;
348
437
  }
349
438
  case "deployment_started": {
@@ -394,9 +483,24 @@ class SessionStateStore {
394
483
  }
395
484
  case "agent_connected": {
396
485
  const m = msg;
486
+ const agentState = m.state;
397
487
  const previewUrl = m.previewUrl;
398
- if (previewUrl)
399
- this.setState({ previewUrl });
488
+ const phases = buildPhaseTimelineFromState(agentState, agentState.generatedFilesMap ?? {});
489
+ let generation = this.state.generation;
490
+ if (agentState.shouldBeGenerating) {
491
+ const filesGenerated = Object.keys(agentState.generatedFilesMap ?? {}).length;
492
+ generation = { status: "running", filesGenerated };
493
+ }
494
+ this.setState({
495
+ previewUrl,
496
+ phases,
497
+ generation,
498
+ behaviorType: agentState.behaviorType,
499
+ projectType: agentState.projectType,
500
+ query: agentState.query,
501
+ shouldBeGenerating: agentState.shouldBeGenerating,
502
+ projectName: agentState.projectName
503
+ });
400
504
  break;
401
505
  }
402
506
  case "error": {
@@ -408,6 +512,38 @@ class SessionStateStore {
408
512
  break;
409
513
  }
410
514
  }
515
+ updateFileStatus(filePath, status) {
516
+ return this.state.phases.map((phase) => ({
517
+ ...phase,
518
+ files: phase.files.map((f) => f.path === filePath ? { ...f, status } : f)
519
+ }));
520
+ }
521
+ updateOrAddPhase(phaseInfo, status, phaseFiles) {
522
+ const phases = [...this.state.phases];
523
+ const existingIndex = phases.findIndex((p) => p.name === phaseInfo.name);
524
+ const files = (phaseFiles ?? []).map((f) => ({
525
+ path: f.path,
526
+ purpose: f.purpose,
527
+ status: status === "completed" ? "completed" : "generating"
528
+ }));
529
+ if (existingIndex >= 0) {
530
+ phases[existingIndex] = {
531
+ ...phases[existingIndex],
532
+ status,
533
+ description: phaseInfo.description ?? phases[existingIndex].description,
534
+ files: files.length > 0 ? files : phases[existingIndex].files
535
+ };
536
+ } else if (phaseInfo.name) {
537
+ phases.push({
538
+ id: `phase-${phases.length}`,
539
+ name: phaseInfo.name,
540
+ description: phaseInfo.description ?? "",
541
+ status,
542
+ files
543
+ });
544
+ }
545
+ return phases;
546
+ }
411
547
  setState(patch) {
412
548
  const prev = this.state;
413
549
  const next = { ...prev, ...patch };
@@ -804,6 +940,14 @@ class BuildSession {
804
940
  snapshot: () => this.workspace.snapshot(),
805
941
  tree: () => buildFileTree(this.workspace.paths())
806
942
  };
943
+ phases = {
944
+ list: () => this.state.get().phases,
945
+ current: () => this.state.get().phases.find((p) => p.status !== "completed" && p.status !== "cancelled"),
946
+ completed: () => this.state.get().phases.filter((p) => p.status === "completed"),
947
+ get: (id) => this.state.get().phases.find((p) => p.id === id),
948
+ count: () => this.state.get().phases.length,
949
+ allCompleted: () => this.state.get().phases.length > 0 && this.state.get().phases.every((p) => p.status === "completed")
950
+ };
807
951
  wait = {
808
952
  generationStarted: (options = {}) => this.waitForGenerationStarted(options),
809
953
  generationComplete: (options = {}) => this.waitForGenerationComplete(options),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cf-vibesdk/sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -22,7 +22,8 @@
22
22
  "bundle-types": "dts-bundle-generator --export-referenced-types false --no-check --project ./tsconfig.protocol.json -o ./dist/index.d.ts ./src/index.ts && bun run scripts/expand-drizzle-types.ts",
23
23
  "typecheck": "tsc -p ./tsconfig.json --noEmit",
24
24
  "test": "bun test test/*.test.ts",
25
- "test:integration": "bun test --timeout 600000 test/integration/*.test.ts"
25
+ "test:integration": "bun test --timeout 600000 test/integration/*.test.ts",
26
+ "package": "bun run typecheck && bun run test && bun run build && bun run bundle-types"
26
27
  },
27
28
  "devDependencies": {
28
29
  "@cloudflare/workers-types": "^4.20241218.0",