@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.
- package/README.md +2 -0
- package/docs/cli.md +36 -18
- package/docs/configuration.md +7 -5
- package/docs/embedding.md +26 -5
- package/package.json +4 -4
- package/reference/sdlc/captain.md +19 -10
- package/reference/sdlc/captain.playbook/captain.fsm.d.ts +6 -2
- package/reference/sdlc/captain.playbook/captain.fsm.js +19 -7
- package/reference/sdlc/captain.playbook/captain.fsm.ts +25 -10
- package/reference/sdlc/captain.playbook/captain.gears.md +16 -8
- package/reference/sdlc/captain.playbook/captain.playbook.d.ts +1 -1
- package/reference/sdlc/captain.playbook/captain.playbook.js +3 -2
- package/reference/sdlc/captain.playbook/captain.playbook.ts +4 -3
- package/reference/sdlc/code.playbook/bin/interactive-session.js +14 -1
- package/reference/sdlc/code.playbook/bin/portable-codec.js +3 -0
- package/reference/sdlc/code.playbook/bin/repository-effects.js +1 -0
- package/reference/sdlc/code.playbook/bin/run.js +128 -85
- package/reference/sdlc/code.playbook/bin/session-host.js +40 -8
- package/reference/sdlc/code.playbook/bin/session-store.js +162 -34
- package/reference/sdlc/code.playbook/code.fsm.d.ts +5 -5
- package/reference/sdlc/code.playbook/host-capabilities.d.ts +3 -0
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +32 -2
- package/reference/sdlc/code.playbook/playbook-captain.js +729 -284
- package/reference/sdlc/code.playbook/playbook-captain.ts +771 -358
- package/reference/sdlc/code.playbook/session-host.d.ts +4 -1
- package/reference/sdlc/code.playbook/session-store.d.ts +17 -0
- package/reference/sdlc/code.playbook/session-store.js +1 -0
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +5 -5
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +5 -5
- package/reference/sdlc/pr.playbook/pr.fsm.d.ts +5 -5
- package/slc/gears2fsm.md +5 -5
- package/slc/link.md +77 -33
- package/src/runtime.d.ts +46 -2
- package/src/runtime.ts +42 -2
- package/src/xstate-playbook-runtime.d.ts +9 -8
- package/src/xstate-playbook-runtime.js +471 -215
- package/src/xstate-playbook-runtime.ts +525 -238
- package/src/xstate-runtime.d.ts +3 -4
- package/src/xstate-runtime.js +52 -0
- package/src/xstate-runtime.ts +59 -4
|
@@ -2551,24 +2551,21 @@ async function createLease({
|
|
|
2551
2551
|
now,
|
|
2552
2552
|
}) {
|
|
2553
2553
|
let released = false;
|
|
2554
|
-
let
|
|
2554
|
+
let operationTail = Promise.resolve();
|
|
2555
2555
|
let indeterminateEffectLedgerWrite;
|
|
2556
|
-
|
|
2557
|
-
const
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
} finally {
|
|
2570
|
-
operationActive = false;
|
|
2571
|
-
}
|
|
2556
|
+
let releasing = false;
|
|
2557
|
+
const ownerChecks = new Set();
|
|
2558
|
+
|
|
2559
|
+
// All lifecycle, step and repository writes share one order, including
|
|
2560
|
+
// calls from parallel players. A failed write does not poison the queue.
|
|
2561
|
+
const runExclusive = (operation) => {
|
|
2562
|
+
if (releasing) return Promise.reject(new Error('Captain session lease is releasing'));
|
|
2563
|
+
const run = operationTail.then(() => {
|
|
2564
|
+
if (released) throw new Error('Captain session lease was already released');
|
|
2565
|
+
return operation();
|
|
2566
|
+
});
|
|
2567
|
+
operationTail = run.then(() => undefined, () => undefined);
|
|
2568
|
+
return run;
|
|
2572
2569
|
};
|
|
2573
2570
|
|
|
2574
2571
|
const assertOwnerUnchecked = async () => {
|
|
@@ -2580,7 +2577,14 @@ async function createLease({
|
|
|
2580
2577
|
return current;
|
|
2581
2578
|
};
|
|
2582
2579
|
|
|
2583
|
-
const assertOwner = () =>
|
|
2580
|
+
const assertOwner = () => {
|
|
2581
|
+
if (releasing) return Promise.reject(new Error('Captain session lease is releasing'));
|
|
2582
|
+
// This reads only the lease token. Parallel players must not compete
|
|
2583
|
+
// for the mutation guard, but release must still join admitted reads.
|
|
2584
|
+
const check = assertOwnerUnchecked();
|
|
2585
|
+
ownerChecks.add(check);
|
|
2586
|
+
return check.finally(() => ownerChecks.delete(check));
|
|
2587
|
+
};
|
|
2584
2588
|
|
|
2585
2589
|
const replayWriter = await createLeaseReplayWriter({
|
|
2586
2590
|
sessionsDir,
|
|
@@ -3113,6 +3117,7 @@ async function createLease({
|
|
|
3113
3117
|
...settledAbandonmentMember(prior),
|
|
3114
3118
|
uncertain: {
|
|
3115
3119
|
baseUpdatedAt: prior.uncertain.baseUpdatedAt,
|
|
3120
|
+
...(prior.uncertain.progress === undefined ? {} : { progress: prior.uncertain.progress }),
|
|
3116
3121
|
input: prior.uncertain.input,
|
|
3117
3122
|
attemptId: nextAttemptId,
|
|
3118
3123
|
attemptNumber: prior.uncertain.attemptNumber + 1,
|
|
@@ -3607,6 +3612,49 @@ async function createLease({
|
|
|
3607
3612
|
return record.effectLedger;
|
|
3608
3613
|
});
|
|
3609
3614
|
|
|
3615
|
+
const recordProgress = (value) => runExclusive(async () => {
|
|
3616
|
+
const change = requireRecord(snapshotJsonValue(value, 'Captain progress change'), 'Captain progress change');
|
|
3617
|
+
exactOptionalKeys(change, [], ['snapshot', 'step'], 'Captain progress change');
|
|
3618
|
+
await assertOwnerUnchecked();
|
|
3619
|
+
const prior = await readRecord(sessionId, { missing: 'undefined' });
|
|
3620
|
+
if (prior?.state !== 'uncertain' || prior.uncertain.abandonment) {
|
|
3621
|
+
throw new Error('Saving progress requires an active uncertain turn');
|
|
3622
|
+
}
|
|
3623
|
+
const progress = structuredClone(prior.uncertain.progress ?? { snapshot: null, steps: [], positionStepId: null });
|
|
3624
|
+
const newStepStart = change.step && !Object.hasOwn(change.step, 'result') && !progress.steps.some(({ id }) => id === change.step.id);
|
|
3625
|
+
if (Object.hasOwn(change, 'snapshot')) {
|
|
3626
|
+
if (change.snapshot !== null && !isDeepStrictEqual(change.snapshot.effectLedger, prior.effectLedger)) {
|
|
3627
|
+
throw new Error('Saved progress must contain the current effect ledger');
|
|
3628
|
+
}
|
|
3629
|
+
progress.snapshot = change.snapshot;
|
|
3630
|
+
progress.positionStepId = change.snapshot !== null && newStepStart ? change.step.id : null;
|
|
3631
|
+
}
|
|
3632
|
+
if (change.step !== undefined) {
|
|
3633
|
+
const step = snapshotJsonValue(change.step, 'Captain step');
|
|
3634
|
+
const index = progress.steps.findIndex(({ id }) => id === step.id);
|
|
3635
|
+
if (index < 0) {
|
|
3636
|
+
if (!Object.hasOwn(change, 'snapshot')) throw new Error('A step start requires its position or explicit null');
|
|
3637
|
+
if (Object.hasOwn(step, 'result') && !['completion', 'answer'].includes(step.kind)) throw new Error('A step result requires its saved start');
|
|
3638
|
+
progress.steps.push(step);
|
|
3639
|
+
} else {
|
|
3640
|
+
const { result, ...start } = step;
|
|
3641
|
+
const saved = progress.steps[index];
|
|
3642
|
+
const { result: priorResult, ...priorStart } = saved;
|
|
3643
|
+
if (!isDeepStrictEqual(start, priorStart) ||
|
|
3644
|
+
(priorResult !== undefined && !isDeepStrictEqual(priorResult, result))) {
|
|
3645
|
+
throw new Error('A saved step cannot be replaced');
|
|
3646
|
+
}
|
|
3647
|
+
progress.steps[index] = step;
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
const record = validateCaptainSessionRecord(projectRecovery({
|
|
3651
|
+
...prior, uncertain: { ...prior.uncertain, progress },
|
|
3652
|
+
}));
|
|
3653
|
+
await assertOwnerUnchecked();
|
|
3654
|
+
await writeRecord(record, { noReplace: false });
|
|
3655
|
+
await assertOwnerUnchecked();
|
|
3656
|
+
});
|
|
3657
|
+
|
|
3610
3658
|
const discard = ({ attemptId } = {}) =>
|
|
3611
3659
|
runExclusive(async () => {
|
|
3612
3660
|
assertUuid(attemptId, 'Captain session attempt id');
|
|
@@ -3620,10 +3668,8 @@ async function createLease({
|
|
|
3620
3668
|
'Captain session unresolved-effect abandonment must recover before discard',
|
|
3621
3669
|
);
|
|
3622
3670
|
}
|
|
3623
|
-
if (!
|
|
3624
|
-
throw new Error(
|
|
3625
|
-
'Captain session uncertain effect ledger differs from its pre-turn checkpoint and cannot be discarded',
|
|
3626
|
-
);
|
|
3671
|
+
if (!isUncertainTurnDiscardable(prior)) {
|
|
3672
|
+
throw new Error('Recorded work or changed repository evidence must be restored and reported; the turn cannot be discarded');
|
|
3627
3673
|
}
|
|
3628
3674
|
await assertOwnerUnchecked();
|
|
3629
3675
|
if (prior.uncertain.baseUpdatedAt === null) {
|
|
@@ -3655,19 +3701,27 @@ async function createLease({
|
|
|
3655
3701
|
});
|
|
3656
3702
|
|
|
3657
3703
|
const release = () => {
|
|
3704
|
+
if (released || releasing) return Promise.reject(new Error(`Captain session lease is ${released ? 'released' : 'releasing'}`));
|
|
3658
3705
|
replayWriter.closeAppendAdmission();
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
if (
|
|
3706
|
+
const pending = runExclusive(async () => {
|
|
3707
|
+
try {
|
|
3708
|
+
await Promise.allSettled([...ownerChecks]);
|
|
3709
|
+
await replayWriter.prepareRelease();
|
|
3710
|
+
if (replayWriter.status().incomplete && previousManifest?.replay?.incomplete !== true) {
|
|
3711
|
+
const record = await readRecord(sessionId, { missing: 'undefined' });
|
|
3712
|
+
if (record !== undefined) await writeRecord(record, { noReplace: false });
|
|
3713
|
+
}
|
|
3714
|
+
const current = await assertOwnerUnchecked();
|
|
3715
|
+
await retireObservedLease(sessionId, current);
|
|
3716
|
+
released = true;
|
|
3717
|
+
portableWriters.delete(sessionId);
|
|
3718
|
+
return replayWriter.status();
|
|
3719
|
+
} finally {
|
|
3720
|
+
releasing = false;
|
|
3664
3721
|
}
|
|
3665
|
-
const current = await assertOwnerUnchecked();
|
|
3666
|
-
await retireObservedLease(sessionId, current);
|
|
3667
|
-
released = true;
|
|
3668
|
-
portableWriters.delete(sessionId);
|
|
3669
|
-
return replayWriter.status();
|
|
3670
3722
|
});
|
|
3723
|
+
releasing = true;
|
|
3724
|
+
return pending;
|
|
3671
3725
|
};
|
|
3672
3726
|
|
|
3673
3727
|
return Object.freeze({
|
|
@@ -3691,6 +3745,7 @@ async function createLease({
|
|
|
3691
3745
|
completeUnresolvedEffectAbandonment,
|
|
3692
3746
|
recoverUnresolvedEffectAbandonment,
|
|
3693
3747
|
writeEffectLedger,
|
|
3748
|
+
recordProgress,
|
|
3694
3749
|
settle,
|
|
3695
3750
|
discard,
|
|
3696
3751
|
assertOwner,
|
|
@@ -4034,7 +4089,7 @@ function validateCanonicalCaptainSessionRecord(
|
|
|
4034
4089
|
exactOptionalKeys(
|
|
4035
4090
|
uncertain,
|
|
4036
4091
|
UNCERTAIN_KEYS,
|
|
4037
|
-
['abandonment'],
|
|
4092
|
+
['abandonment', 'progress'],
|
|
4038
4093
|
'Captain session record uncertain',
|
|
4039
4094
|
);
|
|
4040
4095
|
if (uncertain.baseUpdatedAt !== null) {
|
|
@@ -4112,6 +4167,65 @@ function validateCanonicalCaptainSessionRecord(
|
|
|
4112
4167
|
uncertain.attemptedExecutionProjection,
|
|
4113
4168
|
artifactSchemas,
|
|
4114
4169
|
);
|
|
4170
|
+
if (uncertain.progress !== undefined) {
|
|
4171
|
+
const progress = requireRecord(uncertain.progress, 'Captain progress');
|
|
4172
|
+
exactOptionalKeys(progress, ['snapshot', 'steps'], ['positionStepId'], 'Captain progress');
|
|
4173
|
+
if (!Array.isArray(progress.steps)) throw new Error('Captain steps must be an array');
|
|
4174
|
+
const ids = new Set();
|
|
4175
|
+
for (const step of progress.steps) {
|
|
4176
|
+
exactOptionalKeys(requireRecord(step, 'Captain step'), ['id', 'kind', 'stateId', 'runtimeSessionId', 'playbookId'], ['result'], 'Captain step');
|
|
4177
|
+
assertUuid(step.id, 'Captain step id');
|
|
4178
|
+
assertUuid(step.runtimeSessionId, 'Captain step runtime');
|
|
4179
|
+
if (ids.has(step.id)) throw new Error('Captain step ids must be unique');
|
|
4180
|
+
ids.add(step.id);
|
|
4181
|
+
if (!['player', 'captain', 'script', 'preparation', 'completion', 'answer'].includes(step.kind)) throw new Error('Unknown Captain step kind');
|
|
4182
|
+
if (['completion', 'answer'].includes(step.kind) && (!step.result || typeof step.result !== 'object' || Array.isArray(step.result))) throw new Error('A recorded completion or answer requires its result');
|
|
4183
|
+
if (step.kind === 'completion') {
|
|
4184
|
+
exactOptionalKeys(step.result, ['state'], ['description', 'terminalOutcome', 'retention'], 'Completed root result');
|
|
4185
|
+
if (step.result.retention !== undefined && !['clear', 'keep'].includes(step.result.retention)) throw new Error('Completed root retention must be clear or keep');
|
|
4186
|
+
if (step.result.state?.status !== 'done' || (step.result.state.stateId !== undefined && step.result.state.stateId !== step.stateId)) throw new Error('Completed root result must name its final state');
|
|
4187
|
+
if (step.result.description !== undefined) requireNonblank(step.result.description, 'Completed root description');
|
|
4188
|
+
if (step.result.terminalOutcome !== undefined) {
|
|
4189
|
+
const terminal = step.result.terminalOutcome;
|
|
4190
|
+
exactOptionalKeys(terminal, ['stateId', 'kind'], ['description'], 'Completed root outcome');
|
|
4191
|
+
if (terminal.stateId !== step.stateId || !['success', 'failure'].includes(terminal.kind)) throw new Error('Completed root outcome must name its final state and kind');
|
|
4192
|
+
if (terminal.description !== undefined) requireNonblank(terminal.description, 'Completed root outcome description');
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
if (step.kind === 'answer') {
|
|
4196
|
+
exactOptionalKeys(step.result, ['questions', 'instruction'], [], 'Selected answer');
|
|
4197
|
+
requireNonblank(step.result.instruction, 'Selected answer instruction');
|
|
4198
|
+
if (!Array.isArray(step.result.questions) || !step.result.questions.length) throw new Error('Selected answer requires questions');
|
|
4199
|
+
for (const question of step.result.questions) {
|
|
4200
|
+
exactOptionalKeys(question, ['asker', 'question'], [], 'Selected answer question');
|
|
4201
|
+
if (question.asker?.kind === 'captain') exactOptionalKeys(question.asker, ['kind'], [], 'Selected answer asker');
|
|
4202
|
+
else {
|
|
4203
|
+
exactOptionalKeys(question.asker, ['kind', 'roleId'], [], 'Selected answer asker');
|
|
4204
|
+
if (question.asker.kind !== 'role') throw new Error('Unknown selected answer asker');
|
|
4205
|
+
requireCanonicalNonblank(question.asker.roleId, 'Selected answer role');
|
|
4206
|
+
}
|
|
4207
|
+
requireNonblank(question.question, 'Selected answer question');
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
requireCanonicalNonblank(step.stateId, 'Captain step state');
|
|
4211
|
+
if (!Object.hasOwn(structural.catalog, step.playbookId)) throw new Error('Captain step names an unknown playbook');
|
|
4212
|
+
}
|
|
4213
|
+
if (progress.positionStepId !== undefined && progress.positionStepId !== null && !ids.has(progress.positionStepId)) throw new Error('Captain progress positionStepId must name a recorded step');
|
|
4214
|
+
if (progress.snapshot === null && progress.positionStepId != null) throw new Error('Captain progress positionStepId requires a saved position');
|
|
4215
|
+
if (progress.snapshot !== null) {
|
|
4216
|
+
const prepared = assertPlaybookCaptainShellSnapshot(progress.snapshot);
|
|
4217
|
+
assertSnapshotMatchesStructure(prepared, structural, artifactSchemas);
|
|
4218
|
+
if ((Object.hasOwn(progress.snapshot, 'presentedEffectPrefix') && prepared.presentedEffectPrefix !== snapshot.presentedEffectPrefix) ||
|
|
4219
|
+
!isDeepStrictEqual(prepared.captain, snapshot.captain) ||
|
|
4220
|
+
!isDeepStrictEqual(prepared.journal, snapshot.journal) ||
|
|
4221
|
+
!isDeepStrictEqual(prepared.sequences, snapshot.sequences) ||
|
|
4222
|
+
!isPlaybookEffectLedgerMonotonicExtension(snapshot.effectLedger, prepared.effectLedger) ||
|
|
4223
|
+
!isPlaybookEffectLedgerMonotonicExtension(prepared.effectLedger, effectLedger)) {
|
|
4224
|
+
throw new Error('Saved progress must retain the settled controller and current work');
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4115
4229
|
if (uncertain.baseUpdatedAt === null) {
|
|
4116
4230
|
if (!isDeepStrictEqual(lastApplied, attempted)) {
|
|
4117
4231
|
throw new Error(
|
|
@@ -4436,6 +4550,7 @@ const EFFECT_BOUNDARY_IDENTITY_KEYS = [
|
|
|
4436
4550
|
const EFFECT_BOUNDARY_OPTIONAL_KEYS = [
|
|
4437
4551
|
'after',
|
|
4438
4552
|
'physicalReceipt',
|
|
4553
|
+
'restored',
|
|
4439
4554
|
'finalText',
|
|
4440
4555
|
'semanticCandidate',
|
|
4441
4556
|
'initialSemanticCandidate',
|
|
@@ -5018,6 +5133,7 @@ function assertMonotonicBoundaryReplacement(expected, next, path) {
|
|
|
5018
5133
|
for (const key of [
|
|
5019
5134
|
'after',
|
|
5020
5135
|
'physicalReceipt',
|
|
5136
|
+
'restored',
|
|
5021
5137
|
'finalText',
|
|
5022
5138
|
'initialSemanticCandidate',
|
|
5023
5139
|
]) {
|
|
@@ -6001,9 +6117,13 @@ function validateRetainedGenerationFrame(
|
|
|
6001
6117
|
'roleBindings',
|
|
6002
6118
|
'runtime',
|
|
6003
6119
|
],
|
|
6004
|
-
['parentSessionId', 'parentCallId'],
|
|
6120
|
+
['parentSessionId', 'parentCallId', 'request', 'inputs'],
|
|
6005
6121
|
path,
|
|
6006
6122
|
);
|
|
6123
|
+
if (frame.request !== undefined) {
|
|
6124
|
+
if (index !== 0) throw new Error(`${path}.request belongs only to the root`);
|
|
6125
|
+
requireNonblank(frame.request, `${path}.request`);
|
|
6126
|
+
}
|
|
6007
6127
|
const playbookId = requireCanonicalNonblank(
|
|
6008
6128
|
frame.playbookId,
|
|
6009
6129
|
`${path}.playbookId`,
|
|
@@ -6014,6 +6134,7 @@ function validateRetainedGenerationFrame(
|
|
|
6014
6134
|
`${path}.playbookId names unknown stored playbook ${JSON.stringify(playbookId)}`,
|
|
6015
6135
|
);
|
|
6016
6136
|
}
|
|
6137
|
+
if (frame.inputs !== undefined && (index !== 0 || !Array.isArray(frame.inputs) || frame.inputs.some((input) => typeof input !== 'string' || !input.trim()))) throw new Error(`${path}.inputs belongs only to the root and must contain nonempty strings`);
|
|
6017
6138
|
assertUuid(frame.sessionId, `${path}.sessionId`);
|
|
6018
6139
|
assertUuid(frame.rootSessionId, `${path}.rootSessionId`);
|
|
6019
6140
|
if (sessionIds.has(frame.sessionId)) {
|
|
@@ -6574,3 +6695,10 @@ async function writePrivateBytes(path, bytes, fs) {
|
|
|
6574
6695
|
try { await fs.unlink(temporary); } catch (cause) { if (cause?.code !== 'ENOENT') throw cause; }
|
|
6575
6696
|
}
|
|
6576
6697
|
}
|
|
6698
|
+
|
|
6699
|
+
/** One shared decision for discard controls and durable rollback. */
|
|
6700
|
+
export function isUncertainTurnDiscardable(record) {
|
|
6701
|
+
return record?.state === 'uncertain' && !record.uncertain.abandonment &&
|
|
6702
|
+
(record.uncertain.progress?.steps.length ?? 0) === 0 &&
|
|
6703
|
+
isDeepStrictEqual(record.effectLedger, record.snapshot.effectLedger);
|
|
6704
|
+
}
|
|
@@ -179,15 +179,15 @@ export declare const codeMachine: import("xstate").StateMachine<CodeContext, {
|
|
|
179
179
|
readonly answer: string;
|
|
180
180
|
readonly questionId?: ResumableStateId;
|
|
181
181
|
}, {
|
|
182
|
-
[x: string]: import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<
|
|
182
|
+
[x: string]: import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<PlayerOutput, PlayerInput, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<PlaybookOutput, PlaybookInput, import("xstate").EventObject>> | undefined;
|
|
183
183
|
}, {
|
|
184
|
-
src: "playbook";
|
|
185
|
-
logic: import("xstate").PromiseActorLogic<PlaybookOutput, PlaybookInput, import("xstate").EventObject>;
|
|
186
|
-
id: string | undefined;
|
|
187
|
-
} | {
|
|
188
184
|
src: "player";
|
|
189
185
|
logic: import("xstate").PromiseActorLogic<PlayerOutput, PlayerInput, import("xstate").EventObject>;
|
|
190
186
|
id: string | undefined;
|
|
187
|
+
} | {
|
|
188
|
+
src: "playbook";
|
|
189
|
+
logic: import("xstate").PromiseActorLogic<PlaybookOutput, PlaybookInput, import("xstate").EventObject>;
|
|
190
|
+
id: string | undefined;
|
|
191
191
|
}, {
|
|
192
192
|
type: "playbook.acceptedOutcome";
|
|
193
193
|
params: {
|
|
@@ -86,6 +86,7 @@ export interface PlaybookEffectBoundary {
|
|
|
86
86
|
readonly baseline: PlaybookRepositoryObservation;
|
|
87
87
|
readonly after?: PlaybookRepositoryObservation;
|
|
88
88
|
readonly physicalReceipt?: PlaybookRepositoryReceipt;
|
|
89
|
+
readonly restored?: PlaybookRepositoryObservation;
|
|
89
90
|
readonly finalText?: string;
|
|
90
91
|
readonly semanticCandidate?: JsonValue;
|
|
91
92
|
readonly initialSemanticCandidate?: JsonValue;
|
|
@@ -103,6 +104,7 @@ export type PlaybookEffectBoundaryStart = Omit<
|
|
|
103
104
|
| 'attemptNumber'
|
|
104
105
|
| 'after'
|
|
105
106
|
| 'physicalReceipt'
|
|
107
|
+
| 'restored'
|
|
106
108
|
| 'finalText'
|
|
107
109
|
| 'semanticCandidate'
|
|
108
110
|
| 'initialSemanticCandidate'
|
|
@@ -278,6 +280,7 @@ export interface HostCapabilities {
|
|
|
278
280
|
readonly effectLedger: PlaybookEffectLedgerCapability;
|
|
279
281
|
}
|
|
280
282
|
export interface WorktreeRepositoryCapability extends RepositoryCapability {
|
|
283
|
+
acquire(options?: { signal?: AbortSignal }): Promise<{ assertOwner(): Promise<void>; release(): Promise<void> }>;
|
|
281
284
|
readonly identity: RepositoryIdentity;
|
|
282
285
|
observe(): Promise<PlaybookRepositoryObservation>;
|
|
283
286
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Captain, type CaptainSession, type TuningSelection } from '@sublang/cligent/tmux-play';
|
|
2
2
|
import type { Effort, PermissionPolicy } from '@sublang/cligent';
|
|
3
|
-
import type { JsonValue, PlaybookEffectLedger, PlaybookEffectLedgerCommandBatch, PlaybookControlAction, PlaybookFailureCause, PlaybookRuntime, PlaybookRuntimeSnapshot } from '@sublang/playbook/runtime';
|
|
3
|
+
import type { JsonValue, PlaybookEffectLedger, PlaybookEffectLedgerCommandBatch, PlaybookControlAction, PlaybookFailureCause, PlaybookRuntime, PlaybookRuntimeSnapshot, PlaybookStepRecord } from '@sublang/playbook/runtime';
|
|
4
4
|
import { type CaptainControllerPort } from '../captain.playbook/captain.playbook.js';
|
|
5
5
|
import type { PlaybookSummaryPolicy } from './code.registry.js';
|
|
6
6
|
interface SessionAgent {
|
|
@@ -34,7 +34,24 @@ interface PlaybookCaptainUnresolvedEffectSettlementInput {
|
|
|
34
34
|
}
|
|
35
35
|
type SnapshotAgentEnvelope = DeepReadonly<Omit<SessionAgent, 'model' | 'effort' | 'fastMode'>>;
|
|
36
36
|
type PlayerLedgerSnapshotEntry = DeepReadonly<PlayerLedgerEntry>;
|
|
37
|
+
export interface ProgressChange {
|
|
38
|
+
snapshot?: PlaybookCaptainShellSnapshot | null;
|
|
39
|
+
step?: Omit<PlaybookStepRecord, 'kind'> & {
|
|
40
|
+
kind: PlaybookStepRecord['kind'] | 'preparation' | 'completion' | 'answer';
|
|
41
|
+
runtimeSessionId: string;
|
|
42
|
+
playbookId: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface InterruptedReport {
|
|
46
|
+
text: string;
|
|
47
|
+
effects: readonly PlaybookCaptainUnresolvedEffect[];
|
|
48
|
+
retentionUpdates?: readonly PlaybookCaptainRetentionUpdate[];
|
|
49
|
+
unresolvedEffects?: readonly PlaybookCaptainUnresolvedEffect[];
|
|
50
|
+
}
|
|
37
51
|
export interface PlaybookCaptainDeps {
|
|
52
|
+
/** Stop the host's active turn, including admitted tool calls, on preparation expiry or a required save failure. */
|
|
53
|
+
abortPreparation?: (reason?: string) => void;
|
|
54
|
+
recordProgress?: (change: ProgressChange) => Promise<void>;
|
|
38
55
|
continuity?: {
|
|
39
56
|
beforeCall(participantId: string): Promise<void>;
|
|
40
57
|
acknowledged(participantId: string, token: string): void;
|
|
@@ -137,6 +154,8 @@ export interface PlaybookCaptainFrameSnapshot {
|
|
|
137
154
|
readonly depth: number;
|
|
138
155
|
readonly parentSessionId?: string;
|
|
139
156
|
readonly parentCallId?: string;
|
|
157
|
+
readonly request?: string;
|
|
158
|
+
readonly inputs?: readonly string[];
|
|
140
159
|
readonly options: JsonValue;
|
|
141
160
|
readonly roleBindings: Readonly<Record<string, string>>;
|
|
142
161
|
readonly runtime: DeepReadonly<PlaybookRuntimeSnapshot>;
|
|
@@ -144,6 +163,7 @@ export interface PlaybookCaptainFrameSnapshot {
|
|
|
144
163
|
interface PlaybookCaptainShellSnapshotFields {
|
|
145
164
|
readonly schemaVersion: 4;
|
|
146
165
|
readonly effectLedger: DeepReadonly<PlaybookEffectLedger>;
|
|
166
|
+
readonly presentedEffectPrefix?: number;
|
|
147
167
|
readonly captain: {
|
|
148
168
|
readonly sessionId: string;
|
|
149
169
|
readonly runtime: DeepReadonly<PlaybookRuntimeSnapshot>;
|
|
@@ -158,7 +178,7 @@ interface PlaybookCaptainShellSnapshotFields {
|
|
|
158
178
|
readonly journal: number;
|
|
159
179
|
};
|
|
160
180
|
readonly journal: readonly PlaybookCaptainJournalRecord[];
|
|
161
|
-
readonly lastAction?: 'respond' | 'start' | 'switch' | 'resume' | 'dismiss' | 'deliver' | 'runtime';
|
|
181
|
+
readonly lastAction?: 'respond' | 'start' | 'switch' | 'resume' | 'dismiss' | 'deliver' | 'runtime' | 'recover';
|
|
162
182
|
readonly lastSettlementStatus?: 'ok' | 'rejected' | 'failed';
|
|
163
183
|
}
|
|
164
184
|
/**
|
|
@@ -242,6 +262,8 @@ export interface PlaybookCaptainShell extends Captain {
|
|
|
242
262
|
* `handleBossTurn` (CAPTAIN-7).
|
|
243
263
|
*/
|
|
244
264
|
submitShellAction?(actionId: string): string;
|
|
265
|
+
/** Report an interrupted attempt without dispatching work. */
|
|
266
|
+
selectInterruptedReport?(input: string, report: InterruptedReport): void;
|
|
245
267
|
}
|
|
246
268
|
/**
|
|
247
269
|
* Whether words may stand outside a code span in a statement the shell says
|
|
@@ -282,6 +304,14 @@ declare function bossReport(statements: readonly (string | BossStatement)[], rea
|
|
|
282
304
|
export declare function assertPlaybookCaptainUnresolvedEffects(value: unknown): readonly PlaybookCaptainUnresolvedEffect[];
|
|
283
305
|
/** Validate, detach, and freeze one untrusted shell snapshot. */
|
|
284
306
|
export declare function assertPlaybookCaptainShellSnapshot(value: unknown): PlaybookCaptainShellSnapshot;
|
|
307
|
+
export type PlaybookCaptainUnresolvedEffectReference = {
|
|
308
|
+
readonly kind: 'boundary';
|
|
309
|
+
readonly boundaryId: string;
|
|
310
|
+
} | {
|
|
311
|
+
readonly kind: 'logical-operation';
|
|
312
|
+
readonly operationId: string;
|
|
313
|
+
};
|
|
314
|
+
export declare const projectUnresolvedEffects: (ledger: PlaybookEffectLedger, references: readonly PlaybookCaptainUnresolvedEffectReference[]) => readonly PlaybookCaptainUnresolvedEffect[];
|
|
285
315
|
export declare function createPlaybookCaptainShell(options: unknown, deps?: PlaybookCaptainDeps): PlaybookCaptainShell;
|
|
286
316
|
export declare const _internal: {
|
|
287
317
|
failureStatement: typeof failureStatement;
|