@sublang/playbook 16.0.0 → 17.0.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 (40) hide show
  1. package/README.md +2 -0
  2. package/docs/cli.md +36 -18
  3. package/docs/configuration.md +7 -5
  4. package/docs/embedding.md +26 -5
  5. package/package.json +4 -4
  6. package/reference/sdlc/captain.md +19 -10
  7. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +6 -2
  8. package/reference/sdlc/captain.playbook/captain.fsm.js +19 -7
  9. package/reference/sdlc/captain.playbook/captain.fsm.ts +25 -10
  10. package/reference/sdlc/captain.playbook/captain.gears.md +16 -8
  11. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +1 -1
  12. package/reference/sdlc/captain.playbook/captain.playbook.js +3 -2
  13. package/reference/sdlc/captain.playbook/captain.playbook.ts +4 -3
  14. package/reference/sdlc/code.playbook/bin/interactive-session.js +14 -1
  15. package/reference/sdlc/code.playbook/bin/portable-codec.js +3 -0
  16. package/reference/sdlc/code.playbook/bin/repository-effects.js +1 -0
  17. package/reference/sdlc/code.playbook/bin/run.js +128 -85
  18. package/reference/sdlc/code.playbook/bin/session-host.js +40 -8
  19. package/reference/sdlc/code.playbook/bin/session-store.js +162 -34
  20. package/reference/sdlc/code.playbook/code.fsm.d.ts +5 -5
  21. package/reference/sdlc/code.playbook/host-capabilities.d.ts +3 -0
  22. package/reference/sdlc/code.playbook/playbook-captain.d.ts +32 -2
  23. package/reference/sdlc/code.playbook/playbook-captain.js +729 -284
  24. package/reference/sdlc/code.playbook/playbook-captain.ts +771 -358
  25. package/reference/sdlc/code.playbook/session-host.d.ts +4 -1
  26. package/reference/sdlc/code.playbook/session-store.d.ts +17 -0
  27. package/reference/sdlc/code.playbook/session-store.js +1 -0
  28. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +5 -5
  29. package/reference/sdlc/dev.playbook/dev.fsm.d.ts +5 -5
  30. package/reference/sdlc/pr.playbook/pr.fsm.d.ts +5 -5
  31. package/slc/gears2fsm.md +5 -5
  32. package/slc/link.md +77 -33
  33. package/src/runtime.d.ts +46 -2
  34. package/src/runtime.ts +42 -2
  35. package/src/xstate-playbook-runtime.d.ts +9 -8
  36. package/src/xstate-playbook-runtime.js +471 -215
  37. package/src/xstate-playbook-runtime.ts +525 -238
  38. package/src/xstate-runtime.d.ts +3 -4
  39. package/src/xstate-runtime.js +52 -0
  40. package/src/xstate-runtime.ts +59 -4
package/src/runtime.ts CHANGED
@@ -492,6 +492,8 @@ export type PlaybookRunResult =
492
492
  };
493
493
 
