@vincemakes/kiso-runtime 0.43.0 → 0.44.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/index.d.ts +0 -2
- package/dist/index.js +0 -3
- package/dist/run.js +7 -4
- package/dist/session.d.ts +0 -15
- package/dist/session.js +0 -27
- package/dist/summarize.d.ts +3 -2
- package/dist/summarize.js +3 -2
- package/package.json +6 -6
- package/dist/task-assessment.d.ts +0 -93
- package/dist/task-assessment.js +0 -165
package/dist/index.d.ts
CHANGED
|
@@ -31,8 +31,6 @@ export { disposeExtensions, loadExtensions, loadProjectExtensions } from "./exte
|
|
|
31
31
|
export type { KisoExtension } from "./extensions.js";
|
|
32
32
|
export { executionForCallId, executionLedger } from "./ledger.js";
|
|
33
33
|
export type { ExecutionRecord, ExecutionStatus } from "./ledger.js";
|
|
34
|
-
export { assessTasks } from "./task-assessment.js";
|
|
35
|
-
export type { EvidenceVerdict, TaskAssessment, TaskClaim } from "./task-assessment.js";
|
|
36
34
|
export { kisoHome, projectArtifacts, recordTrust, trustFor } from "./trust.js";
|
|
37
35
|
export type { ProjectArtifact, ProjectArtifacts, TrustDecision, TrustRecord } from "./trust.js";
|
|
38
36
|
export { canonicalizeUsage } from "./usage/canonical.js";
|
package/dist/index.js
CHANGED
|
@@ -31,9 +31,6 @@ export { SessionStore, StaleWriterError, StoreCorruptionError } from "./store.js
|
|
|
31
31
|
export { disposeExtensions, loadExtensions, loadProjectExtensions } from "./extensions.js";
|
|
32
32
|
// ledger
|
|
33
33
|
export { executionForCallId, executionLedger } from "./ledger.js";
|
|
34
|
-
// task assessment (TV-1A) — the pure projection separating the model's
|
|
35
|
-
// CLAIM from VERIFIED under Verified ⟹ evidenceSeq > lastMutationSeq
|
|
36
|
-
export { assessTasks } from "./task-assessment.js";
|
|
37
34
|
// trust
|
|
38
35
|
export { kisoHome, projectArtifacts, recordTrust, trustFor } from "./trust.js";
|
|
39
36
|
// usage — the canonical accounting schema (E2/1.3.0, R4b-1 ruling:
|
package/dist/run.js
CHANGED
|
@@ -37,8 +37,8 @@ export class Run {
|
|
|
37
37
|
#decisionIds = [];
|
|
38
38
|
#started = false;
|
|
39
39
|
constructor(store, adapter, config, session, input, externalSignal, resume,
|
|
40
|
-
// TV-1B: durable PROVENANCE for the input (e.g.
|
|
41
|
-
//
|
|
40
|
+
// TV-1B: durable PROVENANCE for the input (e.g. source:"system"
|
|
41
|
+
// for a line the product wrote) — who produced the line, never a
|
|
42
42
|
// provider-role escalation. Absent = plain user input.
|
|
43
43
|
source,
|
|
44
44
|
// 0.40.0: how a person composed the turn (a skill + the typed line) —
|
|
@@ -505,7 +505,10 @@ export class Run {
|
|
|
505
505
|
yield log.append({ type: "tool_result", callId: call.callId, invocationSeq: call.seq, content: refusal, isError: true, errorKind: "invalid_input" });
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
|
-
|
|
508
|
+
// #20: the same lexical evidence the fresh path hands onPreTool — THIS
|
|
509
|
+
// invocation's deltas, anchored by its seq; never given to the chain.
|
|
510
|
+
const rawInput = rawInputOf(log.all, call.callId, call.seq);
|
|
511
|
+
const payload = { callId: call.callId, name: call.name, input: call.input ?? {}, ...(rawInput !== undefined ? { rawInput } : {}) };
|
|
509
512
|
const policyCall = { name: payload.name, input: payload.input };
|
|
510
513
|
let verdict;
|
|
511
514
|
try {
|
|
@@ -825,7 +828,7 @@ export class Run {
|
|
|
825
828
|
};
|
|
826
829
|
}
|
|
827
830
|
if (this.#config.hooks?.onPostTool) {
|
|
828
|
-
result = await this.#config.hooks.onPostTool({ callId, name, input }, result, { sessionId: this.#session.id });
|
|
831
|
+
result = await this.#config.hooks.onPostTool({ callId, name, input, ...(rawInput !== undefined ? { rawInput } : {}) }, result, { sessionId: this.#session.id });
|
|
829
832
|
}
|
|
830
833
|
}
|
|
831
834
|
// ruling #12 correction one: the honest note rides the recovered failure too —
|
package/dist/session.d.ts
CHANGED
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { EventLog, type AbortSignalLike, type Adapter, type Event, type KisoExtension, type Message, type PermissionDecision, type Tool, type ToolSpec, type EventInput } from "@vincemakes/kiso-core";
|
|
31
31
|
import { type OverflowMeasure } from "./overflow-belt.js";
|
|
32
|
-
import { type TaskAssessment } from "./task-assessment.js";
|
|
33
32
|
import { type ContinuationScope } from "@vincemakes/kiso-core";
|
|
34
33
|
import { type ReasoningSetting } from "./provider/metadata.js";
|
|
35
34
|
import { type SessionStore, type StoreRecord } from "./store.js";
|
|
@@ -375,20 +374,6 @@ export declare class AgentSession {
|
|
|
375
374
|
approve(decisionId: string, allow: boolean, reason?: string): Promise<void>;
|
|
376
375
|
/** Executions that started but never reported a result (crash window). */
|
|
377
376
|
uncertainExecutions(): import("./ledger.js").ExecutionRecord[];
|
|
378
|
-
/**
|
|
379
|
-
* TV-1A — assess the task claims and their evidence freshness over THIS
|
|
380
|
-
* session's durable log. The non-mutating set comes from the live tools'
|
|
381
|
-
* own `effects.precommitSafe` certificates (one direction of truth,
|
|
382
|
-
* never a second declaration) — the read-only+free+local contract, the
|
|
383
|
-
* only certificate that proves the world untouched. `concurrency:
|
|
384
|
-
* "shared"` is a SCHEDULING promise and never feeds this set (TV-1C —
|
|
385
|
-
* slow_touch is shared and writes). The evidence policy defaults to
|
|
386
|
-
* {"shell"} — the convention the task extension's own "make the LAST
|
|
387
|
-
* item a verification step" guidance produces.
|
|
388
|
-
*/
|
|
389
|
-
assessTasks(opts?: {
|
|
390
|
-
readonly evidenceTools?: ReadonlySet<string>;
|
|
391
|
-
}): TaskAssessment;
|
|
392
377
|
/**
|
|
393
378
|
* The human's verdict on an interrupted execution, keyed by EXECUTION ID
|
|
394
379
|
* (B group): "rerun" (the human says the side effect did NOT happen — the
|
package/dist/session.js
CHANGED
|
@@ -31,11 +31,6 @@ import { EventLog, projectMessages, SUMMARY_FRAMING, } from "@vincemakes/kiso-co
|
|
|
31
31
|
import { executionLedger } from "./ledger.js";
|
|
32
32
|
import { overflowBelt } from "./overflow-belt.js";
|
|
33
33
|
import { windowLearner } from "./window-learner.js";
|
|
34
|
-
import { assessTasks } from "./task-assessment.js";
|
|
35
|
-
/** TV-1A — the session-level evidence policy: the PURE projection defaults
|
|
36
|
-
* to ∅ (never inventing evidence); the session names the one built-in
|
|
37
|
-
* verification surface. Override per call for custom evidence tools. */
|
|
38
|
-
const DEFAULT_EVIDENCE_TOOLS = new Set(["shell"]);
|
|
39
34
|
import { denialResult } from "@vincemakes/kiso-core";
|
|
40
35
|
import { buildProfile, readProfile, writeProfile, writeSummary } from "./profile.js";
|
|
41
36
|
import { summarizeEvents } from "./session-summary.js";
|
|
@@ -1064,28 +1059,6 @@ export class AgentSession {
|
|
|
1064
1059
|
uncertainExecutions() {
|
|
1065
1060
|
return [...executionLedger(this.log.all).values()].filter((r) => r.status === "uncertain");
|
|
1066
1061
|
}
|
|
1067
|
-
/**
|
|
1068
|
-
* TV-1A — assess the task claims and their evidence freshness over THIS
|
|
1069
|
-
* session's durable log. The non-mutating set comes from the live tools'
|
|
1070
|
-
* own `effects.precommitSafe` certificates (one direction of truth,
|
|
1071
|
-
* never a second declaration) — the read-only+free+local contract, the
|
|
1072
|
-
* only certificate that proves the world untouched. `concurrency:
|
|
1073
|
-
* "shared"` is a SCHEDULING promise and never feeds this set (TV-1C —
|
|
1074
|
-
* slow_touch is shared and writes). The evidence policy defaults to
|
|
1075
|
-
* {"shell"} — the convention the task extension's own "make the LAST
|
|
1076
|
-
* item a verification step" guidance produces.
|
|
1077
|
-
*/
|
|
1078
|
-
assessTasks(opts) {
|
|
1079
|
-
const nonMutatingTools = new Set();
|
|
1080
|
-
for (const tool of this.#config.registry.list()) {
|
|
1081
|
-
if (tool.effects?.precommitSafe === true)
|
|
1082
|
-
nonMutatingTools.add(tool.name);
|
|
1083
|
-
}
|
|
1084
|
-
return assessTasks(this.log.all, {
|
|
1085
|
-
nonMutatingTools,
|
|
1086
|
-
evidenceTools: opts?.evidenceTools ?? DEFAULT_EVIDENCE_TOOLS,
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1089
1062
|
/**
|
|
1090
1063
|
* The human's verdict on an interrupted execution, keyed by EXECUTION ID
|
|
1091
1064
|
* (B group): "rerun" (the human says the side effect did NOT happen — the
|
package/dist/summarize.d.ts
CHANGED
|
@@ -270,8 +270,9 @@ export declare function estimateEventTokens(ev: Event): number;
|
|
|
270
270
|
* rounds exist (nothing worth covering yet).
|
|
271
271
|
*
|
|
272
272
|
* ⑥ (task round): a tool result tagged do-not-compact is DURABLE work
|
|
273
|
-
* memory (
|
|
274
|
-
*
|
|
273
|
+
* memory (a tool declares it so; the retired task extension's list was
|
|
274
|
+
* the first) — the summary must never cover its round, or the model
|
|
275
|
+
* loses it. When the base boundary would
|
|
275
276
|
* cover such a result, the boundary pulls back to just before the round
|
|
276
277
|
* containing the LATEST one (still a turn boundary). A protected round
|
|
277
278
|
* as the FIRST uncovered round leaves nothing before it to cover →
|
package/dist/summarize.js
CHANGED
|
@@ -501,8 +501,9 @@ export function estimateEventTokens(ev) {
|
|
|
501
501
|
* rounds exist (nothing worth covering yet).
|
|
502
502
|
*
|
|
503
503
|
* ⑥ (task round): a tool result tagged do-not-compact is DURABLE work
|
|
504
|
-
* memory (
|
|
505
|
-
*
|
|
504
|
+
* memory (a tool declares it so; the retired task extension's list was
|
|
505
|
+
* the first) — the summary must never cover its round, or the model
|
|
506
|
+
* loses it. When the base boundary would
|
|
506
507
|
* cover such a result, the boundary pulls back to just before the round
|
|
507
508
|
* containing the LATEST one (still a turn boundary). A protected round
|
|
508
509
|
* as the FIRST uncovered round leaves nothing before it to cover →
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "kiso runtime \u2014 durable multi-turn agent sessions: AgentDefinition, AgentRuntime, AgentSession, Run, append-only JSONL store.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"test": "vitest run"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@vincemakes/kiso-core": "0.
|
|
28
|
+
"@vincemakes/kiso-core": "0.44.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
32
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
33
|
-
"@vincemakes/kiso-provider-openai-responses": "0.
|
|
31
|
+
"@vincemakes/kiso-provider-anthropic": "0.44.0",
|
|
32
|
+
"@vincemakes/kiso-provider-openai": "0.44.0",
|
|
33
|
+
"@vincemakes/kiso-provider-openai-responses": "0.44.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@vincemakes/kiso-provider-anthropic": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@vincemakes/kiso-evals": "0.
|
|
47
|
+
"@vincemakes/kiso-evals": "0.44.0",
|
|
48
48
|
"@types/node": "^26.1.2",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^3.0.0"
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TV-1A — TaskAssessment as a PURE PROJECTION + Evidence Freshness.
|
|
3
|
-
*
|
|
4
|
-
* The task checklist the model maintains through task_set is a SELF-REPORT:
|
|
5
|
-
* an item is "done" because the model said so. This projection mechanically
|
|
6
|
-
* separates that CLAIM from VERIFIED, under one frozen sentence:
|
|
7
|
-
*
|
|
8
|
-
* Verified ⟹ evidenceSeq > lastRelevantMutationSeq.
|
|
9
|
-
*
|
|
10
|
-
* No new event kind, no core diff — the projection reads the existing
|
|
11
|
-
* durable vocabulary (`deriveRecoveryPlan` is the shape precedent). The
|
|
12
|
-
* classifiers are maximally conservative; the exemptions are exactly
|
|
13
|
-
* three (TV-1C — no wider taxonomy exists or is wanted):
|
|
14
|
-
*
|
|
15
|
-
* - MUTATION marker: `tool_execution_started` (intent-to-effect) of any
|
|
16
|
-
* name NOT in `nonMutatingTools`. Started — not the receipt — so failed
|
|
17
|
-
* and crash-window executions invalidate too, and pre-EC-1 overlap eras
|
|
18
|
-
* are covered by the same rule. Absence of a certificate is a mutation.
|
|
19
|
-
* The exemption set is fed by `effects.precommitSafe` — the frozen
|
|
20
|
-
* read-only+free+local contract, the only certificate that PROVES the
|
|
21
|
-
* world untouched. `concurrency: "shared"` proves overlap-safety and
|
|
22
|
-
* nothing else (the TV-1C finding: slow_touch is shared AND writes) —
|
|
23
|
-
* it never feeds this set.
|
|
24
|
-
* ONE proven exception (TV-1B, grounded in WR-1A): an execution whose
|
|
25
|
-
* terminal receipt is failed(errorKind:"precondition") never counts —
|
|
26
|
-
* that kind's frozen contract is "work refused BEFORE it starts".
|
|
27
|
-
* - EVIDENCE: only receipts (`tool_execution_succeeded`) of names in
|
|
28
|
-
* `evidenceTools`. Default ∅ — the projection never invents evidence.
|
|
29
|
-
* What TV-1A's verdict asserts is POSITIONAL: the arc's last
|
|
30
|
-
* intent-to-effect was a successful evidence-class run — an "evidence",
|
|
31
|
-
* never a "proof" (semantic knowledge is the TV-1B driver's).
|
|
32
|
-
* - task_set itself never invalidates: recording the claim after the check
|
|
33
|
-
* is the natural arc (tests → mark done), and the claim-recording act
|
|
34
|
-
* mutates nothing the evidence observed.
|
|
35
|
-
* - VOIDED RANGES (TV-1C): a `model_output_abandoned` marker voids
|
|
36
|
-
* (voidFromSeq, seq] — the durable vocabulary's own "never happened",
|
|
37
|
-
* the same ranges the kernel projection skips. Every consumer here
|
|
38
|
-
* skips them uniformly: a voided echo is not a claim, a voided receipt
|
|
39
|
-
* is not evidence, a voided start is not a mutation. task_set waits
|
|
40
|
-
* for turn commit today (TT-1), so the scheduler already keeps claims
|
|
41
|
-
* out of voided turns — the projection encodes the admissibility rule
|
|
42
|
-
* itself instead of relying on that forever.
|
|
43
|
-
*
|
|
44
|
-
* `verified` means FRESH EVIDENCE UNDER THE CONFIGURED POLICY — never
|
|
45
|
-
* "objectively proven correct". Goal Truth is external (PE-1's
|
|
46
|
-
* evaluator); this projection only ever ranks the model's own claim
|
|
47
|
-
* against the trajectory's own receipts.
|
|
48
|
-
*/
|
|
49
|
-
import type { Event } from "@vincemakes/kiso-core";
|
|
50
|
-
/** One item of the model's plan, exactly as last claimed. */
|
|
51
|
-
export interface TaskClaim {
|
|
52
|
-
readonly text: string;
|
|
53
|
-
readonly status: "pending" | "active" | "done";
|
|
54
|
-
}
|
|
55
|
-
/** The freshness verdict. `stale` and `unreadable` NAME their cause. */
|
|
56
|
-
export type EvidenceVerdict = {
|
|
57
|
-
readonly kind: "verified";
|
|
58
|
-
readonly evidenceSeq: number;
|
|
59
|
-
} | {
|
|
60
|
-
readonly kind: "stale";
|
|
61
|
-
readonly evidenceSeq: number;
|
|
62
|
-
readonly invalidatedBySeq: number;
|
|
63
|
-
} | {
|
|
64
|
-
readonly kind: "none";
|
|
65
|
-
} | {
|
|
66
|
-
readonly kind: "unreadable";
|
|
67
|
-
readonly atSeq: number;
|
|
68
|
-
readonly reason: string;
|
|
69
|
-
};
|
|
70
|
-
export interface TaskAssessment {
|
|
71
|
-
/** The LAST successful task_set echo, parsed — the model's claims. */
|
|
72
|
-
readonly claims: readonly TaskClaim[];
|
|
73
|
-
/** True only for a non-empty plan whose every item is claimed done. */
|
|
74
|
-
readonly allClaimedDone: boolean;
|
|
75
|
-
readonly lastTaskSetSeq: number | null;
|
|
76
|
-
/** Seq of the last mutation-class `tool_execution_started`, if any. */
|
|
77
|
-
readonly lastMutationSeq: number | null;
|
|
78
|
-
readonly evidence: EvidenceVerdict;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Assess the task claims and their evidence freshness over a durable
|
|
82
|
-
* trajectory. Pure: same events, same options, same assessment.
|
|
83
|
-
*/
|
|
84
|
-
export declare function assessTasks(events: readonly Event[], opts?: {
|
|
85
|
-
/** Names whose executions PROVABLY never mutate the observable
|
|
86
|
-
* world (from `effects.precommitSafe` certificates — read-only +
|
|
87
|
-
* free + local). `concurrency: "shared"` is NOT such a proof
|
|
88
|
-
* (TV-1C). Default ∅ — everything mutates. */
|
|
89
|
-
readonly nonMutatingTools?: ReadonlySet<string>;
|
|
90
|
-
/** Names whose successful receipts count as evidence. Default ∅ —
|
|
91
|
-
* nothing does. */
|
|
92
|
-
readonly evidenceTools?: ReadonlySet<string>;
|
|
93
|
-
}): TaskAssessment;
|
package/dist/task-assessment.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TV-1A — TaskAssessment as a PURE PROJECTION + Evidence Freshness.
|
|
3
|
-
*
|
|
4
|
-
* The task checklist the model maintains through task_set is a SELF-REPORT:
|
|
5
|
-
* an item is "done" because the model said so. This projection mechanically
|
|
6
|
-
* separates that CLAIM from VERIFIED, under one frozen sentence:
|
|
7
|
-
*
|
|
8
|
-
* Verified ⟹ evidenceSeq > lastRelevantMutationSeq.
|
|
9
|
-
*
|
|
10
|
-
* No new event kind, no core diff — the projection reads the existing
|
|
11
|
-
* durable vocabulary (`deriveRecoveryPlan` is the shape precedent). The
|
|
12
|
-
* classifiers are maximally conservative; the exemptions are exactly
|
|
13
|
-
* three (TV-1C — no wider taxonomy exists or is wanted):
|
|
14
|
-
*
|
|
15
|
-
* - MUTATION marker: `tool_execution_started` (intent-to-effect) of any
|
|
16
|
-
* name NOT in `nonMutatingTools`. Started — not the receipt — so failed
|
|
17
|
-
* and crash-window executions invalidate too, and pre-EC-1 overlap eras
|
|
18
|
-
* are covered by the same rule. Absence of a certificate is a mutation.
|
|
19
|
-
* The exemption set is fed by `effects.precommitSafe` — the frozen
|
|
20
|
-
* read-only+free+local contract, the only certificate that PROVES the
|
|
21
|
-
* world untouched. `concurrency: "shared"` proves overlap-safety and
|
|
22
|
-
* nothing else (the TV-1C finding: slow_touch is shared AND writes) —
|
|
23
|
-
* it never feeds this set.
|
|
24
|
-
* ONE proven exception (TV-1B, grounded in WR-1A): an execution whose
|
|
25
|
-
* terminal receipt is failed(errorKind:"precondition") never counts —
|
|
26
|
-
* that kind's frozen contract is "work refused BEFORE it starts".
|
|
27
|
-
* - EVIDENCE: only receipts (`tool_execution_succeeded`) of names in
|
|
28
|
-
* `evidenceTools`. Default ∅ — the projection never invents evidence.
|
|
29
|
-
* What TV-1A's verdict asserts is POSITIONAL: the arc's last
|
|
30
|
-
* intent-to-effect was a successful evidence-class run — an "evidence",
|
|
31
|
-
* never a "proof" (semantic knowledge is the TV-1B driver's).
|
|
32
|
-
* - task_set itself never invalidates: recording the claim after the check
|
|
33
|
-
* is the natural arc (tests → mark done), and the claim-recording act
|
|
34
|
-
* mutates nothing the evidence observed.
|
|
35
|
-
* - VOIDED RANGES (TV-1C): a `model_output_abandoned` marker voids
|
|
36
|
-
* (voidFromSeq, seq] — the durable vocabulary's own "never happened",
|
|
37
|
-
* the same ranges the kernel projection skips. Every consumer here
|
|
38
|
-
* skips them uniformly: a voided echo is not a claim, a voided receipt
|
|
39
|
-
* is not evidence, a voided start is not a mutation. task_set waits
|
|
40
|
-
* for turn commit today (TT-1), so the scheduler already keeps claims
|
|
41
|
-
* out of voided turns — the projection encodes the admissibility rule
|
|
42
|
-
* itself instead of relying on that forever.
|
|
43
|
-
*
|
|
44
|
-
* `verified` means FRESH EVIDENCE UNDER THE CONFIGURED POLICY — never
|
|
45
|
-
* "objectively proven correct". Goal Truth is external (PE-1's
|
|
46
|
-
* evaluator); this projection only ever ranks the model's own claim
|
|
47
|
-
* against the trajectory's own receipts.
|
|
48
|
-
*/
|
|
49
|
-
const TASK_TOOL = "task_set";
|
|
50
|
-
const STATUSES = new Set(["pending", "active", "done"]);
|
|
51
|
-
const COUNT_LINE = /^\[task\] (\d+) items? — (\d+) pending, (\d+) active, (\d+) done$/;
|
|
52
|
-
const ITEM_LINE = /^\[(\w+)\] (.*)$/;
|
|
53
|
-
/** Parse the canonical task_set echo (the frozen content contract the
|
|
54
|
-
* checklist cell also reads). Returns the claims, or the reason it
|
|
55
|
-
* cannot — never a guess. */
|
|
56
|
-
function parseEcho(content) {
|
|
57
|
-
const lines = content.split("\n");
|
|
58
|
-
const head = COUNT_LINE.exec(lines[0] ?? "");
|
|
59
|
-
if (head === null)
|
|
60
|
-
return { reason: "line 1 is not the [task] count line" };
|
|
61
|
-
const total = Number(head[1]);
|
|
62
|
-
const declared = { pending: Number(head[2]), active: Number(head[3]), done: Number(head[4]) };
|
|
63
|
-
const claims = [];
|
|
64
|
-
for (let i = 1; i < lines.length; i += 1) {
|
|
65
|
-
const m = ITEM_LINE.exec(lines[i]);
|
|
66
|
-
if (m === null || !STATUSES.has(m[1]))
|
|
67
|
-
return { reason: `line ${i + 1} is not a [pending|active|done] item line` };
|
|
68
|
-
claims.push({ text: m[2], status: m[1] });
|
|
69
|
-
}
|
|
70
|
-
const counts = { pending: 0, active: 0, done: 0 };
|
|
71
|
-
for (const c of claims)
|
|
72
|
-
counts[c.status] += 1;
|
|
73
|
-
if (claims.length !== total || counts.pending !== declared.pending || counts.active !== declared.active || counts.done !== declared.done) {
|
|
74
|
-
return { reason: "the count line disagrees with the item lines" };
|
|
75
|
-
}
|
|
76
|
-
return { claims };
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Assess the task claims and their evidence freshness over a durable
|
|
80
|
-
* trajectory. Pure: same events, same options, same assessment.
|
|
81
|
-
*/
|
|
82
|
-
export function assessTasks(events, opts) {
|
|
83
|
-
const nonMutating = opts?.nonMutatingTools ?? new Set();
|
|
84
|
-
const evidenceNames = opts?.evidenceTools ?? new Set();
|
|
85
|
-
// TV-1C: PASS 0 — the voided ranges. (voidFromSeq, seq] of every
|
|
86
|
-
// `model_output_abandoned` marker is "never happened" for this whole
|
|
87
|
-
// projection, exactly as the kernel's own context projection treats
|
|
88
|
-
// it. Markers are rare; a linear range check is honest and enough.
|
|
89
|
-
const voidRanges = [];
|
|
90
|
-
for (const ev of events) {
|
|
91
|
-
if (ev.type === "model_output_abandoned")
|
|
92
|
-
voidRanges.push({ from: ev.voidFromSeq, to: ev.seq });
|
|
93
|
-
}
|
|
94
|
-
const voided = (seq) => voidRanges.some((r) => seq > r.from && seq <= r.to);
|
|
95
|
-
// TV-1B: PASS 1 — executions whose terminal receipt is a
|
|
96
|
-
// PRECONDITION failure are PROVEN no-mutation (WR-1A froze that
|
|
97
|
-
// contract: work refused before it starts). Everything else stays
|
|
98
|
-
// conservative: no receipt (crash window), fatal/transient/
|
|
99
|
-
// invalid_input, success, and legacy receipts with no errorKind.
|
|
100
|
-
// Two passes, no temporal rollback state — same events, same verdict.
|
|
101
|
-
const provenNoMutation = new Set();
|
|
102
|
-
for (const ev of events) {
|
|
103
|
-
if (voided(ev.seq))
|
|
104
|
-
continue;
|
|
105
|
-
if (ev.type === "tool_execution_failed" && ev.errorKind === "precondition") {
|
|
106
|
-
provenNoMutation.add(ev.executionId);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const nameByExecution = new Map();
|
|
110
|
-
let claims = [];
|
|
111
|
-
let lastTaskSetSeq = null;
|
|
112
|
-
let unreadable = null;
|
|
113
|
-
let lastMutationSeq = null;
|
|
114
|
-
let lastEvidenceSeq = null;
|
|
115
|
-
let staleBySeq = null;
|
|
116
|
-
for (const ev of events) {
|
|
117
|
-
if (voided(ev.seq))
|
|
118
|
-
continue;
|
|
119
|
-
if (ev.type === "tool_execution_started") {
|
|
120
|
-
nameByExecution.set(ev.executionId, ev.name);
|
|
121
|
-
if (ev.name !== TASK_TOOL && !nonMutating.has(ev.name) && !provenNoMutation.has(ev.executionId)) {
|
|
122
|
-
lastMutationSeq = ev.seq;
|
|
123
|
-
if (lastEvidenceSeq !== null && staleBySeq === null)
|
|
124
|
-
staleBySeq = ev.seq;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else if (ev.type === "tool_execution_succeeded") {
|
|
128
|
-
const name = nameByExecution.get(ev.executionId);
|
|
129
|
-
if (name === TASK_TOOL) {
|
|
130
|
-
const parsed = parseEcho(ev.result.content);
|
|
131
|
-
if ("reason" in parsed) {
|
|
132
|
-
unreadable = { atSeq: ev.seq, reason: parsed.reason };
|
|
133
|
-
claims = [];
|
|
134
|
-
lastTaskSetSeq = ev.seq;
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
unreadable = null;
|
|
138
|
-
claims = parsed.claims;
|
|
139
|
-
lastTaskSetSeq = ev.seq;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
else if (name !== undefined && evidenceNames.has(name)) {
|
|
143
|
-
// a fresh evidence receipt supersedes both the older evidence
|
|
144
|
-
// AND any staleness its own start incurred.
|
|
145
|
-
lastEvidenceSeq = ev.seq;
|
|
146
|
-
staleBySeq = null;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const allClaimedDone = claims.length > 0 && claims.every((c) => c.status === "done");
|
|
151
|
-
let evidence;
|
|
152
|
-
if (unreadable !== null) {
|
|
153
|
-
evidence = { kind: "unreadable", atSeq: unreadable.atSeq, reason: unreadable.reason };
|
|
154
|
-
}
|
|
155
|
-
else if (lastEvidenceSeq === null) {
|
|
156
|
-
evidence = { kind: "none" };
|
|
157
|
-
}
|
|
158
|
-
else if (staleBySeq !== null) {
|
|
159
|
-
evidence = { kind: "stale", evidenceSeq: lastEvidenceSeq, invalidatedBySeq: staleBySeq };
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
evidence = { kind: "verified", evidenceSeq: lastEvidenceSeq };
|
|
163
|
-
}
|
|
164
|
-
return { claims, allClaimedDone, lastTaskSetSeq, lastMutationSeq, evidence };
|
|
165
|
-
}
|