@sublang/playbook 17.2.0 → 17.4.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 (43) hide show
  1. package/README.md +5 -2
  2. package/docs/cli.md +6 -5
  3. package/docs/configuration.md +72 -18
  4. package/docs/embedding.md +94 -0
  5. package/package.json +34 -5
  6. package/reference/sdlc/captain.md +6 -4
  7. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +5 -0
  8. package/reference/sdlc/captain.playbook/captain.fsm.js +4 -3
  9. package/reference/sdlc/captain.playbook/captain.fsm.ts +9 -4
  10. package/reference/sdlc/captain.playbook/captain.gears.md +6 -3
  11. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +2 -0
  12. package/reference/sdlc/captain.playbook/captain.playbook.js +7 -3
  13. package/reference/sdlc/captain.playbook/captain.playbook.ts +8 -4
  14. package/reference/sdlc/code.playbook/asset-types.d.ts +32 -0
  15. package/reference/sdlc/code.playbook/bin/launch-config.js +84 -31
  16. package/reference/sdlc/code.playbook/bin/playbook.js +5 -3
  17. package/reference/sdlc/code.playbook/bin/portable-codec.js +12 -5
  18. package/reference/sdlc/code.playbook/bin/run.js +74 -7
  19. package/reference/sdlc/code.playbook/bin/session-host.js +16 -4
  20. package/reference/sdlc/code.playbook/bin/session-media.js +46 -0
  21. package/reference/sdlc/code.playbook/bin/session-store.js +164 -25
  22. package/reference/sdlc/code.playbook/playbook-captain.d.ts +23 -4
  23. package/reference/sdlc/code.playbook/playbook-captain.js +188 -23
  24. package/reference/sdlc/code.playbook/playbook-captain.ts +223 -31
  25. package/reference/sdlc/code.playbook/playbook.config.template.yaml +22 -5
  26. package/reference/sdlc/code.playbook/session-assets.d.ts +53 -0
  27. package/reference/sdlc/code.playbook/session-assets.js +302 -0
  28. package/reference/sdlc/code.playbook/session-host.d.ts +9 -1
  29. package/reference/sdlc/code.playbook/session-store.d.ts +26 -2
  30. package/reference/sdlc/code.playbook/worker-evidence.d.ts +8 -0
  31. package/reference/sdlc/code.playbook/worker-evidence.js +51 -0
  32. package/reference/sdlc/inspect.md +40 -0
  33. package/reference/sdlc/inspect.playbook/inspect.fsm.d.ts +168 -0
  34. package/reference/sdlc/inspect.playbook/inspect.fsm.js +354 -0
  35. package/reference/sdlc/inspect.playbook/inspect.fsm.ts +455 -0
  36. package/reference/sdlc/inspect.playbook/inspect.gears.md +36 -0
  37. package/reference/sdlc/inspect.playbook/inspect.playbook.d.ts +17 -0
  38. package/reference/sdlc/inspect.playbook/inspect.playbook.js +142 -0
  39. package/reference/sdlc/inspect.playbook/inspect.playbook.ts +184 -0
  40. package/reference/sdlc/inspect.playbook/inspect.registry.d.ts +41 -0
  41. package/reference/sdlc/inspect.playbook/inspect.registry.js +51 -0
  42. package/reference/sdlc/inspect.playbook/inspect.registry.ts +110 -0
  43. package/slc/workflow-contracts.json +43 -1
