@cf-vibesdk/sdk 0.0.8 → 0.0.9

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
@@ -250,6 +250,24 @@ if (session.phases.allCompleted()) {
250
250
 
251
251
  // Get phase by ID
252
252
  const phase = session.phases.get('phase-0');
253
+
254
+ // Subscribe to phase changes
255
+ const unsubscribe = session.phases.onChange((event) => {
256
+ console.log(`Phase ${event.type}:`, event.phase.name);
257
+ console.log(`Status: ${event.phase.status}`);
258
+ console.log(`Total phases: ${event.allPhases.length}`);
259
+ });
260
+ // Later: unsubscribe();
261
+ ```
262
+
263
+ The `onChange` callback receives a `PhaseTimelineEvent`:
264
+
265
+ ```ts
266
+ type PhaseTimelineEvent = {
267
+ type: 'added' | 'updated'; // New phase vs status/file change
268
+ phase: PhaseInfo; // The affected phase
269
+ allPhases: PhaseInfo[]; // All phases after this change
270
+ };
253
271
  ```
254
272
 
255
273
  Each phase contains:
@@ -403,6 +421,8 @@ import type {
403
421
  PhaseStatus,
404
422
  PhaseFileStatus,
405
423
  PhaseEventType,
424
+ PhaseTimelineEvent,
425
+ PhaseTimelineChangeType,
406
426
 
407
427
  // API
408
428
  ApiResponse,
package/dist/index.d.ts CHANGED
@@ -5568,6 +5568,8 @@ export type AgentEventMap = {
5568
5568
  error: {
5569
5569
  error: string;
5570
5570
  };
5571
+ /** Emitted when the phase timeline changes (phase added or updated). */
5572
+ phases: PhaseTimelineEvent;
5571
5573
  };
5572
5574
  /**
5573
5575
  * URL provider for WebSocket connections.
@@ -5657,6 +5659,18 @@ export type SessionPhases = {
5657
5659
  /** Check if all phases are completed. */
5658
5660
  allCompleted: () => boolean;
5659
5661
  };
5662
+ /**
5663
+ * Event emitted when the phase timeline changes.
5664
+ */
5665
+ export type PhaseTimelineChangeType = "added" | "updated";
5666
+ export type PhaseTimelineEvent = {
5667
+ /** Type of change: 'added' for new phase, 'updated' for status/file changes. */
5668
+ type: PhaseTimelineChangeType;
5669
+ /** The phase that was added or updated. */
5670
+ phase: PhaseInfo;
5671
+ /** All phases in the timeline after this change. */
5672
+ allPhases: PhaseInfo[];
5673
+ };
5660
5674
  export type SessionDeployable = {
5661
5675
  files: number;
5662
5676
  reason: "generation_complete" | "phase_validated";
@@ -5759,6 +5773,11 @@ export declare class SessionStateStore {
5759
5773
  private emitter;
5760
5774
  get(): SessionState;
5761
5775
  onChange(cb: (next: SessionState, prev: SessionState) => void): () => void;
5776
+ /**
5777
+ * Subscribe to phase timeline changes.
5778
+ * Fires when a phase is added or when a phase's status/files change.
5779
+ */
5780
+ onPhaseChange(cb: (event: PhaseTimelineEvent) => void): () => void;
5762
5781
  setConnection(state: ConnectionState): void;
5763
5782
  applyWsMessage(msg: AgentWsServerMessage): void;
5764
5783
  /**
@@ -5770,6 +5789,14 @@ export declare class SessionStateStore {
5770
5789
  */
5771
5790
  private updateOrAddPhase;
5772
5791
  private setState;
5792
+ /**
5793
+ * Compare old and new phases arrays and emit change events.
5794
+ */
5795
+ private emitPhaseChanges;
5796
+ /**
5797
+ * Check if a phase has meaningfully changed (status or file statuses).
5798
+ */
5799
+ private hasPhaseChanged;
5773
5800
  clear(): void;
5774
5801
  }
5775
5802
  type WorkspaceChange = {
@@ -5856,6 +5883,12 @@ export declare class BuildSession {
5856
5883
  count: () => number;
5857
5884
  /** Check if all phases are completed. */
5858
5885
  allCompleted: () => boolean;
5886
+ /**
5887
+ * Subscribe to phase timeline changes.
5888
+ * Fires when a phase is added or when a phase's status/files change.
5889
+ * @returns Unsubscribe function.
5890
+ */
5891
+ onChange: (cb: (event: PhaseTimelineEvent) => void) => (() => void);
5859
5892
  };
5860
5893
  readonly wait: {
5861
5894
  generationStarted: (options?: WaitOptions) => Promise<{
package/dist/index.js CHANGED
@@ -292,6 +292,9 @@ class SessionStateStore {
292
292
  onChange(cb) {
293
293
  return this.emitter.on("change", ({ prev, next }) => cb(next, prev));
294
294
  }
295
+ onPhaseChange(cb) {
296
+ return this.emitter.on("phaseChange", cb);
297
+ }
295
298
  setConnection(state) {
296
299
  this.setState({ connection: state });
297
300
  }
@@ -524,7 +527,7 @@ class SessionStateStore {
524
527
  const files = (phaseFiles ?? []).map((f) => ({
525
528
  path: f.path,
526
529
  purpose: f.purpose,
527
- status: status === "completed" ? "completed" : "generating"
530
+ status: status === "completed" ? "completed" : "pending"
528
531
  }));
529
532
  if (existingIndex >= 0) {
530
533
  phases[existingIndex] = {
@@ -549,6 +552,38 @@ class SessionStateStore {
549
552
  const next = { ...prev, ...patch };
550
553
  this.state = next;
551
554
  this.emitter.emit("change", { prev, next });
555
+ if (patch.phases && patch.phases !== prev.phases) {
556
+ this.emitPhaseChanges(prev.phases, patch.phases);
557
+ }
558
+ }
559
+ emitPhaseChanges(prevPhases, nextPhases) {
560
+ for (const phase of nextPhases) {
561
+ const prevPhase = prevPhases.find((p) => p.id === phase.id);
562
+ if (!prevPhase) {
563
+ this.emitter.emit("phaseChange", {
564
+ type: "added",
565
+ phase,
566
+ allPhases: nextPhases
567
+ });
568
+ } else if (this.hasPhaseChanged(prevPhase, phase)) {
569
+ this.emitter.emit("phaseChange", {
570
+ type: "updated",
571
+ phase,
572
+ allPhases: nextPhases
573
+ });
574
+ }
575
+ }
576
+ }
577
+ hasPhaseChanged(prev, next) {
578
+ if (prev.status !== next.status)
579
+ return true;
580
+ if (prev.files.length !== next.files.length)
581
+ return true;
582
+ for (let i = 0;i < prev.files.length; i++) {
583
+ if (prev.files[i].status !== next.files[i].status)
584
+ return true;
585
+ }
586
+ return false;
552
587
  }
553
588
  clear() {
554
589
  this.state = INITIAL_STATE;
@@ -946,7 +981,8 @@ class BuildSession {
946
981
  completed: () => this.state.get().phases.filter((p) => p.status === "completed"),
947
982
  get: (id) => this.state.get().phases.find((p) => p.id === id),
948
983
  count: () => this.state.get().phases.length,
949
- allCompleted: () => this.state.get().phases.length > 0 && this.state.get().phases.every((p) => p.status === "completed")
984
+ allCompleted: () => this.state.get().phases.length > 0 && this.state.get().phases.every((p) => p.status === "completed"),
985
+ onChange: (cb) => this.state.onPhaseChange(cb)
950
986
  };
951
987
  wait = {
952
988
  generationStarted: (options = {}) => this.waitForGenerationStarted(options),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cf-vibesdk/sdk",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {