@alisio/core 0.7.0 → 0.8.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/application.d.ts +15 -0
- package/dist/application.js +38 -3
- package/dist/changelog/data.js +23 -0
- package/dist/commands/builtins.js +7 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +32 -0
- package/dist/core/runner.d.ts +4 -0
- package/dist/core/runner.js +57 -1
- package/dist/eval/engine.d.ts +1 -0
- package/dist/eval/engine.js +3 -0
- package/dist/eval/secrets.d.ts +2 -0
- package/dist/eval/secrets.js +10 -0
- package/dist/goal/command.d.ts +7 -2
- package/dist/goal/command.js +10 -2
- package/dist/goal/format.js +2 -0
- package/dist/goal/machine.d.ts +50 -3
- package/dist/goal/machine.js +80 -2
- package/dist/goal/service.d.ts +22 -2
- package/dist/goal/service.js +94 -7
- package/dist/goal/store.js +22 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/plan/criteria.d.ts +30 -0
- package/dist/plan/criteria.js +256 -0
- package/dist/plan/exit-plan.d.ts +8 -0
- package/dist/plan/exit-plan.js +89 -8
- package/dist/plan/manifest.d.ts +4 -2
- package/dist/plan/manifest.js +6 -1
- package/dist/plan/state.d.ts +13 -2
- package/dist/plan/state.js +7 -1
- package/dist/plugins/host.d.ts +7 -0
- package/dist/plugins/host.js +15 -0
- package/dist/proof/command.d.ts +24 -0
- package/dist/proof/command.js +109 -0
- package/dist/proof/coverage.d.ts +40 -0
- package/dist/proof/coverage.js +102 -0
- package/dist/proof/divergence.d.ts +24 -0
- package/dist/proof/divergence.js +120 -0
- package/dist/proof/export.d.ts +65 -0
- package/dist/proof/export.js +144 -0
- package/dist/proof/index.d.ts +12 -0
- package/dist/proof/index.js +12 -0
- package/dist/proof/ledger.d.ts +38 -0
- package/dist/proof/ledger.js +57 -0
- package/dist/proof/plugin-verifiers.d.ts +23 -0
- package/dist/proof/plugin-verifiers.js +39 -0
- package/dist/proof/recorder.d.ts +69 -0
- package/dist/proof/recorder.js +347 -0
- package/dist/proof/scope.d.ts +12 -0
- package/dist/proof/scope.js +121 -0
- package/dist/proof/store.d.ts +52 -0
- package/dist/proof/store.js +192 -0
- package/dist/proof/tree.d.ts +6 -0
- package/dist/proof/tree.js +78 -0
- package/dist/proof/types.d.ts +21 -0
- package/dist/proof/types.js +1 -0
- package/dist/proof/verifiers.d.ts +46 -0
- package/dist/proof/verifiers.js +176 -0
- package/dist/proof/verify.d.ts +36 -0
- package/dist/proof/verify.js +171 -0
- package/dist/runtime/store.js +60 -0
- package/dist/tools/goal.d.ts +5 -1
- package/dist/tools/goal.js +35 -4
- package/package.json +2 -2
package/dist/goal/machine.js
CHANGED
|
@@ -44,11 +44,46 @@ function moveTo(goal, now, status, reason, extra = {}) {
|
|
|
44
44
|
...extra,
|
|
45
45
|
}, true);
|
|
46
46
|
}
|
|
47
|
-
const refuse = (code, message) => ({
|
|
47
|
+
const refuse = (code, message, unmet) => ({
|
|
48
48
|
ok: false,
|
|
49
49
|
code,
|
|
50
50
|
message,
|
|
51
|
+
...(unmet ? { unmet } : {}),
|
|
51
52
|
});
|
|
53
|
+
/** The criteria that apply to a goal: its own and the session-wide ones (no `goalId`). */
|
|
54
|
+
export const criteriaForGoal = (ledger, goalId) => ledger.criteria.filter((c) => c.goalId === undefined || c.goalId === goalId);
|
|
55
|
+
/**
|
|
56
|
+
* Required criteria that are `failed` or `not_checked`. `manual_review` and `not_applicable` do
|
|
57
|
+
* not block (O6 names only the first two; a manual review is the user's own call).
|
|
58
|
+
*/
|
|
59
|
+
export function unmetRequired(criteria) {
|
|
60
|
+
const unmet = [];
|
|
61
|
+
for (const c of criteria)
|
|
62
|
+
if (c.required && (c.status === "failed" || c.status === "not_checked"))
|
|
63
|
+
unmet.push({ criterionId: c.criterionId, text: c.text, status: c.status });
|
|
64
|
+
return unmet;
|
|
65
|
+
}
|
|
66
|
+
const proofInfo = (proof, unmet, override) => ({
|
|
67
|
+
criteria: proof.criteria.map((c) => ({
|
|
68
|
+
criterionId: c.criterionId,
|
|
69
|
+
required: c.required,
|
|
70
|
+
status: c.status,
|
|
71
|
+
})),
|
|
72
|
+
unmet: unmet.map((u) => u.criterionId),
|
|
73
|
+
linkedEvidence: [...new Set(proof.linkedEvidence)],
|
|
74
|
+
...(override ? { override } : {}),
|
|
75
|
+
});
|
|
76
|
+
const UNMET_LISTED = 5;
|
|
77
|
+
const unmetMessage = (unmet) => {
|
|
78
|
+
const listed = unmet
|
|
79
|
+
.slice(0, UNMET_LISTED)
|
|
80
|
+
.map((u) => `"${clip(u.text, 80)}" (${u.status})`)
|
|
81
|
+
.join(", ");
|
|
82
|
+
const more = unmet.length > UNMET_LISTED ? ` and ${unmet.length - UNMET_LISTED} more` : "";
|
|
83
|
+
return (`Completion refused: required acceptance criteria are not verified: ${listed}${more}. ` +
|
|
84
|
+
"Run their verifiers and report again, or report the goal blocked. Only the user can " +
|
|
85
|
+
"override this.");
|
|
86
|
+
};
|
|
52
87
|
/** A new goal (the user's `/goal <objective>`). The caller supplies the id and the limits. */
|
|
53
88
|
export function newGoal(input) {
|
|
54
89
|
return {
|
|
@@ -146,17 +181,59 @@ export function userBudget(goal, now, tokens, ownerPid) {
|
|
|
146
181
|
* The model reports the goal complete. Evidence is mandatory, and only an `active` goal can be
|
|
147
182
|
* completed (a goal the user paused stays paused).
|
|
148
183
|
*/
|
|
149
|
-
export function modelComplete(goal, now, report) {
|
|
184
|
+
export function modelComplete(goal, now, report, proof) {
|
|
150
185
|
if (goal.status !== "active")
|
|
151
186
|
return refuse("not_active", `The goal is ${goal.status}; only an active goal can be completed.`);
|
|
152
187
|
if (!report.evidence.length)
|
|
153
188
|
return refuse("not_allowed", "Completion needs evidence (files, tests, logs or commands).");
|
|
189
|
+
// O6: with criteria the ledger decides, not the model's declaration (free text backs nothing).
|
|
190
|
+
const unmet = proof ? unmetRequired(proof.criteria) : [];
|
|
191
|
+
if (unmet.length)
|
|
192
|
+
return refuse("not_allowed", unmetMessage(unmet), unmet);
|
|
154
193
|
return {
|
|
155
194
|
ok: true,
|
|
156
195
|
goal: moveTo(goal, now, "complete", "model_complete", {
|
|
157
196
|
summary: report.summary,
|
|
158
197
|
evidence: report.evidence,
|
|
159
198
|
inflight: goal.inflight,
|
|
199
|
+
...(proof?.criteria.length ? { proof: proofInfo(proof, unmet) } : {}),
|
|
200
|
+
}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/** Different runs that may be refused a completion over the ledger before the goal pauses. */
|
|
204
|
+
export const COMPLETION_REFUSAL_LIMIT = 3;
|
|
205
|
+
/**
|
|
206
|
+
* A model that keeps reporting `complete` over unmet required criteria, run after run, is not
|
|
207
|
+
* making progress: the goal pauses as `no_progress` (the user resumes or overrides it). The caller
|
|
208
|
+
* counts the refusals; the in-flight claim stays so the running turn still settles.
|
|
209
|
+
*/
|
|
210
|
+
export function pauseForRefusedCompletions(goal, now) {
|
|
211
|
+
return moveTo(goal, now, "paused", "no_progress", {
|
|
212
|
+
detail: "completion_refused",
|
|
213
|
+
inflight: goal.inflight,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/** The longest override reason kept. */
|
|
217
|
+
export const GOAL_OVERRIDE_REASON_MAX = 500;
|
|
218
|
+
/**
|
|
219
|
+
* The USER completes a goal over unmet required criteria (O6). It exists only to bypass that
|
|
220
|
+
* gate: with nothing unmet there is nothing to override. Only a goal that can still be worked
|
|
221
|
+
* on (`active`, `paused`, `blocked`) can be overridden; `budget_limited` and `complete` are final
|
|
222
|
+
* (raise the budget first). The in-flight claim stays, as for a model completion: the running
|
|
223
|
+
* turn still settles. The caller (the service) has already checked that a person asked for it.
|
|
224
|
+
*/
|
|
225
|
+
export function userOverrideComplete(goal, now, input) {
|
|
226
|
+
if (goal.status !== "active" && goal.status !== "paused" && goal.status !== "blocked")
|
|
227
|
+
return refuse("not_allowed", `The goal is ${goal.status}; only an active, paused or blocked goal can be overridden.`);
|
|
228
|
+
const unmet = unmetRequired(input.proof.criteria);
|
|
229
|
+
if (!unmet.length)
|
|
230
|
+
return refuse("not_allowed", "Nothing to override: no required criterion is failed or not checked.");
|
|
231
|
+
const reason = clip(input.reason.trim(), GOAL_OVERRIDE_REASON_MAX);
|
|
232
|
+
return {
|
|
233
|
+
ok: true,
|
|
234
|
+
goal: moveTo(goal, now, "complete", "user_override", {
|
|
235
|
+
proof: proofInfo(input.proof, unmet, { reason, at: now }),
|
|
236
|
+
inflight: goal.inflight,
|
|
160
237
|
}),
|
|
161
238
|
};
|
|
162
239
|
}
|
|
@@ -336,6 +413,7 @@ export function toGoalInfo(goal) {
|
|
|
336
413
|
maxWallMs: goal.maxWallMs,
|
|
337
414
|
...(goal.summary ? { summary: goal.summary } : {}),
|
|
338
415
|
...(goal.evidence?.length ? { evidence: goal.evidence } : {}),
|
|
416
|
+
...(goal.proof ? { proof: goal.proof } : {}),
|
|
339
417
|
actions: userActions(goal.status),
|
|
340
418
|
createdAt: goal.createdAt,
|
|
341
419
|
updatedAt: goal.updatedAt,
|
package/dist/goal/service.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { GoalEvidence, GoalInfo, GoalWaiting } from "@alisio/sdk";
|
|
1
|
+
import type { GoalEvidence, GoalInfo, GoalWaiting, ProofLedger } from "@alisio/sdk";
|
|
2
2
|
import { type GoalBlocker, type GoalHostState } from "./blockers.ts";
|
|
3
|
-
import { type GoalLimits, type GoalLog, type GoalRecord, type GoalRunOutcome } from "./machine.ts";
|
|
3
|
+
import { type GoalLimits, type GoalLog, type GoalRecord, type GoalRunOutcome, type GoalUnmet } from "./machine.ts";
|
|
4
4
|
import { type GoalPrompt } from "./prompts.ts";
|
|
5
5
|
import { type GoalExpectation, type GoalStore } from "./store.ts";
|
|
6
6
|
/** `config.goal`, as the service reads it (live: a settings change applies to the next call). */
|
|
@@ -58,6 +58,13 @@ export interface GoalServiceOptions {
|
|
|
58
58
|
onChange?: (sessionId: string, goal: GoalRecord | undefined) => void;
|
|
59
59
|
/** Breaker observations (counts only; never the objective or the reply). */
|
|
60
60
|
onLog?: (sessionId: string, log: GoalLog) => void;
|
|
61
|
+
/**
|
|
62
|
+
* Where the Proof ledger comes from (`ProofStore` fits). Without it criteria do not exist for
|
|
63
|
+
* the goal: completion behaves as in 0.7.x and a declared `evidenceId` is dropped.
|
|
64
|
+
*/
|
|
65
|
+
proof?: {
|
|
66
|
+
ledger(sessionId: string): ProofLedger;
|
|
67
|
+
};
|
|
61
68
|
}
|
|
62
69
|
export declare class GoalService {
|
|
63
70
|
private readonly options;
|
|
@@ -93,6 +100,10 @@ export declare class GoalService {
|
|
|
93
100
|
next(sessionId: string, view: GoalHostView): GoalDecision;
|
|
94
101
|
/** The claimed continuation could not start: give the claim back (the next call may retry). */
|
|
95
102
|
abandon(sessionId: string, requestId: string, kind: GoalPrompt["kind"]): void;
|
|
103
|
+
/** Runs whose `complete` the ledger refused, per goal (memory only: a restart pauses goals). */
|
|
104
|
+
private readonly refusals;
|
|
105
|
+
/** Records a refused completion of `runId`; true once enough different runs were refused. */
|
|
106
|
+
private refusedRuns;
|
|
96
107
|
/** `update_goal` (the model): the only transitions the model can cause. */
|
|
97
108
|
report(sessionId: string, runId: string | undefined, report: {
|
|
98
109
|
status: "complete" | "blocked";
|
|
@@ -107,7 +118,16 @@ export declare class GoalService {
|
|
|
107
118
|
ok: false;
|
|
108
119
|
code: GoalErrorCode;
|
|
109
120
|
message: string;
|
|
121
|
+
unmet?: GoalUnmet[];
|
|
110
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* The USER completes the goal over unmet required criteria (O6). A separate method on purpose:
|
|
125
|
+
* `report` is the model's only door and has no way to carry an override, so the model cannot
|
|
126
|
+
* reach this path whatever it sends. Hosts call it from a user action only (Phase 5 surfaces).
|
|
127
|
+
*/
|
|
128
|
+
overrideComplete(sessionId: string, input: {
|
|
129
|
+
reason: string;
|
|
130
|
+
}, expect?: GoalExpectation): GoalOutcome;
|
|
111
131
|
/** Startup: goals whose driver process is gone pause with reason `restart`. */
|
|
112
132
|
recover(): GoalRecord[];
|
|
113
133
|
}
|
package/dist/goal/service.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { randomUUID } from "node:crypto";
|
|
19
19
|
import { goalBlocker, waitingOf } from "./blockers.js";
|
|
20
20
|
import { objectiveError } from "./command.js";
|
|
21
|
-
import { checkCaps, DEFAULT_GOAL_LIMITS, modelBlocked, modelComplete, newGoal, settleRun, toGoalInfo, userBudget, userEdit, userPause, userResume, } from "./machine.js";
|
|
21
|
+
import { COMPLETION_REFUSAL_LIMIT, checkCaps, criteriaForGoal, DEFAULT_GOAL_LIMITS, modelBlocked, modelComplete, newGoal, pauseForRefusedCompletions, settleRun, toGoalInfo, userBudget, userEdit, userOverrideComplete, userPause, userResume, } from "./machine.js";
|
|
22
22
|
import { buildGoalPrompt } from "./prompts.js";
|
|
23
23
|
export const settingsToLimits = (settings) => ({
|
|
24
24
|
maxTurns: settings.maxTurns,
|
|
@@ -27,6 +27,24 @@ export const settingsToLimits = (settings) => ({
|
|
|
27
27
|
noToolTurnsLimit: settings.noToolTurnsLimit,
|
|
28
28
|
blockedRepeats: settings.blockedRepeats,
|
|
29
29
|
});
|
|
30
|
+
/**
|
|
31
|
+
* The declared evidence with every `evidenceId` that does not resolve to captured evidence of the
|
|
32
|
+
* session removed (such a declaration is free text and backs nothing), and the ids that resolved.
|
|
33
|
+
*/
|
|
34
|
+
function linkEvidence(evidence, ledger) {
|
|
35
|
+
const known = new Set(ledger?.evidence.map((e) => e.evidenceId));
|
|
36
|
+
const linked = [];
|
|
37
|
+
const out = evidence.map((item) => {
|
|
38
|
+
const { evidenceId, ...rest } = item;
|
|
39
|
+
if (evidenceId === undefined)
|
|
40
|
+
return item;
|
|
41
|
+
if (!known.has(evidenceId))
|
|
42
|
+
return rest;
|
|
43
|
+
linked.push(evidenceId);
|
|
44
|
+
return item;
|
|
45
|
+
});
|
|
46
|
+
return { evidence: out, linked };
|
|
47
|
+
}
|
|
30
48
|
export class GoalService {
|
|
31
49
|
options;
|
|
32
50
|
store;
|
|
@@ -109,6 +127,9 @@ export class GoalService {
|
|
|
109
127
|
if (!this.enabled)
|
|
110
128
|
return { ok: false, code: "disabled", message: "Goals are turned off (goal.enabled)." };
|
|
111
129
|
const limits = this.limits();
|
|
130
|
+
const current = this.store.get(sessionId);
|
|
131
|
+
if (current)
|
|
132
|
+
this.refusals.delete(current.goalId);
|
|
112
133
|
return this.act(sessionId, expect, (goal) => userResume(goal, this.clock(), limits, this.pid));
|
|
113
134
|
}
|
|
114
135
|
edit(sessionId, objective, expect) {
|
|
@@ -232,17 +253,49 @@ export class GoalService {
|
|
|
232
253
|
if (result.ok)
|
|
233
254
|
this.emit(sessionId, result.goal);
|
|
234
255
|
}
|
|
256
|
+
/** Runs whose `complete` the ledger refused, per goal (memory only: a restart pauses goals). */
|
|
257
|
+
refusals = new Map();
|
|
258
|
+
/** Records a refused completion of `runId`; true once enough different runs were refused. */
|
|
259
|
+
refusedRuns(goalId, runId) {
|
|
260
|
+
const runs = this.refusals.get(goalId) ?? new Set();
|
|
261
|
+
runs.add(runId);
|
|
262
|
+
this.refusals.set(goalId, runs);
|
|
263
|
+
if (runs.size < COMPLETION_REFUSAL_LIMIT)
|
|
264
|
+
return false;
|
|
265
|
+
this.refusals.delete(goalId);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
235
268
|
/** `update_goal` (the model): the only transitions the model can cause. */
|
|
236
269
|
report(sessionId, runId, report) {
|
|
237
270
|
const limits = this.limits();
|
|
238
271
|
const outcome = this.store.update(sessionId, (goal) => {
|
|
272
|
+
// The ledger is read inside this transaction, so the verdict and the write see one state.
|
|
273
|
+
// Only a completion, or a declaration that links evidence, needs it (D7: no extra reads).
|
|
274
|
+
const needsLedger = report.status === "complete" || report.evidence.some((e) => e.evidenceId !== undefined);
|
|
275
|
+
const ledger = needsLedger ? this.options.proof?.ledger(sessionId) : undefined;
|
|
276
|
+
const linked = linkEvidence(report.evidence, ledger);
|
|
239
277
|
if (report.status === "complete") {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
278
|
+
const proof = ledger
|
|
279
|
+
? { criteria: criteriaForGoal(ledger, goal.goalId), linkedEvidence: linked.linked }
|
|
280
|
+
: undefined;
|
|
281
|
+
const result = modelComplete(goal, this.clock(), { summary: report.summary, evidence: linked.evidence }, proof);
|
|
282
|
+
if (result.ok)
|
|
283
|
+
return { goal: result.goal, value: { ok: true, stopped: true, needed: undefined } };
|
|
284
|
+
// A refusal over the ledger counts per run; the third run that tries pauses the goal.
|
|
285
|
+
if (result.unmet?.length && runId && this.refusedRuns(goal.goalId, runId)) {
|
|
286
|
+
return {
|
|
287
|
+
goal: pauseForRefusedCompletions(goal, this.clock()),
|
|
288
|
+
value: {
|
|
289
|
+
ok: false,
|
|
290
|
+
message: `${result.message} The goal is paused: completion was refused in ${COMPLETION_REFUSAL_LIMIT} different runs. The user decides: resume, or complete it with an override.`,
|
|
291
|
+
unmet: result.unmet,
|
|
292
|
+
paused: true,
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
return { value: { ok: false, message: result.message, unmet: result.unmet } };
|
|
244
297
|
}
|
|
245
|
-
const result = modelBlocked(goal, this.clock(), { summary: report.summary, evidence:
|
|
298
|
+
const result = modelBlocked(goal, this.clock(), { summary: report.summary, evidence: linked.evidence, runId }, limits);
|
|
246
299
|
return result.ok
|
|
247
300
|
? {
|
|
248
301
|
goal: result.goal.goal,
|
|
@@ -253,8 +306,15 @@ export class GoalService {
|
|
|
253
306
|
if (!outcome.ok)
|
|
254
307
|
return { ok: false, code: outcome.code, message: outcome.message };
|
|
255
308
|
const value = outcome.value;
|
|
309
|
+
if (!value.ok && value.paused)
|
|
310
|
+
this.emit(sessionId, outcome.goal);
|
|
256
311
|
if (!value.ok)
|
|
257
|
-
return {
|
|
312
|
+
return {
|
|
313
|
+
ok: false,
|
|
314
|
+
code: "not_allowed",
|
|
315
|
+
message: value.message,
|
|
316
|
+
...(value.unmet ? { unmet: value.unmet } : {}),
|
|
317
|
+
};
|
|
258
318
|
this.emit(sessionId, outcome.goal);
|
|
259
319
|
return {
|
|
260
320
|
ok: true,
|
|
@@ -263,6 +323,33 @@ export class GoalService {
|
|
|
263
323
|
...(value.needed ? { needed: value.needed } : {}),
|
|
264
324
|
};
|
|
265
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* The USER completes the goal over unmet required criteria (O6). A separate method on purpose:
|
|
328
|
+
* `report` is the model's only door and has no way to carry an override, so the model cannot
|
|
329
|
+
* reach this path whatever it sends. Hosts call it from a user action only (Phase 5 surfaces).
|
|
330
|
+
*/
|
|
331
|
+
overrideComplete(sessionId, input, expect) {
|
|
332
|
+
const reason = input.reason.trim();
|
|
333
|
+
if (!reason)
|
|
334
|
+
return { ok: false, code: "invalid", message: "An override needs a reason." };
|
|
335
|
+
const outcome = this.store.update(sessionId, (goal) => {
|
|
336
|
+
const ledger = this.options.proof?.ledger(sessionId);
|
|
337
|
+
const result = userOverrideComplete(goal, this.clock(), {
|
|
338
|
+
reason,
|
|
339
|
+
proof: {
|
|
340
|
+
criteria: ledger ? criteriaForGoal(ledger, goal.goalId) : [],
|
|
341
|
+
linkedEvidence: goal.proof?.linkedEvidence ?? [],
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
return result.ok ? { goal: result.goal, value: result } : { value: result };
|
|
345
|
+
}, expect);
|
|
346
|
+
if (!outcome.ok)
|
|
347
|
+
return { ok: false, code: outcome.code, message: outcome.message };
|
|
348
|
+
const result = outcome.value;
|
|
349
|
+
if (!result.ok)
|
|
350
|
+
return { ok: false, code: "not_allowed", message: result.message };
|
|
351
|
+
return this.done(sessionId, { ok: true, goal: result.goal });
|
|
352
|
+
}
|
|
266
353
|
/** Startup: goals whose driver process is gone pause with reason `restart`. */
|
|
267
354
|
recover() {
|
|
268
355
|
const paused = this.store.pauseOrphans(this.clock());
|
package/dist/goal/store.js
CHANGED
|
@@ -13,6 +13,24 @@ function parseEvidence(raw) {
|
|
|
13
13
|
return undefined;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
function parseProof(raw) {
|
|
17
|
+
if (typeof raw !== "string" || !raw)
|
|
18
|
+
return undefined;
|
|
19
|
+
try {
|
|
20
|
+
const value = JSON.parse(raw);
|
|
21
|
+
return value && Array.isArray(value.criteria) && Array.isArray(value.unmet)
|
|
22
|
+
? {
|
|
23
|
+
criteria: value.criteria,
|
|
24
|
+
unmet: value.unmet,
|
|
25
|
+
linkedEvidence: Array.isArray(value.linkedEvidence) ? value.linkedEvidence : [],
|
|
26
|
+
...(value.override ? { override: value.override } : {}),
|
|
27
|
+
}
|
|
28
|
+
: undefined;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
16
34
|
export function goalFromRow(row) {
|
|
17
35
|
const status = String(row.status);
|
|
18
36
|
return {
|
|
@@ -26,6 +44,7 @@ export function goalFromRow(row) {
|
|
|
26
44
|
...(parseEvidence(row.evidence)
|
|
27
45
|
? { evidence: parseEvidence(row.evidence) }
|
|
28
46
|
: {}),
|
|
47
|
+
...(parseProof(row.proof_json) ? { proof: parseProof(row.proof_json) } : {}),
|
|
29
48
|
epoch: Number(row.epoch ?? 1),
|
|
30
49
|
...(num(row.token_budget) !== undefined
|
|
31
50
|
? { tokenBudget: num(row.token_budget) }
|
|
@@ -55,7 +74,7 @@ export function goalFromRow(row) {
|
|
|
55
74
|
const COLUMNS = `session, goal_id, objective, status, reason, detail, summary, evidence, epoch,
|
|
56
75
|
token_budget, tokens_used, turns_used, max_turns, max_wall_ms, active_ms, no_tool_streak,
|
|
57
76
|
repeat_streak, last_reply_hash, blocked_streak, blocked_run, continuations, inflight,
|
|
58
|
-
last_run_id, kickoff_sent, owner_pid, created_at, updated_at, completed_at`;
|
|
77
|
+
last_run_id, kickoff_sent, owner_pid, created_at, updated_at, completed_at, proof_json`;
|
|
59
78
|
export class GoalStore {
|
|
60
79
|
db;
|
|
61
80
|
constructor(db) {
|
|
@@ -89,8 +108,8 @@ export class GoalStore {
|
|
|
89
108
|
continuations=excluded.continuations, inflight=excluded.inflight,
|
|
90
109
|
last_run_id=excluded.last_run_id, kickoff_sent=excluded.kickoff_sent,
|
|
91
110
|
owner_pid=excluded.owner_pid, updated_at=excluded.updated_at,
|
|
92
|
-
completed_at=excluded.completed_at`)
|
|
93
|
-
.run(goal.sessionId, goal.goalId, goal.objective, goal.status, goal.reason ?? null, goal.detail ?? null, goal.summary ?? null, goal.evidence?.length ? JSON.stringify(goal.evidence) : null, goal.epoch, goal.tokenBudget ?? null, goal.tokensUsed, goal.turnsUsed, goal.maxTurns, goal.maxWallMs, goal.activeMs, goal.noToolStreak, goal.repeatStreak, goal.lastReplyHash ?? null, goal.blockedStreak, goal.blockedRun ?? null, goal.continuations, goal.inflight ?? null, goal.lastRunId ?? null, goal.kickoffSent ? 1 : 0, goal.ownerPid ?? null, goal.createdAt, goal.updatedAt, goal.completedAt ?? null);
|
|
111
|
+
completed_at=excluded.completed_at, proof_json=excluded.proof_json`)
|
|
112
|
+
.run(goal.sessionId, goal.goalId, goal.objective, goal.status, goal.reason ?? null, goal.detail ?? null, goal.summary ?? null, goal.evidence?.length ? JSON.stringify(goal.evidence) : null, goal.epoch, goal.tokenBudget ?? null, goal.tokensUsed, goal.turnsUsed, goal.maxTurns, goal.maxWallMs, goal.activeMs, goal.noToolStreak, goal.repeatStreak, goal.lastReplyHash ?? null, goal.blockedStreak, goal.blockedRun ?? null, goal.continuations, goal.inflight ?? null, goal.lastRunId ?? null, goal.kickoffSent ? 1 : 0, goal.ownerPid ?? null, goal.createdAt, goal.updatedAt, goal.completedAt ?? null, goal.proof ? JSON.stringify(goal.proof) : null);
|
|
94
113
|
}
|
|
95
114
|
/**
|
|
96
115
|
* Replaces (or creates) the goal of a session. `build` gets the previous goal, if any, so the
|
package/dist/index.d.ts
CHANGED
|
@@ -70,9 +70,10 @@ export { McpConnector, type McpServerInfo, type McpStatus, type McpToolInfo, } f
|
|
|
70
70
|
export { describePermissionStatus, type EffectState, effectState, FULL_ACCESS_WARNING, type ModeFlags, type ModePolicy, modeFromFlags, modeToPreset, PERMISSION_MODE_TABLE, PERMISSION_MODES, PERMISSION_MODES_LOCKED, PERMISSION_USAGE, type PermissionCommand, type PermissionModeLabel, type PermissionModeSpec, parsePermissionCommand, parsePermissionMode, presetToMode, READ_ONLY_POLICY, } from "./permissions/modes.ts";
|
|
71
71
|
export { DIAGRAM_MAX_BYTES, DIAGRAM_MAX_NODES, DIAGRAM_SYNTAXES, DIAGRAMS_MAX_ALLOWED, mermaidProblem, type PlanDiagram, validateDiagrams, } from "./plan/diagrams.ts";
|
|
72
72
|
export { buildPlanManifest, parsePlanHeadings, slugifyHeading, } from "./plan/manifest.ts";
|
|
73
|
-
export { claimApprovedPlan, currentPlan, discardApprovedPlan, IMPLEMENTATION_AGENT_ID, implementationPrompt, PLAN_CONTEXT_MAX_CHARS, PLAN_OPTIONS, PLAN_QUESTION_ID, PLAN_REVIEW_TITLE, PLAN_TEXT_KEY, type PlanFollowUp, type PlanState, type PlanStatus, planHash, readPlanState, settlePlanRun, withdrawPendingPlan, } from "./plan/state.ts";
|
|
73
|
+
export { claimApprovedPlan, currentPlan, discardApprovedPlan, IMPLEMENTATION_AGENT_ID, implementationPrompt, PLAN_CONTEXT_MAX_CHARS, PLAN_CRITERIA_KEY, PLAN_OPTIONS, PLAN_QUESTION_ID, PLAN_REVIEW_TITLE, PLAN_TEXT_KEY, type PlanFollowUp, type PlanState, type PlanStatus, planHash, readPlanState, settlePlanRun, withdrawPendingPlan, } from "./plan/state.ts";
|
|
74
74
|
export { discoverPlugins, type PluginEnvironment, PluginHost, type PluginHostOptions, pluginPrefix, type ViewInfo, ViewRunError, } from "./plugins/host.ts";
|
|
75
75
|
export { type CliInstallOptions, cliInstall, cliUpdateAll, INSTALL_TIMEOUT_MS, type InstallPluginInput, type InstallPluginResult, type InstallRunner, installedGlobalPluginPackages, installedNpmPlugins, installPlugin, isPeerConflict, npmInstallTarget, type PluginSpec, parsePluginSpec, pluginTrustRemark, printInstallResult, registerPluginInstallTool, sanitizeNpmError, updateAllPlugins, } from "./plugins/install.ts";
|
|
76
|
+
export * from "./proof/index.ts";
|
|
76
77
|
export { ActiveProvider, ProviderRegistry, UnconfiguredProvider } from "./providers/registry.ts";
|
|
77
78
|
export { availableProviderModels, type ProviderModelCatalog, resolveProviderModel, } from "./providers/routing.ts";
|
|
78
79
|
export { maskSecret, type ProviderProfile, type ProviderSettings, ProviderSettingsStore, } from "./providers/settings.ts";
|
package/dist/index.js
CHANGED
|
@@ -69,9 +69,10 @@ export { McpConnector, } from "./mcp/connector.js";
|
|
|
69
69
|
export { describePermissionStatus, effectState, FULL_ACCESS_WARNING, modeFromFlags, modeToPreset, PERMISSION_MODE_TABLE, PERMISSION_MODES, PERMISSION_MODES_LOCKED, PERMISSION_USAGE, parsePermissionCommand, parsePermissionMode, presetToMode, READ_ONLY_POLICY, } from "./permissions/modes.js";
|
|
70
70
|
export { DIAGRAM_MAX_BYTES, DIAGRAM_MAX_NODES, DIAGRAM_SYNTAXES, DIAGRAMS_MAX_ALLOWED, mermaidProblem, validateDiagrams, } from "./plan/diagrams.js";
|
|
71
71
|
export { buildPlanManifest, parsePlanHeadings, slugifyHeading, } from "./plan/manifest.js";
|
|
72
|
-
export { claimApprovedPlan, currentPlan, discardApprovedPlan, IMPLEMENTATION_AGENT_ID, implementationPrompt, PLAN_CONTEXT_MAX_CHARS, PLAN_OPTIONS, PLAN_QUESTION_ID, PLAN_REVIEW_TITLE, PLAN_TEXT_KEY, planHash, readPlanState, settlePlanRun, withdrawPendingPlan, } from "./plan/state.js";
|
|
72
|
+
export { claimApprovedPlan, currentPlan, discardApprovedPlan, IMPLEMENTATION_AGENT_ID, implementationPrompt, PLAN_CONTEXT_MAX_CHARS, PLAN_CRITERIA_KEY, PLAN_OPTIONS, PLAN_QUESTION_ID, PLAN_REVIEW_TITLE, PLAN_TEXT_KEY, planHash, readPlanState, settlePlanRun, withdrawPendingPlan, } from "./plan/state.js";
|
|
73
73
|
export { discoverPlugins, PluginHost, pluginPrefix, ViewRunError, } from "./plugins/host.js";
|
|
74
74
|
export { cliInstall, cliUpdateAll, INSTALL_TIMEOUT_MS, installedGlobalPluginPackages, installedNpmPlugins, installPlugin, isPeerConflict, npmInstallTarget, parsePluginSpec, pluginTrustRemark, printInstallResult, registerPluginInstallTool, sanitizeNpmError, updateAllPlugins, } from "./plugins/install.js";
|
|
75
|
+
export * from "./proof/index.js";
|
|
75
76
|
export { ActiveProvider, ProviderRegistry, UnconfiguredProvider } from "./providers/registry.js";
|
|
76
77
|
export { availableProviderModels, resolveProviderModel, } from "./providers/routing.js";
|
|
77
78
|
export { maskSecret, ProviderSettingsStore, } from "./providers/settings.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation of the acceptance criteria a model proposes in `exit_plan` (specs/011 3.6, O3).
|
|
3
|
+
* Pure: no I/O. The result is a DRAFT: nothing here creates a criterion; the plan decision of
|
|
4
|
+
* the user does (`exit-plan.ts`). The model may only propose verifier kinds Alisio can run today
|
|
5
|
+
* (`command`, `file`, `data-check`, `coverage`, `plugin`, `manual`) and plain workspace-relative scopes.
|
|
6
|
+
*/
|
|
7
|
+
import type { PlanCriterionDraft } from "@alisio/sdk";
|
|
8
|
+
export declare const CRITERIA_MAX = 20;
|
|
9
|
+
export declare const CRITERION_TEXT_MAX = 500;
|
|
10
|
+
export interface DroppedCriterion {
|
|
11
|
+
/** Zero-based position in the proposed list. */
|
|
12
|
+
index: number;
|
|
13
|
+
reason: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ValidatedCriteria {
|
|
16
|
+
accepted: PlanCriterionDraft[];
|
|
17
|
+
dropped: DroppedCriterion[];
|
|
18
|
+
}
|
|
19
|
+
/** Turns the model's `acceptanceCriteria` input into drafts, reporting what it could not keep. */
|
|
20
|
+
export declare function validateCriteriaDrafts(raw: unknown): ValidatedCriteria;
|
|
21
|
+
/**
|
|
22
|
+
* Applies the user's per-draft decisions (`plan:criteria`) to the drafts of the plan they approved.
|
|
23
|
+
* `undefined` selection keeps every draft (the plain "approve"). Anything malformed, any index
|
|
24
|
+
* outside the list or an empty edit returns `undefined` for "nothing is activated": a selection
|
|
25
|
+
* the user did not make cleanly never activates a criterion (fail closed).
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyCriteriaSelection(drafts: readonly PlanCriterionDraft[], raw: unknown): {
|
|
28
|
+
kept: PlanCriterionDraft[];
|
|
29
|
+
rejected: number;
|
|
30
|
+
} | undefined;
|