@skill-harness/adapters 0.12.0 → 0.13.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/dist/archive-access.d.ts +58 -0
- package/dist/archive-access.js +61 -0
- package/dist/archive-checkpoint.d.ts +47 -0
- package/dist/archive-checkpoint.js +116 -0
- package/dist/archive-facts.d.ts +19 -0
- package/dist/archive-facts.js +20 -0
- package/dist/archive-observer.d.ts +21 -0
- package/dist/archive-observer.js +79 -0
- package/dist/archive-policy.d.ts +107 -0
- package/dist/archive-policy.js +135 -0
- package/dist/archive-read-capability.d.ts +49 -0
- package/dist/archive-read-capability.js +45 -0
- package/dist/archive-retention-policy.d.ts +52 -0
- package/dist/archive-retention-policy.js +49 -0
- package/dist/archived-work.d.ts +44 -0
- package/dist/archived-work.js +59 -0
- package/dist/blind-intervention.d.ts +29 -0
- package/dist/blind-intervention.js +211 -0
- package/dist/codex-diagnostic-model.d.ts +14 -0
- package/dist/codex-diagnostic-model.js +39 -0
- package/dist/codex-host-observer.d.ts +111 -0
- package/dist/codex-host-observer.js +309 -0
- package/dist/codex-https.d.ts +7 -0
- package/dist/codex-https.js +221 -0
- package/dist/codex-model-profile.d.ts +38 -0
- package/dist/codex-model-profile.js +54 -0
- package/dist/codex-producer-ipc.d.ts +55 -0
- package/dist/codex-producer-ipc.js +67 -0
- package/dist/codex-sdk-transport.d.ts +50 -0
- package/dist/codex-sdk-transport.js +294 -0
- package/dist/codex-subscription.d.ts +114 -0
- package/dist/codex-subscription.js +268 -0
- package/dist/evidence-archive.d.ts +57 -0
- package/dist/evidence-archive.js +221 -0
- package/dist/execution-projection-schema.d.ts +10 -0
- package/dist/execution-projection-schema.js +30 -0
- package/dist/execution-retention-archive.d.ts +69 -0
- package/dist/execution-retention-archive.js +154 -0
- package/dist/generated/retention-v2-contract.d.ts +72 -0
- package/dist/generated/retention-v2-contract.js +105 -0
- package/dist/generated/retention-v2-json.d.ts +24 -0
- package/dist/generated/retention-v2-json.js +268 -0
- package/dist/generated/retention-v2-native-json.d.ts +2 -0
- package/dist/generated/retention-v2-native-json.js +33 -0
- package/dist/generated/retention-v2-native.d.ts +58 -0
- package/dist/generated/retention-v2-native.js +155 -0
- package/dist/generated/retention-v2-pin.d.ts +2 -0
- package/dist/generated/retention-v2-pin.js +4 -0
- package/dist/generated/retention-v2-types.d.ts +49 -0
- package/dist/generated/retention-v2-types.js +2 -0
- package/dist/generated/retention-v2-work-types.d.ts +260 -0
- package/dist/generated/retention-v2-work-types.js +13 -0
- package/dist/generated/work-v4/execution-id.d.ts +5 -0
- package/dist/generated/work-v4/execution-id.js +14 -0
- package/dist/generated/work-v4/json.d.ts +24 -0
- package/dist/generated/work-v4/json.js +268 -0
- package/dist/generated/work-v4/occurrences.d.ts +226 -0
- package/dist/generated/work-v4/occurrences.js +116 -0
- package/dist/generated/work-v4/pin.d.ts +1 -0
- package/dist/generated/work-v4/pin.js +3 -0
- package/dist/generated/work-v4/projection.d.ts +294 -0
- package/dist/generated/work-v4/projection.js +238 -0
- package/dist/generated/work-v4/reader.d.ts +24 -0
- package/dist/generated/work-v4/reader.js +109 -0
- package/dist/generated/work-v4/snapshot.d.ts +28 -0
- package/dist/generated/work-v4/snapshot.js +268 -0
- package/dist/generated/work-v4/types.d.ts +260 -0
- package/dist/generated/work-v4/types.js +13 -0
- package/dist/generated/work-v4/validation.d.ts +13 -0
- package/dist/generated/work-v4/validation.js +227 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +22 -0
- package/dist/installed-review-session.d.ts +28 -0
- package/dist/installed-review-session.js +166 -0
- package/dist/intervention-run.d.ts +403 -0
- package/dist/intervention-run.js +169 -0
- package/dist/learning-case.d.ts +12 -0
- package/dist/learning-case.js +15 -0
- package/dist/learning-journal.d.ts +18 -0
- package/dist/learning-journal.js +256 -0
- package/dist/principal-payload-port.d.ts +25 -0
- package/dist/principal-payload-port.js +40 -0
- package/dist/producer-product.d.ts +70 -0
- package/dist/producer-product.js +186 -0
- package/dist/producer-review.d.ts +53 -0
- package/dist/producer-review.js +207 -0
- package/dist/reviewed-archive-export.d.ts +42 -0
- package/dist/reviewed-archive-export.js +78 -0
- package/dist/trust-lifecycle.d.ts +128 -0
- package/dist/trust-lifecycle.js +200 -0
- package/dist/weekly-investigation.d.ts +237 -0
- package/dist/weekly-investigation.js +330 -0
- package/dist/work-candidates.d.ts +29 -0
- package/dist/work-candidates.js +78 -0
- package/dist/work-case-archive.d.ts +25 -0
- package/dist/work-case-archive.js +63 -0
- package/dist/work-case-review.d.ts +45 -0
- package/dist/work-case-review.js +220 -0
- package/dist/work-signal-cases.d.ts +15 -0
- package/dist/work-signal-cases.js +54 -0
- package/dist/work-signal-observation.d.ts +23 -0
- package/dist/work-signal-observation.js +89 -0
- package/package.json +3 -2
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { isAbsolute } from 'node:path';
|
|
3
|
+
import { constants, openSync, fstatSync, writeSync, fsyncSync, closeSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { screenResults, validateResults, parseLearningRequest, buildHypothesis, selectWeeklyInvestigation, authorizeInvestigation, previewInvestigationScenario, applyInvestigationScenario, freezeInvestigation, assertFrozenInvestigation } from '@skill-harness/core';
|
|
5
|
+
import { createArchiveReadCapability } from './archive-read-capability.js';
|
|
6
|
+
import { readArchiveSource } from './evidence-archive.js';
|
|
7
|
+
import { readLearningCase } from './learning-case.js';
|
|
8
|
+
import { learningJournal, learningCopy, learningHash, learningFile, registerLearningStore, verifyLearningStore } from './learning-journal.js';
|
|
9
|
+
const sha = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
function confirmed(input) {
|
|
11
|
+
if (Object.keys(input).sort().join() !== 'archiveRoot,cases,maxCases,policyDigest,population,reader,week' || !isAbsolute(input.archiveRoot))
|
|
12
|
+
throw Error('closed absolute weekly input required');
|
|
13
|
+
if (!Array.isArray(input.cases) || !input.cases.length || input.cases.length > 32)
|
|
14
|
+
throw Error('bounded confirmed cases required');
|
|
15
|
+
return input.cases.map(c => { const { candidate, current, matched } = readLearningCase(input.archiveRoot, c); if (!matched || current?.disposition !== 'confirmed_defect' || candidate.detector.population !== input.population)
|
|
16
|
+
throw Error('current confirmed case decision and population required'); return candidate.id; });
|
|
17
|
+
}
|
|
18
|
+
function selection(input) { const ids = confirmed(input), reader = createArchiveReadCapability({ ...input.reader, root: input.archiveRoot }); return selectWeeklyInvestigation([], { week: input.week, population: input.population, policyDigest: input.policyDigest, archiveSnapshot: reader.snapshot.id, eligibleCaseIds: ids, maxCases: input.maxCases }); }
|
|
19
|
+
export function createWeeklyInvestigation(directory, input) {
|
|
20
|
+
const safe = learningCopy(input), selected = selection(safe), initial = { type: 'initial', kind: 'weekly-investigation-v1', input: safe, selection: selected };
|
|
21
|
+
registerLearningStore(safe.archiveRoot, 'weekly', selected.key, directory, learningHash(initial));
|
|
22
|
+
try {
|
|
23
|
+
learningJournal(directory, initial);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
if (e.code !== 'EEXIST')
|
|
27
|
+
throw e;
|
|
28
|
+
const old = learningJournal(directory).read()[0].value;
|
|
29
|
+
if (learningHash(old) !== learningHash(initial))
|
|
30
|
+
throw Error('weekly selection already frozen');
|
|
31
|
+
}
|
|
32
|
+
return openWeeklyInvestigation(directory);
|
|
33
|
+
}
|
|
34
|
+
/** Existing run is a finite inert data-only interpreter. The separate prepareModel/runModel
|
|
35
|
+
* composition forwards only capability-selected frozen bytes to an original producer model seam;
|
|
36
|
+
* it does not expose shell/filesystem tools or turn proposal evidence into execution authority. */
|
|
37
|
+
export function openWeeklyInvestigation(directory) {
|
|
38
|
+
const journal = learningJournal(directory), initial = journal.read()[0].value;
|
|
39
|
+
if (initial.kind !== 'weekly-investigation-v1' || initial.type !== 'initial')
|
|
40
|
+
throw Error('wrong supervisor journal');
|
|
41
|
+
const input = initial.input, selected = initial.selection;
|
|
42
|
+
const inspect = () => {
|
|
43
|
+
verifyLearningStore(input.archiveRoot, 'weekly', selected.key, directory, learningHash(initial));
|
|
44
|
+
const events = journal.read();
|
|
45
|
+
let state = 'prepared', hypothesis = null, approved = false, preview = null, frozen = null, files = null, skillHash = null;
|
|
46
|
+
for (const { value: v } of events.slice(1)) {
|
|
47
|
+
switch (v.type) {
|
|
48
|
+
case 'model-prepared':
|
|
49
|
+
if (state !== 'prepared')
|
|
50
|
+
throw Error('invalid model preparation');
|
|
51
|
+
state = 'model-prepared';
|
|
52
|
+
break;
|
|
53
|
+
case 'claim':
|
|
54
|
+
if (!['prepared', 'model-prepared'].includes(state))
|
|
55
|
+
throw Error('invalid supervisor claim');
|
|
56
|
+
state = 'claimed';
|
|
57
|
+
break;
|
|
58
|
+
case 'read':
|
|
59
|
+
if (state !== 'claimed')
|
|
60
|
+
throw Error('invalid read phase');
|
|
61
|
+
break;
|
|
62
|
+
case 'proposed':
|
|
63
|
+
if (state !== 'claimed')
|
|
64
|
+
throw Error('invalid proposal phase');
|
|
65
|
+
hypothesis = buildHypothesis(v.hypothesis.proposal);
|
|
66
|
+
if (hypothesis.id !== v.hypothesis.id)
|
|
67
|
+
throw Error('proposal identity changed');
|
|
68
|
+
state = 'proposed';
|
|
69
|
+
break;
|
|
70
|
+
case 'failed':
|
|
71
|
+
if (state !== 'claimed')
|
|
72
|
+
throw Error('invalid failure phase');
|
|
73
|
+
state = 'failed';
|
|
74
|
+
break;
|
|
75
|
+
case 'approved':
|
|
76
|
+
if (state !== 'proposed')
|
|
77
|
+
throw Error('invalid approval phase');
|
|
78
|
+
approved = true;
|
|
79
|
+
state = 'approved';
|
|
80
|
+
break;
|
|
81
|
+
case 'promotion-pending':
|
|
82
|
+
if (state !== 'approved')
|
|
83
|
+
throw Error('invalid promotion phase');
|
|
84
|
+
preview = v.preview;
|
|
85
|
+
state = 'promotion-pending';
|
|
86
|
+
break;
|
|
87
|
+
case 'promoted':
|
|
88
|
+
if (state !== 'promotion-pending')
|
|
89
|
+
throw Error('invalid promotion completion');
|
|
90
|
+
state = 'promoted';
|
|
91
|
+
break;
|
|
92
|
+
case 'frozen':
|
|
93
|
+
if (state !== 'promoted')
|
|
94
|
+
throw Error('invalid freeze phase');
|
|
95
|
+
frozen = v.frozen;
|
|
96
|
+
files = v.files;
|
|
97
|
+
skillHash = String(v.skillHash);
|
|
98
|
+
state = 'frozen';
|
|
99
|
+
break;
|
|
100
|
+
case 'edit-pending':
|
|
101
|
+
if (state !== 'frozen')
|
|
102
|
+
throw Error('invalid edit phase');
|
|
103
|
+
state = 'edit-pending';
|
|
104
|
+
break;
|
|
105
|
+
case 'edited':
|
|
106
|
+
if (state !== 'edit-pending')
|
|
107
|
+
throw Error('invalid edit completion');
|
|
108
|
+
skillHash = String(v.skillHash);
|
|
109
|
+
state = 'edited';
|
|
110
|
+
break;
|
|
111
|
+
case 'screened':
|
|
112
|
+
if (!['frozen', 'edited'].includes(state))
|
|
113
|
+
throw Error('invalid screen phase');
|
|
114
|
+
break;
|
|
115
|
+
default: throw Error('unknown supervisor event');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return learningCopy({ state, hypothesis, approved, preview, frozen, files, skillHash, selection: selected, tip: events.at(-1).id });
|
|
119
|
+
};
|
|
120
|
+
inspect();
|
|
121
|
+
const append = (prior, value) => journal.append(prior, value);
|
|
122
|
+
const inputs = (files) => ({ specSha256: sha(learningFile(files.spec)), rubricSha256: sha(learningFile(files.rubric)), judgePolicySha256: sha(learningFile(files.judgePolicy)), heldoutSha256: sha(learningFile(files.heldout)), configurationSha256: sha(learningFile(files.configuration)) });
|
|
123
|
+
const evaluation = () => { const s = inspect(); if (!s.frozen || !s.files || !['frozen', 'edited'].includes(s.state))
|
|
124
|
+
throw Error('frozen investigation required'); confirmed(input); assertFrozenInvestigation(s.frozen, inputs(s.files)); if (sha(learningFile(s.files.skill)) !== s.skillHash)
|
|
125
|
+
throw Error('candidate changed outside authorized edit'); return { frozen: s.frozen, caseIds: s.hypothesis.proposal.caseIds, skillSha256: s.skillHash, executionReady: false }; };
|
|
126
|
+
const previewScreen = (request) => {
|
|
127
|
+
const evaluated = evaluation(), s = inspect(), input = learningCopy(request);
|
|
128
|
+
if (Object.keys(input).sort().join() !== 'manifestIds,population' || typeof input.population !== 'string' || !input.population.length || input.population.length > 512 || !Array.isArray(input.manifestIds) || !input.manifestIds.length || input.manifestIds.length > 16 || new Set(input.manifestIds).size !== input.manifestIds.length || input.manifestIds.some(id => !/^[a-f0-9]{64}$/.test(id)))
|
|
129
|
+
throw Error('bounded distinct screen sources required');
|
|
130
|
+
const binding = { input, freeze: evaluated.frozen.digest, skillSha256: evaluated.skillSha256, scenarioId: String(s.preview.scenario.id) };
|
|
131
|
+
return { ...binding, digest: learningHash(binding) };
|
|
132
|
+
};
|
|
133
|
+
return { inspect, evaluation, previewScreen,
|
|
134
|
+
prepareModel(raw) {
|
|
135
|
+
const request = learningCopy(raw), s = inspect();
|
|
136
|
+
confirmed(input);
|
|
137
|
+
if (Object.keys(request).sort().join() !== 'kind,manifestIds,maxInputBytes,proposalLimits' || request.kind !== 'producer-archive-model-v1' || !Number.isSafeInteger(request.maxInputBytes) || request.maxInputBytes < 1 || request.maxInputBytes > 3000 || !Array.isArray(request.manifestIds) || !request.manifestIds.length || new Set(request.manifestIds).size !== request.manifestIds.length || request.manifestIds.length > input.reader.maxCalls || request.manifestIds.some(id => !input.reader.manifestIds.includes(id)) || Object.keys(request.proposalLimits).sort().join() !== 'judgeCalls,subjectCalls,wallMs' || Object.values(request.proposalLimits).some(n => !Number.isSafeInteger(n) || n < 0) || request.proposalLimits.wallMs < 1)
|
|
138
|
+
throw Error('bounded archive-only model request required');
|
|
139
|
+
const old = journal.read().find(e => e.value.type === 'model-prepared');
|
|
140
|
+
if (old) {
|
|
141
|
+
if (learningHash(old.value.request) !== learningHash(request) || s.state !== 'model-prepared')
|
|
142
|
+
throw Error('model preparation changed or already claimed');
|
|
143
|
+
return learningCopy(old.value.prepared);
|
|
144
|
+
}
|
|
145
|
+
if (s.state !== 'prepared')
|
|
146
|
+
throw Error('weekly owner already claimed');
|
|
147
|
+
const reader = createArchiveReadCapability({ ...input.reader, root: input.archiveRoot });
|
|
148
|
+
const sources = request.manifestIds.map(id => { const r = reader.read(id); return { manifestId: r.manifestId, sha256: r.reference.sha256, bytesBase64: r.bytes.toString('base64') }; });
|
|
149
|
+
const text = JSON.stringify({ selection: selected, sources, proposalLimits: request.proposalLimits });
|
|
150
|
+
if (Buffer.byteLength(text) > request.maxInputBytes)
|
|
151
|
+
throw Error('archive model input overbudget');
|
|
152
|
+
const prepared = { input: text, digest: learningHash({ request, input: text, selection: selected }) };
|
|
153
|
+
append(s.tip, { type: 'model-prepared', request, prepared, sourceKind: 'producer-archive-model-v1' });
|
|
154
|
+
return prepared;
|
|
155
|
+
},
|
|
156
|
+
async runModel(preparedDigest, planSha256, invoke) {
|
|
157
|
+
const s = inspect(), prepared = journal.read().find(e => e.value.type === 'model-prepared')?.value;
|
|
158
|
+
if (s.state !== 'model-prepared' || !prepared || prepared.prepared.digest !== preparedDigest || !/^([a-f0-9]{64})$/.test(planSha256))
|
|
159
|
+
throw Error('frozen model preparation required');
|
|
160
|
+
confirmed(input);
|
|
161
|
+
const tip = append(s.tip, { type: 'claim', sourceKind: 'producer-archive-model-v1', preparedDigest, planSha256 }).id;
|
|
162
|
+
try {
|
|
163
|
+
const ref = await invoke(String(prepared.prepared.input)), rows = learningJournal(ref.hostPath).read(), obs = rows.find(r => r.value.type === 'observation' && r.value.id === ref.id), bound = rows.find(r => r.value.type === 'producer-ipc-bound' && r.value.id === ref.id), settled = rows.find(r => r.value.type === 'producer-ipc-settled' && r.value.id === ref.id);
|
|
164
|
+
if (!obs || !bound || !settled || bound.value.bindingHash !== settled.value.bindingHash || bound.value.binding.charterSha256 !== planSha256)
|
|
165
|
+
throw Error('original model settlement required');
|
|
166
|
+
const h = buildHypothesis(JSON.parse(Buffer.from(String(obs.value.outputBase64), 'base64').toString('utf8')));
|
|
167
|
+
if (h.proposal.archiveSnapshot !== selected.archiveSnapshot || h.proposal.population !== selected.population || learningHash([...h.proposal.caseIds].sort()) !== learningHash([...selected.selectedCaseIds].sort()) || learningHash(h.proposal.limits) !== learningHash(prepared.request.proposalLimits))
|
|
168
|
+
throw Error('model proposal scope/limits mismatch');
|
|
169
|
+
confirmed(input);
|
|
170
|
+
append(tip, { type: 'proposed', hypothesis: h, sourceKind: 'producer-archive-model-v1', source: { ...ref, observation: obs.id, settlement: settled.id, planSha256 }, approved: false });
|
|
171
|
+
return h;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
try {
|
|
175
|
+
append(tip, { type: 'failed', reason: 'model-retro-failed; no retry' });
|
|
176
|
+
}
|
|
177
|
+
catch (persist) {
|
|
178
|
+
throw new AggregateError([error, persist], 'retro failure persistence unknown');
|
|
179
|
+
}
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
screen(request, authorizedLinks) {
|
|
184
|
+
const s = inspect(), preview = previewScreen(request);
|
|
185
|
+
if (!authorizedLinks.includes(preview.digest))
|
|
186
|
+
throw Error('independent exact screen linkage authority required');
|
|
187
|
+
if (inspect().tip !== s.tip)
|
|
188
|
+
throw Error('stale screen snapshot');
|
|
189
|
+
const seen = new Set();
|
|
190
|
+
const results = preview.input.manifestIds.map(id => {
|
|
191
|
+
const source = readArchiveSource(input.archiveRoot, id);
|
|
192
|
+
if (source.status !== 'available' || source.reference.retention !== 'exact' || source.reference.parser.id !== 'skill-harness-results' || source.bytes.length > 65536)
|
|
193
|
+
throw Error('retained screen results unavailable');
|
|
194
|
+
if (seen.has(source.reference.sha256))
|
|
195
|
+
throw Error('duplicate screen evidence bytes');
|
|
196
|
+
seen.add(source.reference.sha256);
|
|
197
|
+
const result = validateResults(parseLearningRequest(source.bytes.toString('utf8')));
|
|
198
|
+
if (source.reference.parser.version !== String(result.schema) || typeof result.skill !== 'string' || typeof result.model !== 'string' || new Set(result.scenarios.map(r => r.id)).size !== result.scenarios.length || !result.scenarios.some(r => r.id === preview.scenarioId))
|
|
199
|
+
throw Error('screen result scenario/schema binding mismatch');
|
|
200
|
+
return result;
|
|
201
|
+
});
|
|
202
|
+
const report = screenResults(results), receipt = { ...preview, report: { scenarios: report.scenarios.filter(r => r.id === preview.scenarioId), criteria: report.criteria.filter(r => r.scenario_id === preview.scenarioId) }, exploratory: true, populationMatches: preview.input.population === selected.population, comparisonQualified: false };
|
|
203
|
+
const old = journal.read().find(e => e.value.type === 'screened' && e.value.receipt.digest === receipt.digest);
|
|
204
|
+
if (old) {
|
|
205
|
+
if (learningHash(old.value.receipt) !== learningHash(receipt))
|
|
206
|
+
throw Error('screen receipt changed');
|
|
207
|
+
return receipt;
|
|
208
|
+
}
|
|
209
|
+
append(s.tip, { type: 'screened', receipt });
|
|
210
|
+
return receipt;
|
|
211
|
+
},
|
|
212
|
+
run(program) {
|
|
213
|
+
const before = inspect();
|
|
214
|
+
if (before.state !== 'prepared')
|
|
215
|
+
throw Error('weekly job already claimed; no automatic retry');
|
|
216
|
+
confirmed(input);
|
|
217
|
+
let tip = journal.append(before.tip, { type: 'claim' }).id;
|
|
218
|
+
try {
|
|
219
|
+
const host = learningCopy(program);
|
|
220
|
+
if (host.kind !== 'inert-v1' || Object.keys(host).sort().join() !== 'kind,requests' || !Array.isArray(host.requests) || host.requests.length > 128)
|
|
221
|
+
throw Error('host denied');
|
|
222
|
+
const reader = createArchiveReadCapability({ ...input.reader, root: input.archiveRoot });
|
|
223
|
+
let reads = 0, h = null;
|
|
224
|
+
for (const request of host.requests) {
|
|
225
|
+
if (h)
|
|
226
|
+
throw Error('request after proposal denied');
|
|
227
|
+
if (request.tool === 'archive.read' && Object.keys(request).sort().join() === 'manifestId,tool') {
|
|
228
|
+
const result = reader.read(String(request.manifestId));
|
|
229
|
+
tip = append(tip, { type: 'read', manifestId: result.manifestId, sha256: result.reference.sha256 }).id;
|
|
230
|
+
reads++;
|
|
231
|
+
}
|
|
232
|
+
else if (request.tool === 'hypothesis' && Object.keys(request).sort().join() === 'proposal,tool') {
|
|
233
|
+
h = buildHypothesis(request.proposal);
|
|
234
|
+
if (!reads || h.proposal.archiveSnapshot !== selected.archiveSnapshot || h.proposal.population !== selected.population || learningHash([...h.proposal.caseIds].sort()) !== learningHash([...selected.selectedCaseIds].sort()) || h.proposal.limits.subjectCalls !== 0 || h.proposal.limits.judgeCalls !== 0)
|
|
235
|
+
throw Error('confirmed case/read linkage or inert budget mismatch');
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
throw Error('capability denied');
|
|
239
|
+
}
|
|
240
|
+
if (!h)
|
|
241
|
+
throw Error('hypothesis missing');
|
|
242
|
+
confirmed(input);
|
|
243
|
+
append(tip, { type: 'proposed', hypothesis: h });
|
|
244
|
+
return h;
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
try {
|
|
248
|
+
append(tip, { type: 'failed', reason: 'inert-host-refused-or-failed' });
|
|
249
|
+
}
|
|
250
|
+
catch (persist) {
|
|
251
|
+
throw new AggregateError([error, persist], 'supervisor failed; failure persistence unknown');
|
|
252
|
+
}
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
approve(authority) { const s = inspect(); if (!s.hypothesis)
|
|
257
|
+
throw Error('hypothesis required'); const approval = authorizeInvestigation(s.hypothesis, authority); if (s.approved)
|
|
258
|
+
return approval; if (s.state !== 'proposed')
|
|
259
|
+
throw Error('proposal required'); confirmed(input); append(s.tip, { type: 'approved', approval }); return approval; },
|
|
260
|
+
preview(specPath, scenario) { const s = inspect(); if (!s.approved || !s.hypothesis)
|
|
261
|
+
throw Error('investigation approval required'); confirmed(input); return previewInvestigationScenario(s.hypothesis, specPath, scenario); },
|
|
262
|
+
promote(preview, authority) {
|
|
263
|
+
const s = inspect();
|
|
264
|
+
if (!s.hypothesis || !s.approved)
|
|
265
|
+
throw Error('approved investigation required');
|
|
266
|
+
authorizeInvestigation(s.hypothesis, authority);
|
|
267
|
+
if (!authority.promotions.includes(preview.digest))
|
|
268
|
+
throw Error('exact promotion authority required');
|
|
269
|
+
confirmed(input);
|
|
270
|
+
if (['promoted', 'frozen', 'edited'].includes(s.state) && s.preview?.digest === preview.digest) {
|
|
271
|
+
if (learningHash(s.preview) !== learningHash(preview))
|
|
272
|
+
throw Error('promotion identity changed');
|
|
273
|
+
if (sha(learningFile(preview.specPath)) !== preview.afterSha256)
|
|
274
|
+
throw Error('promoted scenario changed');
|
|
275
|
+
return { scenarioId: String(preview.scenario.id), replayed: true, sha256: preview.afterSha256, scope: 'scenario-only' };
|
|
276
|
+
}
|
|
277
|
+
if (s.state !== 'approved')
|
|
278
|
+
throw Error('pending effects are not retried');
|
|
279
|
+
if (learningHash(previewInvestigationScenario(s.hypothesis, preview.specPath, preview.scenario)) !== learningHash(preview))
|
|
280
|
+
throw Error('promotion identity changed');
|
|
281
|
+
const pending = append(s.tip, { type: 'promotion-pending', preview });
|
|
282
|
+
const result = applyInvestigationScenario(s.hypothesis, preview, authority);
|
|
283
|
+
append(pending.id, { type: 'promoted', result });
|
|
284
|
+
return result;
|
|
285
|
+
},
|
|
286
|
+
freeze(files, authority) { const s = inspect(); if (s.state !== 'promoted' || !s.hypothesis || !s.preview || files.spec !== s.preview.specPath || sha(learningFile(files.spec)) !== s.preview.afterSha256)
|
|
287
|
+
throw Error('promoted scenario binding required'); confirmed(input); const safe = learningCopy(files); for (const key of Object.keys(safe)) {
|
|
288
|
+
learningFile(safe[key]);
|
|
289
|
+
safe[key] = realpathSync(safe[key]);
|
|
290
|
+
} if (Object.keys(safe).sort().join() !== 'configuration,heldout,judgePolicy,rubric,skill,spec' || new Set(Object.values(safe)).size !== 6)
|
|
291
|
+
throw Error('distinct frozen evaluation and candidate paths required'); const frozen = freezeInvestigation(s.hypothesis, inputs(safe), authority); append(s.tip, { type: 'frozen', frozen, files: safe, skillHash: sha(learningFile(safe.skill)) }); return frozen; },
|
|
292
|
+
previewEdit(text) { evaluation(); const s = inspect(); if (s.state !== 'frozen' || typeof text !== 'string' || Buffer.byteLength(text) > 1024 * 1024)
|
|
293
|
+
throw Error('frozen bounded edit required'); return learningHash({ freeze: s.frozen.digest, path: s.files.skill, before: s.skillHash, after: sha(Buffer.from(text)) }); },
|
|
294
|
+
edit(text, authorizedDigests) {
|
|
295
|
+
const id = this.previewEdit(text), s = inspect();
|
|
296
|
+
if (!authorizedDigests.includes(id))
|
|
297
|
+
throw Error('independent exact edit authority required');
|
|
298
|
+
confirmed(input);
|
|
299
|
+
const pending = append(s.tip, { type: 'edit-pending', editId: id });
|
|
300
|
+
const fd = openSync(s.files.skill, constants.O_WRONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
301
|
+
try {
|
|
302
|
+
const stat = fstatSync(fd);
|
|
303
|
+
if (!stat.isFile() || stat.nlink !== 1)
|
|
304
|
+
throw Error('edit destination changed');
|
|
305
|
+
// Whole-file replacement is bounded and explicitly approved. Partial writes remain pending/error.
|
|
306
|
+
const bytes = Buffer.from(text);
|
|
307
|
+
let n = 0;
|
|
308
|
+
while (n < bytes.length) {
|
|
309
|
+
const k = writeSync(fd, bytes, n, bytes.length - n, n);
|
|
310
|
+
if (!k)
|
|
311
|
+
throw Error('edit write stalled');
|
|
312
|
+
n += k;
|
|
313
|
+
}
|
|
314
|
+
// Truncate only this explicitly approved candidate, never an evaluation input or historical evidence.
|
|
315
|
+
truncateCandidate(fd, bytes.length);
|
|
316
|
+
fsyncSync(fd);
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
closeSync(fd);
|
|
320
|
+
}
|
|
321
|
+
const current = sha(learningFile(s.files.skill));
|
|
322
|
+
if (current !== sha(Buffer.from(text)))
|
|
323
|
+
throw Error('edited candidate bytes changed');
|
|
324
|
+
append(pending.id, { type: 'edited', skillHash: current });
|
|
325
|
+
return evaluation();
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
import { ftruncateSync as truncateCandidate } from 'node:fs';
|
|
330
|
+
//# sourceMappingURL=weekly-investigation.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type WorkCaptureCaseV2 } from "@skill-harness/core";
|
|
2
|
+
import type { WorkProjection } from "./generated/retention-v2-work-types.js";
|
|
3
|
+
export interface WorkDetectionOptions {
|
|
4
|
+
version: string;
|
|
5
|
+
population: string;
|
|
6
|
+
scopeDigest: string;
|
|
7
|
+
minEquivalentAttempts: number;
|
|
8
|
+
/** Explicit frozen domain facts, never inferred from worker prose. Values are obligation digests. */
|
|
9
|
+
expectedWaits: string[];
|
|
10
|
+
expectedFailures: string[];
|
|
11
|
+
exemplar?: {
|
|
12
|
+
unit: "wall_ms" | "tool_calls" | "usd";
|
|
13
|
+
maximum: number;
|
|
14
|
+
};
|
|
15
|
+
usage?: Record<string, {
|
|
16
|
+
observed: boolean;
|
|
17
|
+
cost: number;
|
|
18
|
+
unit: "wall_ms" | "tool_calls" | "usd";
|
|
19
|
+
evidence: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export declare const WORK_CANDIDATE_IMPLEMENTATION = "structural-work-v2";
|
|
23
|
+
/** Consumes the actual P01 host projection, not arbitrary native wire or a new acceptance authority. */
|
|
24
|
+
export declare function detectWorkCandidates(work: WorkProjection, options: WorkDetectionOptions): {
|
|
25
|
+
version: "work-candidates-v1";
|
|
26
|
+
cases: WorkCaptureCaseV2[];
|
|
27
|
+
issues: string[];
|
|
28
|
+
expected: string[];
|
|
29
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { buildWorkCapture } from "@skill-harness/core";
|
|
3
|
+
export const WORK_CANDIDATE_IMPLEMENTATION = "structural-work-v2";
|
|
4
|
+
const hash = /^[a-f0-9]{64}$/;
|
|
5
|
+
/** Consumes the actual P01 host projection, not arbitrary native wire or a new acceptance authority. */
|
|
6
|
+
export function detectWorkCandidates(work, options) {
|
|
7
|
+
if (![options.version, options.population].every(s => typeof s === "string" && s.length > 0 && s.length <= 128 && !/[\u0000-\u001f\u007f]/.test(s))
|
|
8
|
+
|| !Array.isArray(options.expectedWaits) || !Array.isArray(options.expectedFailures)
|
|
9
|
+
|| options.expectedWaits.length > 4096 || options.expectedFailures.length > 4096
|
|
10
|
+
|| options.expectedFailures.some(id => typeof id !== "string" || !id || id.length > 512)
|
|
11
|
+
|| !Number.isSafeInteger(options.minEquivalentAttempts) || options.minEquivalentAttempts < 2 || options.minEquivalentAttempts > 256
|
|
12
|
+
|| !hash.test(options.scopeDigest) || options.expectedWaits.some(d => !hash.test(d))
|
|
13
|
+
|| (options.exemplar && (!Number.isFinite(options.exemplar.maximum) || options.exemplar.maximum < 0)))
|
|
14
|
+
throw new Error("invalid structural detector policy");
|
|
15
|
+
if (work.selectedSnapshot && work.selectedSnapshot.digest !== options.scopeDigest)
|
|
16
|
+
throw new Error("detector scope mismatch");
|
|
17
|
+
if (work.scopeState !== "valid" || !work.selectedSnapshot || !work.runtime || work.errors.length) {
|
|
18
|
+
return { version: "work-candidates-v1", cases: [], issues: ["work-scope-not-resolved"], expected: [] };
|
|
19
|
+
}
|
|
20
|
+
const policyDigest = createHash("sha256").update(JSON.stringify({ minEquivalentAttempts: options.minEquivalentAttempts, exemplar: options.exemplar ? { unit: options.exemplar.unit, maximum: options.exemplar.maximum } : null })).digest("hex");
|
|
21
|
+
const version = `${WORK_CANDIDATE_IMPLEMENTATION}:${options.version}:${policyDigest}`;
|
|
22
|
+
const cases = [], expected = [];
|
|
23
|
+
const waits = new Set(options.expectedWaits), expectedFailures = new Set(options.expectedFailures);
|
|
24
|
+
const occurrences = work.runtime.occurrences;
|
|
25
|
+
for (const obligation of work.obligations) {
|
|
26
|
+
const ref = obligation.binding.obligation;
|
|
27
|
+
const target = { kind: "work", snapshotDigest: work.selectedSnapshot.digest, obligationId: ref.id, obligationDigest: ref.digest };
|
|
28
|
+
const evidence = [target.snapshotDigest, target.obligationDigest];
|
|
29
|
+
const add = (reason, classification, metrics, refs = evidence) => {
|
|
30
|
+
cases.push(buildWorkCapture({ detector: { id: reason, version, population: options.population }, target, reason, classification, metrics, evidence: refs }));
|
|
31
|
+
};
|
|
32
|
+
if (waits.has(ref.digest)) {
|
|
33
|
+
expected.push(`declared-wait:${ref.digest}`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (obligation.acceptance === "unresolved" || obligation.artifactCoverage.state !== "available" || obligation.evidenceCoverage.state !== "available" || obligation.problems.some(problem => problem.code !== "TRUSTED_REJECTION")) {
|
|
37
|
+
add("coverage_gap", "coverage_issue", {});
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
for (const attempt of work.runtime.attempts)
|
|
41
|
+
if (expectedFailures.has(attempt.executionId) && attempt.bindings.some(b => b.obligation.digest === ref.digest))
|
|
42
|
+
expected.push(`expected-failure:${attempt.executionId}`);
|
|
43
|
+
const attempts = work.runtime.attempts.filter(attempt => attempt.resolution === "resolved"
|
|
44
|
+
&& !expectedFailures.has(attempt.executionId)
|
|
45
|
+
&& attempt.bindings.some(b => b.obligation.id === ref.id && b.obligation.digest === ref.digest)
|
|
46
|
+
&& occurrences.some(o => o.payload.executionId === attempt.executionId && o.payload.obligation.id === ref.id && o.payload.obligation.digest === ref.digest
|
|
47
|
+
&& o.payload.provenance === "observed" && ["completed", "failed"].includes(o.payload.state)));
|
|
48
|
+
if (obligation.acceptance === "accepted-under-supplied-authority") {
|
|
49
|
+
if (!work.authoritySnapshot || !options.exemplar || !attempts.length)
|
|
50
|
+
continue;
|
|
51
|
+
const usage = attempts.map(a => options.usage?.[a.executionId]);
|
|
52
|
+
if (usage.some(u => !u || !u.observed || u.unit !== options.exemplar.unit || !Number.isFinite(u.cost) || u.cost < 0 || !hash.test(u.evidence)))
|
|
53
|
+
continue;
|
|
54
|
+
const measuredCost = usage.reduce((sum, u) => sum + u.cost, 0);
|
|
55
|
+
if (measuredCost <= options.exemplar.maximum)
|
|
56
|
+
add("economical_exemplar", "candidate_exemplar", { measuredCost, costLimit: options.exemplar.maximum, costUnit: options.exemplar.unit }, [...evidence, ...usage.map(u => u.evidence)]);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const groups = new Map();
|
|
60
|
+
for (const attempt of attempts) {
|
|
61
|
+
const config = attempt.effectiveLabels.configurationDigest;
|
|
62
|
+
if (!config || !hash.test(config) || !attempt.observedLabels.some(label => label.configurationDigest === config))
|
|
63
|
+
continue;
|
|
64
|
+
const bindings = attempt.bindings.filter(b => b.obligation.id === ref.id && b.obligation.digest === ref.digest);
|
|
65
|
+
const bindingKeys = bindings.map(b => JSON.stringify({ variants: [...b.variantIds].sort(), artifacts: b.artifacts.map(a => a.digest).sort() })).sort();
|
|
66
|
+
const key = JSON.stringify([config, attempt.effectiveLabels.modelId, attempt.effectiveLabels.effortId, bindingKeys]);
|
|
67
|
+
groups.set(key, [...(groups.get(key) ?? []), attempt]);
|
|
68
|
+
}
|
|
69
|
+
const strongest = [...groups].sort(([ka, a], [kb, b]) => b.length - a.length || (ka < kb ? -1 : ka > kb ? 1 : 0))[0]?.[1] ?? [];
|
|
70
|
+
const ids = new Set(strongest.map(a => a.executionId));
|
|
71
|
+
if (ids.size >= options.minEquivalentAttempts) {
|
|
72
|
+
const refs = occurrences.filter(o => ids.has(o.payload.executionId) && o.payload.obligation.id === ref.id && o.payload.obligation.digest === ref.digest).map(o => o.event.digest);
|
|
73
|
+
add("repeat_without_progress", "candidate_defect", { equivalentAttempts: ids.size }, [...evidence, ...refs]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { version: "work-candidates-v1", cases: cases.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0), issues: [], expected: expected.sort() };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=work-candidates.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type WorkCaptureCaseV2 } from "@skill-harness/core";
|
|
2
|
+
import { type WorkDetectionOptions } from "./work-candidates.js";
|
|
3
|
+
import type { WorkProjection } from "./generated/retention-v2-work-types.js";
|
|
4
|
+
/** Explicit private archival, never promotion into a scored specification or automatic alert exposure. */
|
|
5
|
+
export declare function retainWorkCandidate(root: string, candidate: WorkCaptureCaseV2): string;
|
|
6
|
+
export declare function readWorkCandidate(root: string, manifestId: string): WorkCaptureCaseV2;
|
|
7
|
+
/** Capture the actual trusted P01 projection before retaining advisory cases; never a worker callback. */
|
|
8
|
+
export declare function captureWorkCandidates(root: string, work: WorkProjection, options: WorkDetectionOptions): {
|
|
9
|
+
batchId: string;
|
|
10
|
+
observationId: string;
|
|
11
|
+
candidateIds: string[];
|
|
12
|
+
};
|
|
13
|
+
/** Several observations/detector versions of the same scoped work are not independent incidents. */
|
|
14
|
+
export declare function groupWorkIncidents(cases: readonly WorkCaptureCaseV2[]): {
|
|
15
|
+
key: string;
|
|
16
|
+
target: {
|
|
17
|
+
kind: "work";
|
|
18
|
+
snapshotDigest: string;
|
|
19
|
+
obligationId: string;
|
|
20
|
+
obligationDigest: string;
|
|
21
|
+
};
|
|
22
|
+
observations: WorkCaptureCaseV2[];
|
|
23
|
+
status: "unresolved";
|
|
24
|
+
visibility: "silent";
|
|
25
|
+
}[];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { buildWorkCapture } from "@skill-harness/core";
|
|
2
|
+
import { readArchiveSource, retainArchiveSource } from "./evidence-archive.js";
|
|
3
|
+
import { detectWorkCandidates } from "./work-candidates.js";
|
|
4
|
+
function validate(value) {
|
|
5
|
+
if (!value || value.capture_schema !== 2 || value.status !== "unresolved" || value.visibility !== "silent" || value.causalAttribution !== "not-established")
|
|
6
|
+
throw new Error("invalid work case state");
|
|
7
|
+
const built = buildWorkCapture({ detector: value.detector, target: value.target, classification: value.classification,
|
|
8
|
+
reason: value.reason, evidence: value.evidence, metrics: value.metrics });
|
|
9
|
+
if (built.id !== value.id || Object.keys(value).sort().join() !== Object.keys(built).sort().join())
|
|
10
|
+
throw new Error("invalid work case identity");
|
|
11
|
+
return built;
|
|
12
|
+
}
|
|
13
|
+
/** Explicit private archival, never promotion into a scored specification or automatic alert exposure. */
|
|
14
|
+
export function retainWorkCandidate(root, candidate) {
|
|
15
|
+
const value = validate(candidate);
|
|
16
|
+
return retainArchiveSource(root, { sourceId: `work-case-${value.id}`, parser: { id: "work-capture", version: "2" }, retention: "exact", bytes: Buffer.from(JSON.stringify(value)) }).manifestId;
|
|
17
|
+
}
|
|
18
|
+
export function readWorkCandidate(root, manifestId) {
|
|
19
|
+
const source = readArchiveSource(root, manifestId);
|
|
20
|
+
if (source.status !== "available" || source.reference.retention !== "exact" || source.reference.parser.id !== "work-capture" || source.reference.parser.version !== "2")
|
|
21
|
+
throw new Error("work case missing or invalid");
|
|
22
|
+
const value = validate(JSON.parse(source.bytes.toString("utf8")));
|
|
23
|
+
if (source.reference.sourceId !== `work-case-${value.id}`)
|
|
24
|
+
throw new Error("work case source mismatch");
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
/** Capture the actual trusted P01 projection before retaining advisory cases; never a worker callback. */
|
|
28
|
+
export function captureWorkCandidates(root, work, options) {
|
|
29
|
+
const detection = detectWorkCandidates(work, options);
|
|
30
|
+
const observation = retainArchiveSource(root, {
|
|
31
|
+
sourceId: `work-facts-${options.scopeDigest}`, parser: { id: "pi-daddy-work-projection", version: "1" },
|
|
32
|
+
retention: "exact", bytes: Buffer.from(JSON.stringify(work)),
|
|
33
|
+
});
|
|
34
|
+
const candidates = detection.cases.map(candidate => buildWorkCapture({
|
|
35
|
+
detector: candidate.detector, target: candidate.target, classification: candidate.classification,
|
|
36
|
+
reason: candidate.reason, metrics: candidate.metrics, evidence: [...candidate.evidence, observation.reference.sha256],
|
|
37
|
+
}));
|
|
38
|
+
const candidateIds = candidates.map(candidate => retainWorkCandidate(root, candidate));
|
|
39
|
+
const batch = { version: "work-candidate-batch-v1", observationId: observation.manifestId, candidateIds,
|
|
40
|
+
issues: detection.issues, expected: detection.expected, visibility: "silent", promotion: "not-authorized" };
|
|
41
|
+
const stored = retainArchiveSource(root, { sourceId: `case-batch-${options.scopeDigest}`, parser: { id: "work-candidate-batch", version: "1" },
|
|
42
|
+
retention: "exact", bytes: Buffer.from(JSON.stringify(batch)) });
|
|
43
|
+
return { batchId: stored.manifestId, observationId: observation.manifestId, candidateIds };
|
|
44
|
+
}
|
|
45
|
+
/** Several observations/detector versions of the same scoped work are not independent incidents. */
|
|
46
|
+
export function groupWorkIncidents(cases) {
|
|
47
|
+
if (cases.length > 4096)
|
|
48
|
+
throw new Error("work incident snapshot exceeds bound");
|
|
49
|
+
const groups = new Map();
|
|
50
|
+
for (const raw of cases) {
|
|
51
|
+
const c = validate(raw);
|
|
52
|
+
const key = JSON.stringify([c.target.snapshotDigest, c.target.obligationId, c.target.obligationDigest, c.detector.population]);
|
|
53
|
+
const values = groups.get(key) ?? [];
|
|
54
|
+
if (!values.some(v => v.id === c.id && JSON.stringify(v) === JSON.stringify(c)))
|
|
55
|
+
values.push(c);
|
|
56
|
+
groups.set(key, values);
|
|
57
|
+
}
|
|
58
|
+
return [...groups].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, values]) => ({
|
|
59
|
+
key, target: values[0].target, observations: values.sort((a, b) => { const left = JSON.stringify(a), right = JSON.stringify(b); return left < right ? -1 : left > right ? 1 : 0; }),
|
|
60
|
+
status: "unresolved", visibility: "silent",
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=work-case-archive.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type WorkCaseDecision, type WorkCaseDisposition } from "@skill-harness/core";
|
|
2
|
+
export interface WorkCaseReviewRequest {
|
|
3
|
+
caseManifestId: string;
|
|
4
|
+
priorDecisionId: string | null;
|
|
5
|
+
disposition: WorkCaseDisposition;
|
|
6
|
+
note: string;
|
|
7
|
+
}
|
|
8
|
+
/** Operator-selected batch/author capability. Not user authentication, worker authority or scenario promotion. */
|
|
9
|
+
export declare function createWorkCaseReviewer(root: string, batchId: string, author: string): {
|
|
10
|
+
history: (caseManifestId: string) => WorkCaseDecision[];
|
|
11
|
+
list(offset?: number, limit?: number): {
|
|
12
|
+
total: number;
|
|
13
|
+
offset: number;
|
|
14
|
+
items: {
|
|
15
|
+
caseManifestId: string;
|
|
16
|
+
candidate: import("@skill-harness/core").WorkCaptureCaseV2;
|
|
17
|
+
priorDecisionId: string | null;
|
|
18
|
+
disposition: string;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
decide(request: WorkCaseReviewRequest): {
|
|
22
|
+
current: WorkCaseDecision;
|
|
23
|
+
replayed: boolean;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/** Explicit new signal contract; the existing v2 entry point still refuses these batches. */
|
|
27
|
+
export declare function createWorkSignalReviewer(root: string, batchId: string, author: string): {
|
|
28
|
+
list(offset?: number, limit?: number): {
|
|
29
|
+
observationId: string;
|
|
30
|
+
issues: string[];
|
|
31
|
+
total: number;
|
|
32
|
+
offset: number;
|
|
33
|
+
items: {
|
|
34
|
+
caseManifestId: string;
|
|
35
|
+
candidate: import("@skill-harness/core").WorkCaptureCaseV2 | import("@skill-harness/core").WorkCaptureCaseV3;
|
|
36
|
+
priorDecisionId: string | null;
|
|
37
|
+
disposition: string;
|
|
38
|
+
}[];
|
|
39
|
+
};
|
|
40
|
+
history: (caseManifestId: string) => WorkCaseDecision[];
|
|
41
|
+
decide(request: WorkCaseReviewRequest): {
|
|
42
|
+
current: WorkCaseDecision;
|
|
43
|
+
replayed: boolean;
|
|
44
|
+
};
|
|
45
|
+
};
|