494
494
  export interface PlaybookPorts {
495
+ /** Acknowledge the durable start before work and its result before transition. */
496
+ recordStep?(step: PlaybookStepRecord, position?: PlaybookRuntimeSnapshot): Promise<void>;
495
497
  callPlayer(
496
498
  roleId: string,
497
499
  prompt: string,
@@ -508,6 +510,7 @@ export interface PlaybookPorts {
508
510
  request: PlaybookCallRequest,
509
511
  signal: AbortSignal,
510
512
  ): Promise<PlaybookCallStart>;
513
+ /** Mark question presentation with data.kind = "boss-question"; Captain relays it from pendingQuestions. */
511
514
  emitStatus(message: string, data?: unknown): Promise<void>;
512
515
  emitTelemetry(event: { topic: string; payload: unknown }): Promise<void>;
513
516
  }
@@ -639,6 +642,8 @@ export interface PlaybookEffectBoundary {
639
642
  readonly baseline: PlaybookRepositoryObservation;
640
643
  readonly after?: PlaybookRepositoryObservation;
641
644
  readonly physicalReceipt?: PlaybookRepositoryReceipt;
645
+ /** A later exact restoration permits replay of this standalone read-only call. */
646
+ readonly restored?: PlaybookRepositoryObservation;
642
647
  readonly finalText?: string;
643
648
  readonly semanticCandidate?: JsonValue;
644
649
  readonly initialSemanticCandidate?: JsonValue;
@@ -655,6 +660,7 @@ export type PlaybookEffectBoundaryStart = Omit<
655
660
  | 'attemptNumber'
656
661
  | 'after'
657
662
  | 'physicalReceipt'
663
+ | 'restored'
658
664
  | 'finalText'
659
665
  | 'semanticCandidate'
660
666
  | 'initialSemanticCandidate'
@@ -774,9 +780,29 @@ export interface PlaybookRuntimeSnapshot {
774
780
  readonly boundaryPrefix: number;
775
781
  readonly attemptId: string | null;
776
782
  };
783
+ /** Interrupted invocation, captured before its external call (DR-069). */
784
+ recoveryCheckpoint?: PlaybookRecoveryCheckpoint;
777
785
  suspendedCall?: PlaybookSuspendedCall;
778
786
  }
779
787
 
788
+ export interface PlaybookRecoveryCheckpoint {
789
+ readonly id?: string;
790
+ readonly stateId: string;
791
+ readonly prompt: string;
792
+ readonly machine: JsonValue;
793
+ readonly boundaryPrefix: number;
794
+ /** Completed actor output supplied by the execution journal on restore. */
795
+ readonly result?: JsonValue;
796
+ readonly delivered?: true;
797
+ }
798
+
799
+ export interface PlaybookStepRecord {
800
+ readonly id: string;
801
+ readonly kind: 'player' | 'captain' | 'script';
802
+ readonly stateId: string;
803
+ readonly result?: JsonValue;
804
+ }
805
+
780
806
  // DR-063 §3: what running an advertised action would do. `ready` is the only
781
807
  // standing that can change anything; `no-op` runs and changes nothing; a
782
808
  // `blocked` action cannot run at all. An absent standing reads as `ready`, so
@@ -805,6 +831,16 @@ export interface PlaybookControlAction {
805
831
  // the Boss-appropriate grounding a host may speak from; the state id is
806
832
  // internal and is absent from it whenever the runtime's source declares
807
833
  // no description for the state it is in.
834
+ export interface PlaybookRecoveryOffer {
835
+ prompt: string;
836
+ description?: string;
837
+ /** Runtime-owned conditions to satisfy; these never authorize task completion. */
838
+ preparation?: string;
839
+ /** Saved observations and result choices for diagnosing the stopped step. */
840
+ evidence?: JsonValue;
841
+ continuation: { kind: 'reply' } | { kind: 'runtime'; actionId: string };
842
+ }
843
+
808
844
  export interface PlaybookControlView {
809
845
  state: PlaybookState;
810
846
  stateDescription?: string;
@@ -812,6 +848,8 @@ export interface PlaybookControlView {
812
848
  pendingQuestions: readonly PlaybookPendingBossQuestion[];
813
849
  lastError?: NormalizedError;
814
850
  actions: readonly PlaybookControlAction[];
851
+ /** Context only for an explicitly authorized Captain preparation call. */
852
+ recovery?: PlaybookRecoveryOffer;
815
853
  }
816
854
 
817
855
  // DR-029: the receipt `apply()` returns says which of three things
@@ -820,7 +858,7 @@ export interface PlaybookControlView {
820
858
  export type PlaybookControlReceipt =
821
859
  | { disposition: 'rejected'; reason: string }
822
860
  | { disposition: 'executed'; run: PlaybookRunResult }
823
- | { disposition: 'failed'; error: NormalizedError };
861
+ | { disposition: 'failed'; error: NormalizedError; run?: PlaybookRunResult };
824
862
 
825
863
  // DR-038 §2: link-authored metadata the Captain uses to decide whether a
826
864
  // quiescent generation is eligible for retention and whether a root terminal
@@ -838,7 +876,7 @@ export interface PlaybookRuntime {
838
876
  // a safe capture point (parked quiescence between public boundaries);
839
877
  // `restore` is an alternative to `init` that rehydrates the exported
840
878
  // snapshot under the same immutable session identity.
841
- exportSnapshot?(): PlaybookRuntimeSnapshot | undefined;
879
+ exportSnapshot?(checkpoint?: { child?: PlaybookPendingCall }): PlaybookRuntimeSnapshot | undefined;
842
880
  restore?(
843
881
  session: PlaybookSession,
844
882
  snapshot: PlaybookRuntimeSnapshot,
@@ -879,6 +917,8 @@ export interface PlaybookRuntime {
879
917
  handleBossInput(turn: {
880
918
  text: string;
881
919
  signal: AbortSignal;
920
+ /** Called synchronously just before accepting the event, at most once. */
921
+ onAccepted?: () => void;
882
922
  }): Promise<PlaybookRunResult>;
883
923
  resumePlaybookCall(input: {
884
924
  callId: string;
@@ -208,6 +208,13 @@ interface XStateRepositoryCohortResult<T> {
208
208
  readonly effectLedger: PlaybookEffectLedger;
209
209
  }
210
210
  export interface XStateRepositoryCapability {
211
+ observe?(): Promise<PlaybookRepositoryReceipt['baseline']>;
212
+ acquire?(options: {
213
+ readonly signal: AbortSignal;
214
+ }): Promise<{
215
+ assertOwner(): Promise<void>;
216
+ release(): Promise<void>;
217
+ }>;
211
218
  runExclusive<T>(options: {
212
219
  readonly signal: AbortSignal;
213
220
  readonly effectBoundary: XStateEffectBoundarySeed;
@@ -360,14 +367,7 @@ interface XStatePlaybookRuntimeSpecBase<TOptions> {
360
367
  entryEvent?: {
361
368
  type: string;
362
369
  textField: string;
363
- /**
364
- * DR-034: the FSM context member this machine's entry action copies the
365
- * exact Boss text into. Where it is named, the failure-state retry
366
- * builds its payload from that member of the live snapshot instead of
367
- * from the process-local recorded event, so the action derives the same
368
- * before and after `restore`. Absent: the recorded event stays the
369
- * source and the action lives only as long as the process.
370
- */
370
+ /** @deprecated Step checkpoints now carry the accepted input directly. */
371
371
  contextField?: string;
372
372
  };
373
373
  /**
@@ -516,6 +516,7 @@ interface PlayerBridgeSpec {
516
516
  adjudication: PlayerAdjudicationSpec;
517
517
  resumableStateIds: ReadonlySet<string>;
518
518
  allowsCorrectiveReplay?: (result: PlayerResult) => boolean;
519
+ run?: (input: PlaybookPlayerInput, signal: AbortSignal, execute: () => Promise<PlaybookActorOutput>) => Promise<PlaybookActorOutput>;
519
520
  }
520
521
  export declare function createPlayerBridge(spec: PlayerBridgeSpec, ports: PlaybookPorts, getActiveSignal?: () => AbortSignal | undefined, boundary?: RuntimeBoundaryCalls, onControlPlaneError?: (error: unknown) => void): PromiseActorLogic<PlaybookActorOutput, PlaybookPlayerInput>;
521
522
  /**