@sublang/playbook 1.0.0 → 2.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.
- package/README.md +8 -1
- package/package.json +6 -2
- package/reference/sdlc/captain.playbook/captain.fsm.d.ts +1 -1
- package/reference/sdlc/code.playbook/bin/playbook.js +6 -2
- package/reference/sdlc/code.playbook/bin/run.js +110 -4
- package/reference/sdlc/code.playbook/code.playbook.d.ts +4 -16
- package/reference/sdlc/code.playbook/code.playbook.js +88 -1272
- package/reference/sdlc/code.playbook/code.playbook.ts +127 -1547
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +11 -0
- package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +1 -1
- package/slc/link.md +122 -47
- package/slc/optimize.md +7 -3
- package/slc/text2gears.md +8 -2
- package/src/runtime.d.ts +1 -0
- package/src/runtime.ts +1 -0
- package/src/xstate-playbook-runtime.d.ts +201 -0
- package/src/xstate-playbook-runtime.js +2099 -0
- package/src/xstate-playbook-runtime.ts +2849 -0
- package/src/xstate-runtime.d.ts +1 -0
- package/src/xstate-runtime.js +11 -0
- package/src/xstate-runtime.ts +14 -0
|
@@ -66,3 +66,14 @@ playbooks:
|
|
|
66
66
|
# host: claude-opus
|
|
67
67
|
# participant: codex-gpt
|
|
68
68
|
# committer: host
|
|
69
|
+
|
|
70
|
+
# Non-interactive `playbook run` defaults (optional). Each value is an
|
|
71
|
+
# agent string <adapter>[:<model>][@<effort>]; `player` is the catch-all
|
|
72
|
+
# for any required role without its own `players.<role>` entry, across
|
|
73
|
+
# every playbook. Flags override per role (--player / --captain), and
|
|
74
|
+
# `playbook run resume` keeps the lineup stored with the parked session.
|
|
75
|
+
#run:
|
|
76
|
+
# captain: claude:claude-opus-4-8@high
|
|
77
|
+
# player: claude:claude-opus-4-8@high
|
|
78
|
+
# players:
|
|
79
|
+
# coder: claude:claude-opus-4-8[1m]@xhigh
|
|
@@ -266,7 +266,7 @@ export declare const discussMachine: import("xstate").StateMachine<DiscussContex
|
|
|
266
266
|
} | {
|
|
267
267
|
type: "mixedReview";
|
|
268
268
|
params: unknown;
|
|
269
|
-
}, never, "done" | "failed" | "
|
|
269
|
+
}, never, "done" | "failed" | "awaitBossReply" | "ready" | "hostWritesAgreement" | "commitInitialChanges" | "reviewSpecInitialCommit" | "reviewSpecHostChanges" | "reviewDrInitialCommit" | "reviewDrHostChanges" | "reviewMixedInitialCommit" | "reviewMixedHostChanges" | "hostAddressesFindings" | "participantAddressesRebuttals" | "commitReviewedChanges" | {
|
|
270
270
|
initialProposalRound: {
|
|
271
271
|
host: "working" | "waiting" | "complete";
|
|
272
272
|
participant: "working" | "waiting" | "complete";
|
package/slc/link.md
CHANGED
|
@@ -257,15 +257,21 @@ array requests a tool-free call, while omission preserves the host Captain's
|
|
|
257
257
|
configured tools.
|
|
258
258
|
`CaptainResult` carries no resume token or player-continuation selection.
|
|
259
259
|
A non-`ok`
|
|
260
|
-
result, or an `ok` result without `finalText`, shall
|
|
261
|
-
the
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
drive it to quiescence,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
260
|
+
result, or an `ok` result without non-empty `finalText`, shall record that
|
|
261
|
+
failure on the call's single finish trace and reject the actor through the
|
|
262
|
+
FSM's error path. These structured host-result failures are recoverable
|
|
263
|
+
workflow failures, not control-plane failures: the runtime shall let the actor
|
|
264
|
+
take `onError`, drive it to quiescence, drain ordered emissions, and resolve
|
|
265
|
+
the public method with `{ outcome: 'failed' }` carrying the failure state's
|
|
266
|
+
error. This matches the delegated-player result boundary.
|
|
267
|
+
A non-abort thrown `callCaptain` port, a malformed host result, and a rejecting
|
|
268
|
+
trace sink remain control-plane failures that reject the public method. If the
|
|
269
|
+
required finish sink rejects after a structured host-result failure, the
|
|
270
|
+
actor's error and failure-state evidence shall remain the host-result failure,
|
|
271
|
+
while the public method rejects with the sink failure surfaced by the turn's
|
|
272
|
+
emission drain. Absent such a control-plane failure, if the combined signal
|
|
273
|
+
has aborted, ordinary abort settlement remains authoritative after the actor
|
|
274
|
+
reaches its error path.
|
|
269
275
|
|
|
270
276
|
Every linked runtime owns a map from resolved player id to its latest non-empty `resumeToken`.
|
|
271
277
|
Before reading a resolved direct-Captain or delegated-player result, the
|
|
@@ -753,8 +759,11 @@ Adjudicator failures are control-plane errors.
|
|
|
753
759
|
The runtime shall propagate them by throwing out of `handleBossInput` after attempting cleanup.
|
|
754
760
|
The host adapter surfaces the throw on its control-plane channel (cligent surfaces such throws as `runtime_error` per [TMUX-025](https://github.com/sublang-ai/cligent/blob/main/specs/user/tmux-play.md#tmux-025)).
|
|
755
761
|
The host's player-result channels (`player_finished` and equivalents) are reserved for failures the player itself produced; the host emits them when `callPlayer` resolves with `status !== 'ok'`.
|
|
756
|
-
Captain
|
|
757
|
-
reported as player failures
|
|
762
|
+
Direct-Captain host-result failures stay on the Captain actor boundary and
|
|
763
|
+
shall not be reported as player failures; they follow the recoverable FSM
|
|
764
|
+
failure path specified above. Captain transport, result-shape, trace-sink, and
|
|
765
|
+
adjudication failures remain control-plane errors unless the transport failure
|
|
766
|
+
is causally identical to the active abort signal.
|
|
758
767
|
Because XState still needs the invoked promise to settle, the linked runtime
|
|
759
768
|
shall latch an adjudicator, actor-output JSON-validation, or nested-boundary
|
|
760
769
|
control error outside machine context, allow the invocation's `onError` path to
|
|
@@ -779,8 +788,9 @@ independent cleanup evidence.
|
|
|
779
788
|
## Script execution
|
|
780
789
|
|
|
781
790
|
Where the FSM declares the typed `script` actor from
|
|
782
|
-
[gears2fsm "Setup"](gears2fsm.md#setup), the
|
|
783
|
-
implementation
|
|
791
|
+
[gears2fsm "Setup"](gears2fsm.md#setup), the linked runtime shall provide
|
|
792
|
+
its implementation through the shared factory (§Output); the linker shall
|
|
793
|
+
not regenerate a script executor inside each emitted module.
|
|
784
794
|
A script invocation is the one actor kind that runs without any agent:
|
|
785
795
|
it makes no `callPlayer`, `callCaptain`, or `callJudge` call and needs no
|
|
786
796
|
adjudication.
|
|
@@ -791,7 +801,8 @@ The provided actor shall:
|
|
|
791
801
|
(`sh -c`), with the working directory taken from the emitted
|
|
792
802
|
`PlaybookRuntimeOptions.cwd` when the caller supplies it, else the process
|
|
793
803
|
working directory. The linker shall declare the optional `cwd` option on the
|
|
794
|
-
emitted options interface whenever the FSM contains a script state
|
|
804
|
+
emitted options interface whenever the FSM contains a script state; the
|
|
805
|
+
validated option reaches the shared script actor through the spec.
|
|
795
806
|
- Resolve deterministically from the child's exit status: status zero resolves
|
|
796
807
|
`{ guard: <first declared guard>, exitStatus: 0 }`; any nonzero status
|
|
797
808
|
resolves the second declared guard with that status. Guard selection is
|
|
@@ -816,9 +827,9 @@ them in machine context, prompts, or trace payloads.
|
|
|
816
827
|
|
|
817
828
|
Where the FSM declares the typed `playbook` actor from
|
|
818
829
|
[gears2fsm](gears2fsm.md#nested-playbook-calls), the linked runtime shall provide
|
|
819
|
-
it with the shared `createNestedPlaybookBridge(...).actorLogic
|
|
820
|
-
regenerate a second pending-call,
|
|
821
|
-
substrate inside each linked artifact.
|
|
830
|
+
it with the shared `createNestedPlaybookBridge(...).actorLogic` — wired by the
|
|
831
|
+
shared factory per §Output — and shall not regenerate a second pending-call,
|
|
832
|
+
identity-validation, or abort-cleanup substrate inside each linked artifact.
|
|
822
833
|
Instantiate the generic bridge with the FSM-exported `PlaybookInput` type so
|
|
823
834
|
XState `.provide(...)` receives the exact declared actor input rather than a
|
|
824
835
|
structurally similar local type.
|
|
@@ -1122,7 +1133,9 @@ At a safe capture point it shall return a JSON-safe
|
|
|
1122
1133
|
- `playerResumeTokens`: the resume-token map as a plain object
|
|
1123
1134
|
(§PlaybookPorts contract).
|
|
1124
1135
|
- `sequences`: the live `trace`, `turn`, `judgeCall`, `playerCall`, and
|
|
1125
|
-
`playbookCall` counters
|
|
1136
|
+
`playbookCall` counters, plus `captainCall` when the runtime supports direct
|
|
1137
|
+
Captain calls. `captainCall` remains optional under schema version `1` for
|
|
1138
|
+
backward compatibility; a direct-Captain-capable runtime shall persist it.
|
|
1126
1139
|
- `state`: the current normalized state descriptor.
|
|
1127
1140
|
- `pendingBossQuestions`: the pending Boss question(s) from FSM context as
|
|
1128
1141
|
a list of `{ questionId, player, question, sourceItem? }`, empty when the
|
|
@@ -1141,7 +1154,9 @@ module identity — that the factory constructing this runtime still
|
|
|
1141
1154
|
belongs to the snapshot's playbook — is likewise the host's check to
|
|
1142
1155
|
make before calling `restore`.
|
|
1143
1156
|
`restore` shall bind the session, restore the resume-token map, the
|
|
1144
|
-
sequence counters
|
|
1157
|
+
sequence counters (using the persisted global `trace` counter as a
|
|
1158
|
+
collision-safe floor for an absent legacy `captainCall`), and the
|
|
1159
|
+
prior-state descriptor from the snapshot,
|
|
1145
1160
|
construct the actor with the persisted `machine` snapshot, and start it
|
|
1146
1161
|
with root inspection emissions suppressed so rehydration emits no
|
|
1147
1162
|
`session.started` trace, no transition trace, and no human status — the
|
|
@@ -1227,7 +1242,19 @@ The `playbook.trace` copies are the host-agnostic runtime-boundary record requir
|
|
|
1227
1242
|
|
|
1228
1243
|
## Output
|
|
1229
1244
|
|
|
1230
|
-
The link compiler emits **one** TypeScript module
|
|
1245
|
+
The link compiler emits **one thin** TypeScript module per playbook.
|
|
1246
|
+
The FSM-interpreter machinery — actor wiring, boundary tracing, Boss-event
|
|
1247
|
+
mapping, adjudication, script execution, nested-playbook bridging, session
|
|
1248
|
+
lifecycle, abort handling, and the optional parked-session snapshot
|
|
1249
|
+
capability — is not regenerated per artifact: it ships once as the shared
|
|
1250
|
+
`createXStatePlaybookRuntime(machine, spec)` factory exported by
|
|
1251
|
+
`@sublang/playbook/xstate-runtime`, and the emitted module hands its FSM and
|
|
1252
|
+
a small per-playbook `spec` to that factory. Every behavioral section of
|
|
1253
|
+
this definition still binds the emitted module's runtime; the shared factory
|
|
1254
|
+
is how the emitted module satisfies them, so a runtime fix ships as a
|
|
1255
|
+
package release instead of a re-link of every artifact.
|
|
1256
|
+
|
|
1257
|
+
The emitted module:
|
|
1231
1258
|
|
|
1232
1259
|
- Imports the FSM artifact by relative path with an extension-bearing
|
|
1233
1260
|
runtime specifier. When the linked TypeScript is part of a package that
|
|
@@ -1240,34 +1267,82 @@ The link compiler emits **one** TypeScript module that:
|
|
|
1240
1267
|
that strip cleanly, no constructor parameter properties, `enum`s, or
|
|
1241
1268
|
namespaces — so a host running under type stripping loads it
|
|
1242
1269
|
directly.
|
|
1243
|
-
- Imports
|
|
1244
|
-
`
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
`
|
|
1255
|
-
`
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1270
|
+
- Imports `createXStatePlaybookRuntime` (plus any shared strategy defaults
|
|
1271
|
+
its `_internal` surface re-exports) from the shared engine module through
|
|
1272
|
+
its bare package specifier `@sublang/playbook/xstate-runtime`, and the
|
|
1273
|
+
contract types through `@sublang/playbook/runtime`. It shall not copy,
|
|
1274
|
+
inline, or re-derive interpreter machinery — actor bridges, trace
|
|
1275
|
+
emission, judge-JSON recovery, lifecycle guards — beside the factory
|
|
1276
|
+
call, and shall not import `xstate`, `p-queue`, or `node:child_process`
|
|
1277
|
+
itself; those are the shared engine's dependencies.
|
|
1278
|
+
- Declares and exports the typed `PlaybookRuntimeOptions` interface for that
|
|
1279
|
+
playbook, derived from every required FSM input field that is not supplied
|
|
1280
|
+
by `PlaybookSession` or another linker-owned source (§PlaybookRuntime
|
|
1281
|
+
contract), plus the optional `cwd` option whenever the FSM contains a
|
|
1282
|
+
`script` state (§Script execution).
|
|
1283
|
+
- Supplies the spec's `snapshotOptions` with the same options-validation
|
|
1284
|
+
semantics previously generated inline: validate and JSON-snapshot the
|
|
1285
|
+
caller's options, rejecting undeclared keys and non-conforming values, so
|
|
1286
|
+
the factory binds an immutable options record before constructing any
|
|
1287
|
+
actor.
|
|
1288
|
+
- Supplies in `spec` only what the factory cannot read from the FSM
|
|
1289
|
+
artifact's own data: the deterministic textual entry event where
|
|
1290
|
+
§Boss-event mapping prescribes deterministic entry; compact `bossEvents`
|
|
1291
|
+
metadata for each additional Boss-union arm whose exact required/optional
|
|
1292
|
+
judge fields, runtime-owned text fields, or closed string values disappear
|
|
1293
|
+
under TypeScript erasure; `placeholderFields` only for authored token/field
|
|
1294
|
+
exceptions not covered by the canonical kebab-token-to-camel-field mapping
|
|
1295
|
+
and the canonical `<#>` → `irNumber` special case; the
|
|
1296
|
+
transition-event payload fields the FSM's Boss union declares; a
|
|
1297
|
+
non-default player binding where the linker inputs supplied one; and any
|
|
1298
|
+
per-playbook strategy override (classifier, prompt composers,
|
|
1299
|
+
required-field extraction, status formatting) an earlier section of this
|
|
1300
|
+
definition requires for that playbook. The metadata shall keep the shared
|
|
1301
|
+
classifier's reply contract exactly flat `{ type, ...declaredFields }` and
|
|
1302
|
+
distinguish judge-authored routing fields from exact-text fields the
|
|
1303
|
+
runtime attaches itself. Everything else — player/script/captain/nested actor
|
|
1304
|
+
provisioning, prompt composition, classification, adjudication, statuses,
|
|
1305
|
+
resumable-state derivation — comes from the factory's generic defaults,
|
|
1306
|
+
which implement the behavioral sections of this definition.
|
|
1307
|
+
|
|
1308
|
+
```ts
|
|
1309
|
+
interface XStateBossEventFieldSpec {
|
|
1310
|
+
source: 'judge' | 'text';
|
|
1311
|
+
required?: boolean;
|
|
1312
|
+
values?: readonly string[];
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
interface XStateBossEventSpec {
|
|
1316
|
+
type: string;
|
|
1317
|
+
fields?: Readonly<Record<string, XStateBossEventFieldSpec>>;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
bossEvents?: readonly XStateBossEventSpec[];
|
|
1321
|
+
placeholderFields?: Readonly<Record<string, string>>;
|
|
1322
|
+
```
|
|
1323
|
+
|
|
1324
|
+
Supplied `bossEvents` metadata shall merge with, and shall not replace or
|
|
1325
|
+
weaken, runtime-derived entry text ownership or closed interrupt targets.
|
|
1326
|
+
A conflicting duplicate field contract is a linker/runtime construction
|
|
1327
|
+
error.
|
|
1328
|
+
`NO_ACTION` and `BOSS_REPLY` are runtime-owned event types the factory
|
|
1329
|
+
supplies itself — `NO_ACTION` as exactly `{ type: 'NO_ACTION' }`, and
|
|
1330
|
+
`BOSS_REPLY` as an optional judge-selected `questionId` plus the exact-text
|
|
1331
|
+
`answer` the runtime attaches. `bossEvents` shall carry no entry for either
|
|
1332
|
+
type; supplying one is a construction error, so a linker that judges a
|
|
1333
|
+
runtime-owned arm to have lost payload detail under erasure shall report
|
|
1334
|
+
that gap rather than emit the entry.
|
|
1335
|
+
- Default-exports the factory call as `createPlaybookRuntime`, typed
|
|
1336
|
+
`PlaybookRuntimeFactory<PlaybookRuntimeOptions>`.
|
|
1262
1337
|
- Exposes, under an `_internal` export, the pure helpers verification
|
|
1263
1338
|
needs — at least the player-prompt and Captain-prompt composers
|
|
1264
|
-
(`composePlayerPrompt` and `composeCaptainPrompt`)
|
|
1339
|
+
(`composePlayerPrompt` and `composeCaptainPrompt`), which may re-export
|
|
1340
|
+
the shared defaults when the spec does not override composition — so
|
|
1265
1341
|
compilation-correctness tests can exercise composition without a host.
|
|
1266
1342
|
- Holds no host-specific types and no host primitive calls. The runtime
|
|
1267
|
-
speaks only `PlaybookPorts` for every agent and host concern; the
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
locally.
|
|
1343
|
+
speaks only `PlaybookPorts` for every agent and host concern; the
|
|
1344
|
+
`node:child_process` dependency of §Script execution lives in the shared
|
|
1345
|
+
factory, not in the emitted module.
|
|
1271
1346
|
- Records the linker inputs (FSM path, player binding, strategies) in a
|
|
1272
1347
|
top-of-file header comment so the file is reproducible from the same
|
|
1273
1348
|
inputs.
|
|
@@ -1276,12 +1351,12 @@ The link compiler emits **one** TypeScript module that:
|
|
|
1276
1351
|
`PlaybookTraceEvent`,
|
|
1277
1352
|
`PlaybookCallRequest`, `PlaybookCallResult`, `PlaybookCallStart`,
|
|
1278
1353
|
`PlaybookStateValue`, `PlaybookState`, `PlaybookRunResult`,
|
|
1279
|
-
`PlaybookRuntime`, `PlaybookRuntimeFactory`) from
|
|
1354
|
+
`PlaybookRuntime`, `PlaybookRuntimeFactory`) from the single shared
|
|
1280
1355
|
type-only module instead of redefining them, and re-exports the names
|
|
1281
1356
|
its consumers import, so every linked playbook shares one contract
|
|
1282
|
-
definition. The shared
|
|
1357
|
+
definition. The shared modules import no FSM or host types, so the
|
|
1283
1358
|
dependency runs one way — from each linked module to the shared
|
|
1284
|
-
contract, never the reverse.
|
|
1359
|
+
engine and contract, never the reverse.
|
|
1285
1360
|
|
|
1286
1361
|
When a co-located integration test for the linked runtime already exists, the
|
|
1287
1362
|
link compiler shall run it before reporting success and treat any failure as a
|
package/slc/optimize.md
CHANGED
|
@@ -56,9 +56,9 @@ For each eligible item, the pass shall:
|
|
|
56
56
|
|
|
57
57
|
- Keep the item's ID, heading form, and condition text unchanged.
|
|
58
58
|
- Replace the acting clause with the literal script form `Captain shall run:`.
|
|
59
|
-
Like guard names, the script clause is fixed machine syntax and
|
|
60
|
-
this exact English form even when the surrounding item text is
|
|
61
|
-
language.
|
|
59
|
+
Like guard names, the script clause is fixed machine syntax [[1]] and
|
|
60
|
+
stays in this exact English form even when the surrounding item text is
|
|
61
|
+
in another language.
|
|
62
62
|
- Replace the blockquoted prompt with the exact POSIX shell script that
|
|
63
63
|
performs the behavior, static text only.
|
|
64
64
|
- Emit exactly two `Results:` bullets per
|
|
@@ -86,3 +86,7 @@ with no `## Optimizations` section.
|
|
|
86
86
|
- Reordering, merging, splitting, or deleting items.
|
|
87
87
|
- Any change to prompts, conditions, or result contracts of items the pass
|
|
88
88
|
does not rewrite.
|
|
89
|
+
|
|
90
|
+
## References
|
|
91
|
+
|
|
92
|
+
[1]: GEARS definition shipped by the installed `@sublang/spex` package: `@sublang/spex/scaffold/specs/meta.md` (English) and `@sublang/spex/scaffold/i18n/zh/specs/meta.md` (Chinese); canonical renditions [GEARS: AI-Ready Spec Syntax](https://sublang.ai/ref/gears-ai-ready-spec-syntax) (en) and [GEARS:面向 AI 的规约语法](https://sublang.ai/zh/ref/gears-ai-ready-spec-syntax) (zh)
|
package/slc/text2gears.md
CHANGED
|
@@ -287,7 +287,13 @@ Results:
|
|
|
287
287
|
- `failed`: The command exited with a nonzero status.
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
-
Target
|
|
290
|
+
Target shall be written in the same language as Source: an item's condition
|
|
291
|
+
prose, acting prompts, and result descriptions follow the Source language,
|
|
292
|
+
read per the matching localization of the GEARS definition [[1]].
|
|
293
|
+
The four `Captain shall` acting-clause forms defined above (direct,
|
|
294
|
+
delegated, nested playbook call, and script), guard names, and the
|
|
295
|
+
`Players:` and `Results:` labels are fixed machine syntax and stay in this
|
|
296
|
+
exact English form regardless of Source language.
|
|
291
297
|
|
|
292
298
|
## Transformation-spec sources
|
|
293
299
|
|
|
@@ -324,4 +330,4 @@ Dead branches mislead readers and downstream phases.
|
|
|
324
330
|
|
|
325
331
|
## References
|
|
326
332
|
|
|
327
|
-
[1]:
|
|
333
|
+
[1]: GEARS definition shipped by the installed `@sublang/spex` package: `@sublang/spex/scaffold/specs/meta.md` (English) and `@sublang/spex/scaffold/i18n/zh/specs/meta.md` (Chinese); canonical renditions [GEARS: AI-Ready Spec Syntax](https://sublang.ai/ref/gears-ai-ready-spec-syntax) (en) and [GEARS:面向 AI 的规约语法](https://sublang.ai/zh/ref/gears-ai-ready-spec-syntax) (zh)
|
package/src/runtime.d.ts
CHANGED
package/src/runtime.ts
CHANGED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type { AnyStateMachine, EventObject, PromiseActorLogic } from 'xstate';
|
|
2
|
+
import type { CaptainResult, JsonValue, PlaybookPorts, PlaybookRuntimeFactory, PlaybookSession, PlaybookState, PlayerResult } from './runtime.js';
|
|
3
|
+
export interface PlaybookPendingBossQuestionContext {
|
|
4
|
+
questionId: string;
|
|
5
|
+
resumeStateId: string;
|
|
6
|
+
sourceItem: string;
|
|
7
|
+
player: string;
|
|
8
|
+
question: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PlaybookPlayerInput {
|
|
11
|
+
stateId: string;
|
|
12
|
+
player: string;
|
|
13
|
+
sourceItem: string;
|
|
14
|
+
prompt: string;
|
|
15
|
+
result: Readonly<Record<string, string>>;
|
|
16
|
+
pendingBossQuestion?: {
|
|
17
|
+
readonly question: string;
|
|
18
|
+
};
|
|
19
|
+
bossReply?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PlaybookCaptainInput {
|
|
22
|
+
stateId: string;
|
|
23
|
+
sourceItem: string;
|
|
24
|
+
prompt: string;
|
|
25
|
+
result: Readonly<Record<string, string>>;
|
|
26
|
+
allowedTools?: readonly string[];
|
|
27
|
+
pendingBossQuestion?: {
|
|
28
|
+
readonly question: string;
|
|
29
|
+
};
|
|
30
|
+
bossReply?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface PlaybookScriptInput {
|
|
33
|
+
stateId: string;
|
|
34
|
+
sourceItem: string;
|
|
35
|
+
command: string;
|
|
36
|
+
result: Readonly<Record<string, string>>;
|
|
37
|
+
}
|
|
38
|
+
/** Adjudicated actor output: the selected guard plus payload fields. */
|
|
39
|
+
export type PlaybookActorOutput = Record<string, unknown> & {
|
|
40
|
+
guard: string;
|
|
41
|
+
};
|
|
42
|
+
export type JudgePurpose = 'boss-input-classification' | 'player-output-adjudication' | 'captain-output-adjudication';
|
|
43
|
+
/**
|
|
44
|
+
* Traced runtime boundary used by the provided actors. The factory's runtime
|
|
45
|
+
* implements it; standalone helpers accept it optionally so verification can
|
|
46
|
+
* exercise composition/adjudication without a live runtime.
|
|
47
|
+
*/
|
|
48
|
+
export interface RuntimeBoundaryCalls {
|
|
49
|
+
callPlayer(input: PlaybookPlayerInput, playerId: string, prompt: string, signal: AbortSignal): Promise<PlayerResult>;
|
|
50
|
+
callJudge(purpose: JudgePurpose, stateId: string | undefined, prompt: string, signal: AbortSignal): Promise<string>;
|
|
51
|
+
callCaptain?(input: PlaybookCaptainInput, prompt: string, signal: AbortSignal): Promise<CaptainResult>;
|
|
52
|
+
}
|
|
53
|
+
export interface ScheduledStatus {
|
|
54
|
+
message: string;
|
|
55
|
+
data?: JsonValue;
|
|
56
|
+
}
|
|
57
|
+
export interface XStateBossEventFieldSpec {
|
|
58
|
+
/** The judge supplies routing data; the runtime supplies exact Boss text. */
|
|
59
|
+
source: 'judge' | 'text';
|
|
60
|
+
/** Judge-authored fields are optional unless explicitly required. */
|
|
61
|
+
required?: boolean;
|
|
62
|
+
/** Optional closed set for a string-valued judge field. */
|
|
63
|
+
values?: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
export interface XStateBossEventSpec {
|
|
66
|
+
type: string;
|
|
67
|
+
fields?: Readonly<Record<string, XStateBossEventFieldSpec>>;
|
|
68
|
+
}
|
|
69
|
+
export declare const BOSS_REPLY_ERRORS: {
|
|
70
|
+
readonly missingQuestion: "needsBossReply outcome missing 'question' field";
|
|
71
|
+
readonly unregisteredState: (stateId: string) => string;
|
|
72
|
+
};
|
|
73
|
+
export interface XStatePlaybookRuntimeSpec<TOptions> {
|
|
74
|
+
/** Diagnostic label used in internal invariant errors. Default 'playbook'. */
|
|
75
|
+
label?: string;
|
|
76
|
+
/** Validate and JSON-snapshot the caller's per-run options. */
|
|
77
|
+
snapshotOptions: (value: unknown) => TOptions;
|
|
78
|
+
/** Derive the FSM machine input from validated options. Default: identity. */
|
|
79
|
+
machineInput?: (options: TOptions, session: PlaybookSession) => unknown;
|
|
80
|
+
/**
|
|
81
|
+
* Deterministic textual entry event (slc/link.md §Boss-event mapping):
|
|
82
|
+
* where the ready or reconstructed terminal machine accepts exactly one
|
|
83
|
+
* ordinary textual entry event, send it without a judge call, carrying the
|
|
84
|
+
* exact Boss text in `textField`. Absent: every non-empty turn classifies.
|
|
85
|
+
*/
|
|
86
|
+
entryEvent?: {
|
|
87
|
+
type: string;
|
|
88
|
+
textField: string;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Exact flat Boss-event contracts whose non-text fields the judge may
|
|
92
|
+
* select. `entryEvent` and scalar `BOSS_REPLY` contracts are supplied by
|
|
93
|
+
* the factory; linkers emit entries here for additional typed events such
|
|
94
|
+
* as `BOSS_INTERRUPT` when their erased payload cannot be recovered from
|
|
95
|
+
* the XState machine alone.
|
|
96
|
+
*/
|
|
97
|
+
bossEvents?: readonly XStateBossEventSpec[];
|
|
98
|
+
/** Boss-input classifier override; default: generic parked-state classifier. */
|
|
99
|
+
classifyBossText?: (text: string, ports: PlaybookPorts, signal: AbortSignal, snapshotOrState: unknown, boundary?: RuntimeBoundaryCalls) => Promise<EventObject | undefined>;
|
|
100
|
+
/** Status line emitted after classification names an event. Default: none. */
|
|
101
|
+
classificationStatus?: (event: EventObject) => string | undefined;
|
|
102
|
+
/** Map a player-invoking state's input to the host player id. Default: lowercased player name. */
|
|
103
|
+
resolvePlayerId?: (input: PlaybookPlayerInput, options: TOptions) => string;
|
|
104
|
+
/** Compose the player prompt. Default: continuation blocks + `<field>` placeholder substitution. */
|
|
105
|
+
composePlayerPrompt?: (input: PlaybookPlayerInput) => string;
|
|
106
|
+
/** Compose the direct-Captain prompt. Default: continuation blocks + placeholder substitution with deterministic JSON rendering. */
|
|
107
|
+
composeCaptainPrompt?: (input: PlaybookCaptainInput) => string;
|
|
108
|
+
/** Linker-known exceptions to the default kebab-token → camel-field mapping. */
|
|
109
|
+
placeholderFields?: Readonly<Record<string, string>>;
|
|
110
|
+
/** Adjudicator prompt for delegated players. Default: generic guard menu. */
|
|
111
|
+
buildJudgePrompt?: (input: PlaybookPlayerInput, finalText: string) => string;
|
|
112
|
+
/** Required-payload-field extraction from a `result` description. Default: bilingual `Output shall include` clause scan. */
|
|
113
|
+
extractRequiredFields?: (description: string) => string[];
|
|
114
|
+
/** Required fields carried verbatim from the player's finalText instead of judge JSON. Default: none. */
|
|
115
|
+
verbatimPayloadFields?: ReadonlySet<string>;
|
|
116
|
+
/** States that may suspend for a Boss reply. Default: targets of the FSM's `awaitBossReply` BOSS_REPLY transitions. */
|
|
117
|
+
resumableStateIds?: ReadonlySet<string>;
|
|
118
|
+
/** Human status lines for a root transition. Default: entry lines with question/failure surfacing. */
|
|
119
|
+
statusesForState?: (state: PlaybookState, context: Record<string, unknown>, event: unknown) => readonly ScheduledStatus[];
|
|
120
|
+
/** Detached JSON-safe transition-event descriptor. Default: `type` + `transitionEventFields` strings + validated output + normalized error. */
|
|
121
|
+
normalizeTransitionEvent?: (event: unknown) => JsonValue | undefined;
|
|
122
|
+
/** String payload fields the default transition-event descriptor copies. */
|
|
123
|
+
transitionEventFields?: readonly string[];
|
|
124
|
+
/** Working directory for `script` actors. Default: the validated options' string `cwd`, else the process working directory. */
|
|
125
|
+
scriptCwd?: (options: TOptions) => string | undefined;
|
|
126
|
+
}
|
|
127
|
+
/** Strip a single Markdown code fence that wraps the whole string. */
|
|
128
|
+
export declare function stripCodeFence(text: string): string;
|
|
129
|
+
export declare function extractJsonValue(text: string, start: number, repair: boolean): string | undefined;
|
|
130
|
+
export declare function parseJudgeJson(raw: string): unknown;
|
|
131
|
+
export declare function normalizeErrorCompact(err: unknown): {
|
|
132
|
+
name: string;
|
|
133
|
+
message: string;
|
|
134
|
+
} | undefined;
|
|
135
|
+
export declare function normalizeErrorFull(err: unknown): {
|
|
136
|
+
name: string;
|
|
137
|
+
message: string;
|
|
138
|
+
stack?: string;
|
|
139
|
+
} | undefined;
|
|
140
|
+
/** Read the FSM context's single pending Boss question, when well-formed. */
|
|
141
|
+
export declare function pendingBossQuestionFromContext(context: Record<string, unknown>): PlaybookPendingBossQuestionContext | undefined;
|
|
142
|
+
/**
|
|
143
|
+
* Default player-prompt composer (slc/link.md §Player prompt composition).
|
|
144
|
+
* One callback-based pass substitutes each `<fieldName>` placeholder whose
|
|
145
|
+
* typed input field is a string; replacement text is literal, and
|
|
146
|
+
* placeholder-looking text inside a value is never re-substituted. The
|
|
147
|
+
* continuation preamble and Q/A blocks precede the domain body on resume.
|
|
148
|
+
*/
|
|
149
|
+
export declare function defaultComposePlayerPrompt(input: PlaybookPlayerInput, placeholderFields?: Readonly<Record<string, string>>): string;
|
|
150
|
+
/**
|
|
151
|
+
* Default direct-Captain prompt composer (slc/link.md §Captain prompt
|
|
152
|
+
* composition). Placeholder substitution is presence-based: string fields
|
|
153
|
+
* substitute verbatim; JSON-safe arrays/objects render as deterministic JSON
|
|
154
|
+
* with lexicographically sorted keys at every depth.
|
|
155
|
+
*/
|
|
156
|
+
export declare function defaultComposeCaptainPrompt(input: PlaybookCaptainInput, placeholderFields?: Readonly<Record<string, string>>): string;
|
|
157
|
+
/** Default player binding: each player to its lowercased name. */
|
|
158
|
+
export declare function defaultResolvePlayerId(input: PlaybookPlayerInput): string;
|
|
159
|
+
/**
|
|
160
|
+
* Default required-field extraction (slc/link.md §Captain adjudication).
|
|
161
|
+
* Limited to the description's `Output shall include` / `输出应包含` clause;
|
|
162
|
+
* recognizes both the bare backticked name and the annotated `name: <...>`
|
|
163
|
+
* form.
|
|
164
|
+
*/
|
|
165
|
+
export declare function defaultExtractRequiredFields(description: string): string[];
|
|
166
|
+
/** Default delegated-player adjudicator prompt. */
|
|
167
|
+
export declare function defaultBuildJudgePrompt(input: PlaybookPlayerInput, finalText: string): string;
|
|
168
|
+
export interface PlayerAdjudicationSpec {
|
|
169
|
+
buildJudgePrompt?: (input: PlaybookPlayerInput, finalText: string) => string;
|
|
170
|
+
extractRequiredFields?: (description: string) => string[];
|
|
171
|
+
verbatimPayloadFields?: ReadonlySet<string>;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* LLM-judge adjudicator for delegated players. Coerces the player's
|
|
175
|
+
* finalText into one of the state's declared guards, extracts every required
|
|
176
|
+
* payload field from the judge reply, and fails loudly (throws) on a missing
|
|
177
|
+
* JSON object, an undeclared guard, or a missing required field. Fields in
|
|
178
|
+
* `verbatimPayloadFields` carry `finalText.trim()` rather than round-tripping
|
|
179
|
+
* long-form prose through judge JSON.
|
|
180
|
+
*/
|
|
181
|
+
export declare function adjudicatePlayerOutput(spec: PlayerAdjudicationSpec, input: PlaybookPlayerInput, finalText: string, ports: PlaybookPorts, signal: AbortSignal, boundary?: RuntimeBoundaryCalls): Promise<PlaybookActorOutput>;
|
|
182
|
+
export interface PlayerBridgeSpec {
|
|
183
|
+
resolvePlayerId: (input: PlaybookPlayerInput) => string;
|
|
184
|
+
composePlayerPrompt: (input: PlaybookPlayerInput) => string;
|
|
185
|
+
adjudication: PlayerAdjudicationSpec;
|
|
186
|
+
resumableStateIds: ReadonlySet<string>;
|
|
187
|
+
}
|
|
188
|
+
export declare function createPlayerBridge(spec: PlayerBridgeSpec, ports: PlaybookPorts, getActiveSignal?: () => AbortSignal | undefined, boundary?: RuntimeBoundaryCalls, onControlPlaneError?: (error: unknown) => void): PromiseActorLogic<PlaybookActorOutput, PlaybookPlayerInput>;
|
|
189
|
+
/** Targets of the FSM's `awaitBossReply` BOSS_REPLY transitions. */
|
|
190
|
+
export declare function resumableStateIdsFromMachine(machine: AnyStateMachine): ReadonlySet<string>;
|
|
191
|
+
/**
|
|
192
|
+
* Build a `PlaybookRuntimeFactory` that interprets the given FSM artifact
|
|
193
|
+
* under the slc/link.md contract. The factory provides every actor kind the
|
|
194
|
+
* machine declares — `player`, `script`, `captain`, and nested `playbook`
|
|
195
|
+
* (literal and dynamic) — and implements the full runtime lifecycle including
|
|
196
|
+
* the optional parked-session snapshot capability (DR-014).
|
|
197
|
+
*
|
|
198
|
+
* Scope: single-region root machines (each snapshot exposes exactly one
|
|
199
|
+
* playbook state id). Parallel-region FSMs keep their own linked runtimes.
|
|
200
|
+
*/
|
|
201
|
+
export declare function createXStatePlaybookRuntime<TOptions>(machine: AnyStateMachine, spec: XStatePlaybookRuntimeSpec<TOptions>): PlaybookRuntimeFactory<TOptions>;
|