@@ -0,0 +1,455 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+
4
+ import { setup, fromPromise, assign } from 'xstate';
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Exported boundary types (the linked runtime provides the actor implementation
8
+ // and imports these exact types).
9
+ // ---------------------------------------------------------------------------
10
+
11
+ /**
12
+ * Machine construction input. The caller-supplied inspection request and any
13
+ * relevant discussion context are delivered through the Boss entry event; an
14
+ * optional seed may ride along at construction. No agent call runs before the
15
+ * first Boss-originated event.
16
+ */
17
+ export interface InspectInput {
18
+ inspectionRequest?: string;
19
+ discussionContext?: string;
20
+ }
21
+
22
+ /**
23
+ * Terminal workflow output returned to the caller. Source declares two terminal
24
+ * returns: `{ status: 'complete', report }` and `{ status: 'unavailable', report }`.
25
+ */
26
+ export type MachineOutput =
27
+ | { status: 'complete'; report: string }
28
+ | { status: 'unavailable'; report: string };
29
+
30
+ /** Standard suspended-question record carried while awaiting a Boss reply. */
31
+ export interface PendingBossQuestion {
32
+ questionId: string;
33
+ resumeStateId: string;
34
+ sourceItem: string;
35
+ asker: { kind: 'captain' } | { kind: 'role'; roleId: string };
36
+ question: string;
37
+ }
38
+
39
+ /** Typed input for the delegated `player` (Inspector) actor. */
40
+ export interface PlayerInput {
41
+ stateId: string;
42
+ role: 'inspector';
43
+ sourceItem: string;
44
+ prompt: string;
45
+ inspectionRequest: string;
46
+ discussionContext: string;
47
+ pendingBossQuestion?: PendingBossQuestion;
48
+ bossReply?: string;
49
+ result: Record<'completed' | 'unavailable' | 'needsBossReply', string>;
50
+ }
51
+
52
+ /** Discriminated result contract the Inspector actor returns. */
53
+ export type PlayerOutput =
54
+ | { guard: 'completed'; status: string; report: string }
55
+ | { guard: 'unavailable'; status: string; report: string }
56
+ | { guard: 'needsBossReply'; question: string };
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Internal machine types.
60
+ // ---------------------------------------------------------------------------
61
+
62
+ interface LastError {
63
+ name: string;
64
+ message: string;
65
+ stack?: string;
66
+ }
67
+
68
+ interface InspectContext {
69
+ inspectionRequest: string;
70
+ discussionContext: string;
71
+ report: string;
72
+ status: 'complete' | 'unavailable' | null;
73
+ lastError: LastError | null;
74
+ pendingBossQuestion: PendingBossQuestion | null;
75
+ bossReply: string | null;
76
+ }
77
+
78
+ type InspectEvent =
79
+ | { type: 'START'; inspectionRequest: string; discussionContext?: string }
80
+ | { type: 'BOSS_REPLY'; answer: string; questionId?: string };
81
+
82
+ interface AcceptedOutcomeParams {
83
+ source: string;
84
+ target: string;
85
+ acceptedOutcome: string;
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // Source-derived constants.
90
+ // ---------------------------------------------------------------------------
91
+
92
+ // INSPECT-1 full final prompt, verbatim from the GEARS blockquote. The literal
93
+ // leading `>` on the quoted-context lines is prompt content and is preserved.
94
+ const INSPECT_1_PROMPT = [
95
+ 'Inspect the requested material and give an evidence-based explanation.',
96
+ 'Use the attachments actually supplied to this call. Attachment names and metadata alone do not prove what their bytes show.',
97
+ 'For a request about a running interface, use the available browser tools to open the requested location, inspect it, and capture a screenshot when needed to support the explanation. Request screenshots as native image content by omitting an explicit filename when the tool supports that form.',
98
+ 'Reason from the images or tool results you actually receive, and explain the concrete observations that support your conclusions. Distinguish observed behavior from inference. Never claim to have opened, captured, or viewed something that the available evidence does not establish.',
99
+ 'Do not edit repository files, create commits, implement changes, or make unrelated changes to the inspected application. Browser interaction remains within the inspection Boss requested.',
100
+ 'If a missing choice or target prevents a useful inspection, ask Boss one concise question carrying every detail needed to answer. Do not invent the missing target.',
101
+ 'If required evidence or tools are unavailable, explain exactly what is unavailable and what that prevents you from concluding. Do not substitute a promise to inspect later for a result.',
102
+ "Otherwise, complete the requested inspection and explain the findings, answering Boss's request directly. Make clear which captured figures support the findings without inventing a file path or asset identifier.",
103
+ '',
104
+ '> Inspection request: <inspection-request>',
105
+ '> Discussion context: <discussion-context>',
106
+ ].join('\n');
107
+
108
+ // INSPECT-1 local result contract, guard names and descriptions preserved
109
+ // verbatim in declared order, with the universal needsBossReply appended.
110
+ const INSPECT_1_RESULT: Record<'completed' | 'unavailable' | 'needsBossReply', string> = {
111
+ completed:
112
+ "Inspector's response affirmatively supports a completed inspection, not depending on any fixed presentation format. `inspect` terminates successfully and returns `{ status: 'complete', report }` to the caller, and its terminal description states that the inspection is complete without a repository change. Output shall include `status` (the string complete) and `report: <verbatim final text>`, where report is Inspector's complete explanation.",
113
+ unavailable:
114
+ "Inspector's response affirmatively supports that required evidence or tools are unavailable, not depending on any fixed presentation format. `inspect` terminates as an authored failure and returns `{ status: 'unavailable', report }` to the caller, and its terminal description states that the requested inspection could not be completed. Output shall include `status` (the string unavailable) and `report: <verbatim final text>`, where report is Inspector's complete explanation of the missing evidence or tools.",
115
+ needsBossReply:
116
+ "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.",
117
+ };
118
+
119
+ // Role-id arrays per parallel group; this machine has no parallel group.
120
+ export const concurrentRoleSets = [] as const;
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Structural narrowing helpers (accept unknown event output and narrow to the
124
+ // declared contract before reading fields).
125
+ // ---------------------------------------------------------------------------
126
+
127
+ function playerOutputOf(event: unknown): PlayerOutput | null {
128
+ if (typeof event !== 'object' || event === null) return null;
129
+ const output = (event as { output?: unknown }).output;
130
+ if (typeof output !== 'object' || output === null) return null;
131
+ const guard = (output as { guard?: unknown }).guard;
132
+ if (guard === 'completed' || guard === 'unavailable') {
133
+ const status = (output as { status?: unknown }).status;
134
+ const report = (output as { report?: unknown }).report;
135
+ if (typeof status === 'string' && typeof report === 'string' && report.trim().length > 0) {
136
+ return guard === 'completed'
137
+ ? { guard: 'completed', status, report }
138
+ : { guard: 'unavailable', status, report };
139
+ }
140
+ return null;
141
+ }
142
+ if (guard === 'needsBossReply') {
143
+ const question = (output as { question?: unknown }).question;
144
+ if (typeof question === 'string') return { guard: 'needsBossReply', question };
145
+ return null;
146
+ }
147
+ return null;
148
+ }
149
+
150
+ function reportOf(event: unknown): string {
151
+ const output = playerOutputOf(event);
152
+ return output && (output.guard === 'completed' || output.guard === 'unavailable')
153
+ ? output.report
154
+ : '';
155
+ }
156
+
157
+ function normalizeError(error: unknown): LastError {
158
+ if (error instanceof Error) {
159
+ return error.stack !== undefined
160
+ ? { name: error.name, message: error.message, stack: error.stack }
161
+ : { name: error.name, message: error.message };
162
+ }
163
+ return { name: 'Error', message: typeof error === 'string' ? error : 'Unknown error' };
164
+ }
165
+
166
+ function nonEmptyString(value: unknown): boolean {
167
+ return typeof value === 'string' && value.trim().length > 0;
168
+ }
169
+
170
+ // ---------------------------------------------------------------------------
171
+ // Machine.
172
+ // ---------------------------------------------------------------------------
173
+
174
+ export const inspectMachine = setup({
175
+ types: {
176
+ context: {} as InspectContext,
177
+ events: {} as InspectEvent,
178
+ input: {} as InspectInput,
179
+ output: {} as MachineOutput,
180
+ },
181
+ actors: {
182
+ // Delegated Inspector work; the runner supplies the implementation.
183
+ player: fromPromise<PlayerOutput, PlayerInput>(async () => {
184
+ throw new Error('player actor must be provided by the runner');
185
+ }),
186
+ },
187
+ actions: {
188
+ // No-op accepted-outcome marker; the linked runtime consumes these
189
+ // three string params to publish the accepted-outcome trace.
190
+ 'playbook.acceptedOutcome': (_args: unknown, _params: AcceptedOutcomeParams) => {},
191
+ applyEntry: assign({
192
+ inspectionRequest: ({ event }) => {
193
+ const value = (event as { inspectionRequest?: unknown }).inspectionRequest;
194
+ return typeof value === 'string' ? value : '';
195
+ },
196
+ discussionContext: ({ context, event }) => {
197
+ const value = (event as { discussionContext?: unknown }).discussionContext;
198
+ return typeof value === 'string' ? value : context.discussionContext;
199
+ },
200
+ report: () => '',
201
+ status: () => null,
202
+ lastError: () => null,
203
+ pendingBossQuestion: () => null,
204
+ bossReply: () => null,
205
+ }),
206
+ recordCompleted: assign({
207
+ report: ({ event }) => reportOf(event),
208
+ status: () => 'complete' as const,
209
+ }),
210
+ recordUnavailable: assign({
211
+ report: ({ event }) => reportOf(event),
212
+ status: () => 'unavailable' as const,
213
+ }),
214
+ setPendingBossQuestion: assign({
215
+ pendingBossQuestion: ({ event }): PendingBossQuestion => {
216
+ const output = playerOutputOf(event);
217
+ const question = output && output.guard === 'needsBossReply' ? output.question : '';
218
+ return {
219
+ questionId: 'inspecting',
220
+ resumeStateId: 'inspecting',
221
+ sourceItem: 'INSPECT-1',
222
+ asker: { kind: 'role' as const, roleId: 'inspector' },
223
+ question,
224
+ };
225
+ },
226
+ }),
227
+ storeBossReply: assign({
228
+ bossReply: ({ event }) => {
229
+ const answer = (event as { answer?: unknown }).answer;
230
+ return typeof answer === 'string' ? answer : '';
231
+ },
232
+ }),
233
+ clearBossReplyContext: assign({
234
+ pendingBossQuestion: () => null,
235
+ bossReply: () => null,
236
+ }),
237
+ rememberError: assign({
238
+ lastError: ({ event }) => normalizeError((event as { error?: unknown }).error),
239
+ }),
240
+ rememberMalformedOutput: assign({
241
+ lastError: () => ({
242
+ name: 'MalformedActorOutput',
243
+ message: 'Inspector returned an unrecognized result.',
244
+ }),
245
+ }),
246
+ rememberEmptyReply: assign({
247
+ lastError: () => ({
248
+ name: 'InvalidBossReply',
249
+ message: 'BOSS_REPLY carried an empty or whitespace-only answer.',
250
+ }),
251
+ }),
252
+ },
253
+ guards: {
254
+ hasInspectionRequest: ({ event }) =>
255
+ nonEmptyString((event as { inspectionRequest?: unknown }).inspectionRequest),
256
+ hasBossReply: ({ event }) => nonEmptyString((event as { answer?: unknown }).answer),
257
+ playerCompleted: ({ event }) => playerOutputOf(event)?.guard === 'completed',
258
+ playerUnavailable: ({ event }) => playerOutputOf(event)?.guard === 'unavailable',
259
+ playerNeedsBossReply: ({ event }) => playerOutputOf(event)?.guard === 'needsBossReply',
260
+ },
261
+ }).createMachine({
262
+ id: 'inspect',
263
+ description:
264
+ 'Investigate and explain existing material or a running interface without changing the repository.',
265
+ initial: 'ready',
266
+ context: ({ input }) => ({
267
+ inspectionRequest: input.inspectionRequest ?? '',
268
+ discussionContext: input.discussionContext ?? '',
269
+ report: '',
270
+ status: null,
271
+ lastError: null,
272
+ pendingBossQuestion: null,
273
+ bossReply: null,
274
+ }),
275
+ output: ({ context }): MachineOutput =>
276
+ context.status === 'complete'
277
+ ? { status: 'complete', report: context.report }
278
+ : { status: 'unavailable', report: context.report },
279
+ states: {
280
+ ready: {
281
+ id: 'ready',
282
+ description: 'Idle hub awaiting a Boss inspection request.',
283
+ tags: ['playbook.parked'],
284
+ meta: {
285
+ playbook: {
286
+ stateId: 'ready',
287
+ description: 'Idle hub awaiting a Boss inspection request.',
288
+ },
289
+ },
290
+ on: {
291
+ START: {
292
+ guard: 'hasInspectionRequest',
293
+ target: 'inspecting',
294
+ actions: 'applyEntry',
295
+ },
296
+ },
297
+ },
298
+ inspecting: {
299
+ id: 'inspecting',
300
+ description:
301
+ 'Inspector investigates the requested material and gives an evidence-based explanation.',
302
+ tags: ['playbook.busy'],
303
+ meta: {
304
+ playbook: {
305
+ stateId: 'inspecting',
306
+ description:
307
+ 'Inspector investigates the requested material and gives an evidence-based explanation.',
308
+ role: 'inspector',
309
+ },
310
+ },
311
+ invoke: {
312
+ src: 'player',
313
+ input: ({ context }): PlayerInput => ({
314
+ stateId: 'inspecting',
315
+ role: 'inspector',
316
+ sourceItem: 'INSPECT-1',
317
+ prompt: INSPECT_1_PROMPT,
318
+ inspectionRequest: context.inspectionRequest,
319
+ discussionContext: context.discussionContext,
320
+ ...(context.pendingBossQuestion
321
+ ? { pendingBossQuestion: context.pendingBossQuestion }
322
+ : {}),
323
+ ...(context.bossReply ? { bossReply: context.bossReply } : {}),
324
+ result: INSPECT_1_RESULT,
325
+ }),
326
+ onDone: [
327
+ {
328
+ guard: 'playerCompleted',
329
+ target: 'complete',
330
+ actions: [
331
+ {
332
+ type: 'playbook.acceptedOutcome',
333
+ params: {
334
+ source: 'inspecting',
335
+ target: 'complete',
336
+ acceptedOutcome: 'completed',
337
+ },
338
+ },
339
+ 'recordCompleted',
340
+ 'clearBossReplyContext',
341
+ ],
342
+ },
343
+ {
344
+ guard: 'playerUnavailable',
345
+ target: 'unavailable',
346
+ actions: [
347
+ {
348
+ type: 'playbook.acceptedOutcome',
349
+ params: {
350
+ source: 'inspecting',
351
+ target: 'unavailable',
352
+ acceptedOutcome: 'unavailable',
353
+ },
354
+ },
355
+ 'recordUnavailable',
356
+ 'clearBossReplyContext',
357
+ ],
358
+ },
359
+ {
360
+ guard: 'playerNeedsBossReply',
361
+ target: 'awaitBossReply',
362
+ actions: [
363
+ {
364
+ type: 'playbook.acceptedOutcome',
365
+ params: {
366
+ source: 'inspecting',
367
+ target: 'awaitBossReply',
368
+ acceptedOutcome: 'needsBossReply',
369
+ },
370
+ },
371
+ 'setPendingBossQuestion',
372
+ ],
373
+ },
374
+ {
375
+ target: 'failed',
376
+ actions: ['rememberMalformedOutput', 'clearBossReplyContext'],
377
+ },
378
+ ],
379
+ onError: {
380
+ target: 'failed',
381
+ actions: ['rememberError', 'clearBossReplyContext'],
382
+ },
383
+ },
384
+ },
385
+ awaitBossReply: {
386
+ id: 'awaitBossReply',
387
+ description: "Waiting for Boss to answer the acting agent's question.",
388
+ tags: ['playbook.parked'],
389
+ meta: {
390
+ playbook: {
391
+ stateId: 'awaitBossReply',
392
+ description: "Waiting for Boss to answer the acting agent's question.",
393
+ },
394
+ },
395
+ on: {
396
+ BOSS_REPLY: [
397
+ {
398
+ guard: 'hasBossReply',
399
+ target: '#inspecting',
400
+ reenter: true,
401
+ actions: 'storeBossReply',
402
+ },
403
+ {
404
+ target: '#failed',
405
+ actions: ['rememberEmptyReply', 'clearBossReplyContext'],
406
+ },
407
+ ],
408
+ },
409
+ },
410
+ complete: {
411
+ id: 'complete',
412
+ type: 'final',
413
+ description: 'The inspection is complete without a repository change.',
414
+ meta: {
415
+ playbook: {
416
+ stateId: 'complete',
417
+ description: 'The inspection is complete without a repository change.',
418
+ terminal: 'success',
419
+ },
420
+ },
421
+ },
422
+ unavailable: {
423
+ id: 'unavailable',
424
+ type: 'final',
425
+ description: 'The requested inspection could not be completed.',
426
+ meta: {
427
+ playbook: {
428
+ stateId: 'unavailable',
429
+ description: 'The requested inspection could not be completed.',
430
+ terminal: 'failure',
431
+ },
432
+ },
433
+ },
434
+ failed: {
435
+ id: 'failed',
436
+ description:
437
+ 'A control error or malformed result interrupted the inspection; awaiting Boss recovery.',
438
+ tags: ['playbook.parked'],
439
+ meta: {
440
+ playbook: {
441
+ stateId: 'failed',
442
+ description:
443
+ 'A control error or malformed result interrupted the inspection; awaiting Boss recovery.',
444
+ },
445
+ },
446
+ on: {
447
+ START: {
448
+ guard: 'hasInspectionRequest',
449
+ target: 'inspecting',
450
+ actions: 'applyEntry',
451
+ },
452
+ },
453
+ },
454
+ },
455
+ });
@@ -0,0 +1,36 @@
1
+ <!-- SPDX-License-Identifier: Apache-2.0 -->
2
+ <!-- SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai> -->
3
+
4
+ # Inspect
5
+
6
+ Roles:
7
+
8
+ - Inspector
9
+
10
+ The caller supplies an inspection request, including the subject, any requested comparison or explanation, and relevant context.
11
+ The host supplies only the attachments selected for this engagement and any explicitly enabled browser tools.
12
+ `inspect` investigates and explains existing material or a running interface without changing the repository, implementing a fix, or making a commit.
13
+
14
+ ### INSPECT-1
15
+
16
+ Every Inspector call requires the repository to remain unchanged.
17
+ For a needs-Boss-reply outcome, `inspect` uses the standard Boss-question suspension with Inspector's complete response; after Boss replies, it resumes Inspector in the same conversation with the answer and the original request, including the previous question when the conversation starts fresh.
18
+ A failed or interrupted agent call follows the standard failure or abort path and never establishes a completed inspection.
19
+
20
+ At the start of `inspect` and after each Boss reply, Captain shall relay the inspection request and relevant discussion context to Inspector in quotes (`>`):
21
+
22
+ > Inspect the requested material and give an evidence-based explanation.
23
+ > Use the attachments actually supplied to this call. Attachment names and metadata alone do not prove what their bytes show.
24
+ > For a request about a running interface, use the available browser tools to open the requested location, inspect it, and capture a screenshot when needed to support the explanation. Request screenshots as native image content by omitting an explicit filename when the tool supports that form.
25
+ > Reason from the images or tool results you actually receive, and explain the concrete observations that support your conclusions. Distinguish observed behavior from inference. Never claim to have opened, captured, or viewed something that the available evidence does not establish.
26
+ > Do not edit repository files, create commits, implement changes, or make unrelated changes to the inspected application. Browser interaction remains within the inspection Boss requested.
27
+ > If a missing choice or target prevents a useful inspection, ask Boss one concise question carrying every detail needed to answer. Do not invent the missing target.
28
+ > If required evidence or tools are unavailable, explain exactly what is unavailable and what that prevents you from concluding. Do not substitute a promise to inspect later for a result.
29
+ > Otherwise, complete the requested inspection and explain the findings, answering Boss's request directly. Make clear which captured figures support the findings without inventing a file path or asset identifier.
30
+ >
31
+ > > Inspection request: <inspection-request>
32
+ > > Discussion context: <discussion-context>
33
+
34
+ Results:
35
+ - `completed`: Inspector's response affirmatively supports a completed inspection, not depending on any fixed presentation format. `inspect` terminates successfully and returns `{ status: 'complete', report }` to the caller, and its terminal description states that the inspection is complete without a repository change. Output shall include `status` (the string complete) and `report: <verbatim final text>`, where report is Inspector's complete explanation.
36
+ - `unavailable`: Inspector's response affirmatively supports that required evidence or tools are unavailable, not depending on any fixed presentation format. `inspect` terminates as an authored failure and returns `{ status: 'unavailable', report }` to the caller, and its terminal description states that the requested inspection could not be completed. Output shall include `status` (the string unavailable) and `report: <verbatim final text>`, where report is Inspector's complete explanation of the missing evidence or tools.
@@ -0,0 +1,17 @@
1
+ import { type XStatePromptIdentity, type PlaybookPlayerInput, type XStatePlaybookRuntimeConstruction, type XStatePlaybookRuntimeFactory } from '@sublang/playbook/xstate-runtime';
2
+ export type { PlayerResult, PlayerCallOptions, PlayerSessionStore, CaptainResult, CaptainCallOptions, JsonValue, NormalizedError, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookStateValue, PlaybookState, PlaybookPendingCall, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookPorts, PlaybookTraceEvent, PlaybookTraceType, PlaybookControlReceipt, PlaybookControlView, } from '@sublang/playbook/runtime';
3
+ export interface PlaybookRuntimeOptions {
4
+ readonly "discussionContext"?: string;
5
+ }
6
+ export type PlaybookHostCapabilities = XStatePlaybookRuntimeConstruction<PlaybookRuntimeOptions, {
7
+ readonly authority: object;
8
+ }>['hostCapabilities'];
9
+ export declare function validateOptions(value: unknown): PlaybookRuntimeOptions;
10
+ export declare const _internal: {
11
+ composePlayerPrompt: (input: PlaybookPlayerInput, promptIdentity: XStatePromptIdentity, resuming?: boolean) => string;
12
+ RESUMABLE_STATE_IDS: ReadonlySet<string>;
13
+ UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string>;
14
+ VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
15
+ };
16
+ declare const createPlaybookRuntime: XStatePlaybookRuntimeFactory<XStatePlaybookRuntimeConstruction<PlaybookRuntimeOptions, PlaybookHostCapabilities>, 3>;
17
+ export default createPlaybookRuntime;
@@ -0,0 +1,142 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ // Generated by slc/materialize-link.mjs; governed by the complete slc/link.md.
4
+ // FSM: "./inspect.fsm.js"; export: inspectMachine; strategies: shared defaults with labelled string relays and nested actors.
5
+ import { inspectMachine as machine } from "./inspect.fsm.js";
6
+ import { createXStatePlaybookRuntime, snapshotJsonValue, composePlayerContinuation, } from '@sublang/playbook/xstate-runtime';
7
+ const OPTION_SCHEMA = {
8
+ "discussionContext": {
9
+ "type": "string",
10
+ "required": false
11
+ }
12
+ };
13
+ const INPUT_MAPPING = {
14
+ "discussionContext": "discussionContext"
15
+ };
16
+ const RESUMABLE_STATE_IDS = new Set([
17
+ "inspecting"
18
+ ]);
19
+ const UNFINISHED_FINAL_STATE_IDS = new Set([
20
+ "unavailable"
21
+ ]);
22
+ const VERBATIM_PAYLOAD_FIELDS = new Set([
23
+ "report"
24
+ ]);
25
+ export function validateOptions(value) {
26
+ const captured = snapshotJsonValue(value === undefined ? {} : value, "INSPECT runtime options");
27
+ if (captured === null || typeof captured !== 'object' || Array.isArray(captured)) {
28
+ throw new TypeError("INSPECT runtime options must be an object");
29
+ }
30
+ const fields = captured;
31
+ for (const key of Object.keys(captured)) {
32
+ if (!Object.hasOwn(OPTION_SCHEMA, key))
33
+ throw new TypeError("INSPECT runtime options." + key + ' is not declared');
34
+ }
35
+ for (const [key, schema] of Object.entries(OPTION_SCHEMA)) {
36
+ if (!Object.hasOwn(captured, key)) {
37
+ if (schema.required)
38
+ throw new TypeError("INSPECT runtime options." + key + ' is required');
39
+ }
40
+ else if (typeof fields[key] !== schema.type || (schema.type === 'number' && !Number.isFinite(fields[key]))) {
41
+ throw new TypeError("INSPECT runtime options." + key + ' must be a ' + schema.type);
42
+ }
43
+ }
44
+ return captured;
45
+ }
46
+ function renderPlayerPrompt(input, promptIdentity, resuming = false) {
47
+ const fields = input;
48
+ const mapping = {};
49
+ const identities = {};
50
+ const omitted = new Set(["> Discussion context: <discussion-context>"]);
51
+ const fieldFor = (token) => Object.hasOwn(mapping, token) ? mapping[token] : (token === '#' ? 'irNumber' : token.replace(/-([A-Za-z0-9_$])/g, (_match, next) => next.toUpperCase()));
52
+ const parts = input.prompt.split(/(\r?\n)/);
53
+ let body = '';
54
+ for (let index = 0; index < parts.length; index += 2) {
55
+ const line = parts[index];
56
+ if (omitted.has(line)) {
57
+ const token = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>$/.exec(line)[1];
58
+ if (fields[fieldFor(token)] === '')
59
+ continue;
60
+ }
61
+ body += line.replace(/<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g, (match, token) => {
62
+ const value = Object.hasOwn(identities, token) ? promptIdentity(identities[token]) : fields[fieldFor(token)];
63
+ if (typeof value !== 'string')
64
+ return match;
65
+ return line.startsWith('> ') ? value.replace(/\r?\n/g, (ending) => ending + '> ') : value;
66
+ }) + (parts[index + 1] ?? '');
67
+ }
68
+ return composePlayerContinuation(input, body, resuming);
69
+ }
70
+ const composePlayerPrompt = (input, promptIdentity, resuming) => renderPlayerPrompt(input, promptIdentity, resuming);
71
+ const runtimeSpec = {
72
+ composePlayerPrompt,
73
+ ...{
74
+ "label": "INSPECT",
75
+ "compat": {
76
+ "artifactSchema": 3,
77
+ "runtimeAbi": 1
78
+ },
79
+ "entryEvent": {
80
+ "type": "START",
81
+ "textField": "inspectionRequest",
82
+ "contextField": "inspectionRequest"
83
+ },
84
+ "bossEvents": [],
85
+ "roleStates": {
86
+ "inspecting": {
87
+ "role": "inspector",
88
+ "label": "Inspector investigates the requested material and gives an evidence-based explanation."
89
+ }
90
+ },
91
+ "outcomeAuthority": {
92
+ "governedPlayerStates": {
93
+ "inspecting": {
94
+ "completed": {
95
+ "fields": {
96
+ "status": "semantic",
97
+ "report": "presentation"
98
+ },
99
+ "repositoryDisposition": "unchanged"
100
+ },
101
+ "unavailable": {
102
+ "fields": {
103
+ "status": "semantic",
104
+ "report": "presentation"
105
+ },
106
+ "repositoryDisposition": "unchanged"
107
+ },
108
+ "needsBossReply": {
109
+ "fields": {
110
+ "question": "presentation"
111
+ },
112
+ "repositoryDisposition": "unchanged"
113
+ }
114
+ }
115
+ }
116
+ },
117
+ "placeholderFields": {},
118
+ "transitionEventFields": [
119
+ "inspectionRequest",
120
+ "discussionContext",
121
+ "answer",
122
+ "questionId"
123
+ ],
124
+ "controlContextFields": [
125
+ "inspectionRequest"
126
+ ]
127
+ },
128
+ snapshotOptions: validateOptions,
129
+ machineInput: (options) => Object.fromEntries(Object.entries(INPUT_MAPPING).filter(([, key]) => options[key] !== undefined)
130
+ .map(([field, key]) => [field, options[key]])),
131
+ verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
132
+ resumableStateIds: RESUMABLE_STATE_IDS,
133
+ unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
134
+ };
135
+ export const _internal = {
136
+ composePlayerPrompt,
137
+ RESUMABLE_STATE_IDS,
138
+ UNFINISHED_FINAL_STATE_IDS,
139
+ VERBATIM_PAYLOAD_FIELDS,
140
+ };
141
+ const createPlaybookRuntime = createXStatePlaybookRuntime(machine, runtimeSpec);
142
+ export default createPlaybookRuntime;