@vincemakes/kiso-runtime 0.13.0 → 0.14.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 CHANGED
@@ -31,6 +31,8 @@ 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";
34
36
  export { kisoHome, projectArtifacts, recordTrust, trustFor } from "./trust.js";
35
37
  export type { ProjectArtifact, ProjectArtifacts, TrustDecision, TrustRecord } from "./trust.js";
36
38
  export { canonicalizeUsage } from "./usage/canonical.js";
package/dist/index.js CHANGED
@@ -31,6 +31,9 @@ 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";
34
37
  // trust
35
38
  export { kisoHome, projectArtifacts, recordTrust, trustFor } from "./trust.js";
36
39
  // usage — the canonical accounting schema (E2/1.3.0, R4b-1 ruling:
package/dist/run.d.ts CHANGED
@@ -13,7 +13,7 @@ import { type AgentSession, type SessionConfig } from "./session.js";
13
13
  export declare class Run implements AsyncIterable<Event> {
14
14
  #private;
15
15
  runId: string;
16
- constructor(store: SessionStore, adapter: Adapter, config: SessionConfig, session: AgentSession, input: string | undefined, externalSignal: AbortSignalLike | undefined, resume: boolean);
16
+ constructor(store: SessionStore, adapter: Adapter, config: SessionConfig, session: AgentSession, input: string | undefined, externalSignal: AbortSignalLike | undefined, resume: boolean, source?: import("@vincemakes/kiso-core").MessageSource);
17
17
  /** Cancel the run: propagates to the adapter (SDK) and future executions. */
18
18
  abort(): void;
19
19
  [Symbol.asyncIterator](): AsyncIterator<Event>;
package/dist/run.js CHANGED
@@ -23,12 +23,17 @@ export class Run {
23
23
  #session;
24
24
  #input;
25
25
  #resume;
26
+ #source;
26
27
  #abort = new AbortController();
27
28
  #externalSignal;
28
29
  #decisionIds = [];
29
30
  #uncertaintyIds = [];
30
31
  #started = false;
31
- constructor(store, adapter, config, session, input, externalSignal, resume) {
32
+ constructor(store, adapter, config, session, input, externalSignal, resume,
33
+ // TV-1B: durable PROVENANCE for the input (e.g. the verification
34
+ // seed's source:"system") — who produced the line, never a
35
+ // provider-role escalation. Absent = plain user input.
36
+ source) {
32
37
  this.#store = store;
33
38
  this.#adapter = adapter;
34
39
  this.#config = config;
@@ -36,6 +41,7 @@ export class Run {
36
41
  this.#input = input;
37
42
  this.#externalSignal = externalSignal;
38
43
  this.#resume = resume;
44
+ this.#source = source;
39
45
  this.runId = crypto.randomUUID();
40
46
  }
41
47
  /** Cancel the run: propagates to the adapter (SDK) and future executions. */
@@ -244,7 +250,7 @@ export class Run {
244
250
  // session. The prompt is also the first event the consumer
245
251
  // sees, so what was asked and what happened live in the same
246
252
  // stream.
247
- const inputEvent = log.append({ type: "user_input", content: this.#input });
253
+ const inputEvent = log.append({ type: "user_input", content: this.#input, ...(this.#source !== undefined ? { source: this.#source } : {}) });
248
254
  await this.#session.persist(this.runId, inputEvent);
249
255
  yield inputEvent;
250
256
  // 2. The loop projects from the session log — multi-turn context
@@ -714,8 +720,8 @@ export class Run {
714
720
  }
715
721
  // ruling #12 correction one: the honest note rides the recovered failure too —
716
722
  // the receipt and the repaired tool_result reproduce the live path
717
- // losslessly.
718
- if (result.isError && tool?.idempotent !== true) {
723
+ // losslessly. WR-1-F1: precondition refusals excluded — nothing ran.
724
+ if (result.isError && result.errorKind !== "precondition" && tool?.idempotent !== true) {
719
725
  result = {
720
726
  ...result,
721
727
  content: `${result.content}\n[non-idempotent tool failed — its side effects may have partially applied; verify before retrying]`,
package/dist/session.d.ts CHANGED
@@ -28,6 +28,7 @@
28
28
  * same package, same exports (index.ts re-exports all four).
29
29
  */
30
30
  import { EventLog, type AbortSignalLike, type Adapter, type Event, type KisoExtension, type Message, type PermissionDecision, type Tool } from "@vincemakes/kiso-core";
31
+ import { type TaskAssessment } from "./task-assessment.js";
31
32
  import { type SessionStore } from "./store.js";
32
33
  import { Run } from "./run.js";
33
34
  /** TUI2-R3v2 ③ — one off-trajectory model request (session.sideQuery).
@@ -158,6 +159,7 @@ export declare class AgentSession {
158
159
  /** Run one user turn. Iterate to consume; `run.abort()` cancels. */
159
160
  run(input: string, options?: {
160
161
  signal?: AbortSignalLike;
162
+ source?: import("@vincemakes/kiso-core").MessageSource;
161
163
  }): Run;
162
164
  /**
163
165
  * Continue the interrupted run (Area 2): apply durable decisions,
@@ -220,6 +222,17 @@ export declare class AgentSession {
220
222
  approve(decisionId: string, allow: boolean, reason?: string): Promise<void>;
221
223
  /** Executions that started but never reported a result (crash window). */
222
224
  uncertainExecutions(): import("./ledger.js").ExecutionRecord[];
225
+ /**
226
+ * TV-1A — assess the task claims and their evidence freshness over THIS
227
+ * session's durable log. The shared-tool set comes from the live tools'
228
+ * own `effects.concurrency: "shared"` certificates (one direction of
229
+ * truth, never a second declaration); the evidence policy defaults to
230
+ * {"shell"} — the convention the task extension's own "make the LAST
231
+ * item a verification step" guidance produces.
232
+ */
233
+ assessTasks(opts?: {
234
+ readonly evidenceTools?: ReadonlySet<string>;
235
+ }): TaskAssessment;
223
236
  /**
224
237
  * The human's verdict on an interrupted execution, keyed by EXECUTION ID
225
238
  * (B group): "rerun" (the human says the side effect did NOT happen — the
package/dist/session.js CHANGED
@@ -29,6 +29,11 @@
29
29
  */
30
30
  import { EventLog, projectMessages, } from "@vincemakes/kiso-core";
31
31
  import { executionLedger } from "./ledger.js";
32
+ import { assessTasks } from "./task-assessment.js";
33
+ /** TV-1A — the session-level evidence policy: the PURE projection defaults
34
+ * to ∅ (never inventing evidence); the session names the one built-in
35
+ * verification surface. Override per call for custom evidence tools. */
36
+ const DEFAULT_EVIDENCE_TOOLS = new Set(["shell"]);
32
37
  import { denialResult } from "@vincemakes/kiso-core";
33
38
  import { DROP_PLACEHOLDER, estimateSummarySavings, KEEP_RECENT_ROUNDS, KEEP_TOKENS_DEFAULT, lastSummaryPoint, MAX_SUMMARY_FAILURES, policyTriggerFromWindow, serializeCovered, SUMMARY_MAX_OUTPUT, summarizeConversation, summaryBoundarySeq, } from "./summarize.js";
34
39
  import { canonicalizeUsage } from "./usage/canonical.js";
@@ -252,7 +257,7 @@ export class AgentSession {
252
257
  /** Run one user turn. Iterate to consume; `run.abort()` cancels. */
253
258
  run(input, options) {
254
259
  this.ensureHealthy();
255
- return new Run(this.#store, this.#adapter, this.#config, this, input, options?.signal, false);
260
+ return new Run(this.#store, this.#adapter, this.#config, this, input, options?.signal, false, options?.source);
256
261
  }
257
262
  /**
258
263
  * Continue the interrupted run (Area 2): apply durable decisions,
@@ -528,6 +533,25 @@ export class AgentSession {
528
533
  uncertainExecutions() {
529
534
  return [...executionLedger(this.log.all).values()].filter((r) => r.status === "uncertain");
530
535
  }
536
+ /**
537
+ * TV-1A — assess the task claims and their evidence freshness over THIS
538
+ * session's durable log. The shared-tool set comes from the live tools'
539
+ * own `effects.concurrency: "shared"` certificates (one direction of
540
+ * truth, never a second declaration); the evidence policy defaults to
541
+ * {"shell"} — the convention the task extension's own "make the LAST
542
+ * item a verification step" guidance produces.
543
+ */
544
+ assessTasks(opts) {
545
+ const sharedTools = new Set();
546
+ for (const tool of this.#config.registry.list()) {
547
+ if (tool.effects?.concurrency === "shared")
548
+ sharedTools.add(tool.name);
549
+ }
550
+ return assessTasks(this.log.all, {
551
+ sharedTools,
552
+ evidenceTools: opts?.evidenceTools ?? DEFAULT_EVIDENCE_TOOLS,
553
+ });
554
+ }
531
555
  /**
532
556
  * The human's verdict on an interrupted execution, keyed by EXECUTION ID
533
557
  * (B group): "rerun" (the human says the side effect did NOT happen — the
@@ -0,0 +1,73 @@
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 and reuse EC-1's certificate
13
+ * direction verbatim:
14
+ *
15
+ * - MUTATION marker: `tool_execution_started` (intent-to-effect) of any
16
+ * name NOT in `sharedTools`. Started — not the receipt — so failed and
17
+ * crash-window executions invalidate too, and pre-EC-1 overlap eras are
18
+ * covered by the same rule. Absence of a certificate is a mutation.
19
+ * ONE proven exception (TV-1B, grounded in WR-1A): an execution whose
20
+ * terminal receipt is failed(errorKind:"precondition") never counts —
21
+ * that kind's frozen contract is "work refused BEFORE it starts".
22
+ * - EVIDENCE: only receipts (`tool_execution_succeeded`) of names in
23
+ * `evidenceTools`. Default ∅ — the projection never invents evidence.
24
+ * What TV-1A's verdict asserts is POSITIONAL: the arc's last
25
+ * intent-to-effect was a successful evidence-class run — an "evidence",
26
+ * never a "proof" (semantic knowledge is the TV-1B driver's).
27
+ * - task_set itself never invalidates: recording the claim after the check
28
+ * is the natural arc (tests → mark done), and the claim-recording act
29
+ * mutates nothing the evidence observed.
30
+ */
31
+ import type { Event } from "@vincemakes/kiso-core";
32
+ /** One item of the model's plan, exactly as last claimed. */
33
+ export interface TaskClaim {
34
+ readonly text: string;
35
+ readonly status: "pending" | "active" | "done";
36
+ }
37
+ /** The freshness verdict. `stale` and `unreadable` NAME their cause. */
38
+ export type EvidenceVerdict = {
39
+ readonly kind: "verified";
40
+ readonly evidenceSeq: number;
41
+ } | {
42
+ readonly kind: "stale";
43
+ readonly evidenceSeq: number;
44
+ readonly invalidatedBySeq: number;
45
+ } | {
46
+ readonly kind: "none";
47
+ } | {
48
+ readonly kind: "unreadable";
49
+ readonly atSeq: number;
50
+ readonly reason: string;
51
+ };
52
+ export interface TaskAssessment {
53
+ /** The LAST successful task_set echo, parsed — the model's claims. */
54
+ readonly claims: readonly TaskClaim[];
55
+ /** True only for a non-empty plan whose every item is claimed done. */
56
+ readonly allClaimedDone: boolean;
57
+ readonly lastTaskSetSeq: number | null;
58
+ /** Seq of the last mutation-class `tool_execution_started`, if any. */
59
+ readonly lastMutationSeq: number | null;
60
+ readonly evidence: EvidenceVerdict;
61
+ }
62
+ /**
63
+ * Assess the task claims and their evidence freshness over a durable
64
+ * trajectory. Pure: same events, same options, same assessment.
65
+ */
66
+ export declare function assessTasks(events: readonly Event[], opts?: {
67
+ /** Names whose executions never mutate (from `effects.concurrency:
68
+ * "shared"` certificates). Default ∅ — everything mutates. */
69
+ readonly sharedTools?: ReadonlySet<string>;
70
+ /** Names whose successful receipts count as evidence. Default ∅ —
71
+ * nothing does. */
72
+ readonly evidenceTools?: ReadonlySet<string>;
73
+ }): TaskAssessment;
@@ -0,0 +1,133 @@
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 and reuse EC-1's certificate
13
+ * direction verbatim:
14
+ *
15
+ * - MUTATION marker: `tool_execution_started` (intent-to-effect) of any
16
+ * name NOT in `sharedTools`. Started — not the receipt — so failed and
17
+ * crash-window executions invalidate too, and pre-EC-1 overlap eras are
18
+ * covered by the same rule. Absence of a certificate is a mutation.
19
+ * ONE proven exception (TV-1B, grounded in WR-1A): an execution whose
20
+ * terminal receipt is failed(errorKind:"precondition") never counts —
21
+ * that kind's frozen contract is "work refused BEFORE it starts".
22
+ * - EVIDENCE: only receipts (`tool_execution_succeeded`) of names in
23
+ * `evidenceTools`. Default ∅ — the projection never invents evidence.
24
+ * What TV-1A's verdict asserts is POSITIONAL: the arc's last
25
+ * intent-to-effect was a successful evidence-class run — an "evidence",
26
+ * never a "proof" (semantic knowledge is the TV-1B driver's).
27
+ * - task_set itself never invalidates: recording the claim after the check
28
+ * is the natural arc (tests → mark done), and the claim-recording act
29
+ * mutates nothing the evidence observed.
30
+ */
31
+ const TASK_TOOL = "task_set";
32
+ const STATUSES = new Set(["pending", "active", "done"]);
33
+ const COUNT_LINE = /^\[task\] (\d+) items? — (\d+) pending, (\d+) active, (\d+) done$/;
34
+ const ITEM_LINE = /^\[(\w+)\] (.*)$/;
35
+ /** Parse the canonical task_set echo (the frozen content contract the
36
+ * checklist cell also reads). Returns the claims, or the reason it
37
+ * cannot — never a guess. */
38
+ function parseEcho(content) {
39
+ const lines = content.split("\n");
40
+ const head = COUNT_LINE.exec(lines[0] ?? "");
41
+ if (head === null)
42
+ return { reason: "line 1 is not the [task] count line" };
43
+ const total = Number(head[1]);
44
+ const declared = { pending: Number(head[2]), active: Number(head[3]), done: Number(head[4]) };
45
+ const claims = [];
46
+ for (let i = 1; i < lines.length; i += 1) {
47
+ const m = ITEM_LINE.exec(lines[i]);
48
+ if (m === null || !STATUSES.has(m[1]))
49
+ return { reason: `line ${i + 1} is not a [pending|active|done] item line` };
50
+ claims.push({ text: m[2], status: m[1] });
51
+ }
52
+ const counts = { pending: 0, active: 0, done: 0 };
53
+ for (const c of claims)
54
+ counts[c.status] += 1;
55
+ if (claims.length !== total || counts.pending !== declared.pending || counts.active !== declared.active || counts.done !== declared.done) {
56
+ return { reason: "the count line disagrees with the item lines" };
57
+ }
58
+ return { claims };
59
+ }
60
+ /**
61
+ * Assess the task claims and their evidence freshness over a durable
62
+ * trajectory. Pure: same events, same options, same assessment.
63
+ */
64
+ export function assessTasks(events, opts) {
65
+ const shared = opts?.sharedTools ?? new Set();
66
+ const evidenceNames = opts?.evidenceTools ?? new Set();
67
+ // TV-1B: PASS 1 — executions whose terminal receipt is a
68
+ // PRECONDITION failure are PROVEN no-mutation (WR-1A froze that
69
+ // contract: work refused before it starts). Everything else stays
70
+ // conservative: no receipt (crash window), fatal/transient/
71
+ // invalid_input, success, and legacy receipts with no errorKind.
72
+ // Two passes, no temporal rollback state — same events, same verdict.
73
+ const provenNoMutation = new Set();
74
+ for (const ev of events) {
75
+ if (ev.type === "tool_execution_failed" && ev.errorKind === "precondition") {
76
+ provenNoMutation.add(ev.executionId);
77
+ }
78
+ }
79
+ const nameByExecution = new Map();
80
+ let claims = [];
81
+ let lastTaskSetSeq = null;
82
+ let unreadable = null;
83
+ let lastMutationSeq = null;
84
+ let lastEvidenceSeq = null;
85
+ let staleBySeq = null;
86
+ for (const ev of events) {
87
+ if (ev.type === "tool_execution_started") {
88
+ nameByExecution.set(ev.executionId, ev.name);
89
+ if (ev.name !== TASK_TOOL && !shared.has(ev.name) && !provenNoMutation.has(ev.executionId)) {
90
+ lastMutationSeq = ev.seq;
91
+ if (lastEvidenceSeq !== null && staleBySeq === null)
92
+ staleBySeq = ev.seq;
93
+ }
94
+ }
95
+ else if (ev.type === "tool_execution_succeeded") {
96
+ const name = nameByExecution.get(ev.executionId);
97
+ if (name === TASK_TOOL) {
98
+ const parsed = parseEcho(ev.result.content);
99
+ if ("reason" in parsed) {
100
+ unreadable = { atSeq: ev.seq, reason: parsed.reason };
101
+ claims = [];
102
+ lastTaskSetSeq = ev.seq;
103
+ }
104
+ else {
105
+ unreadable = null;
106
+ claims = parsed.claims;
107
+ lastTaskSetSeq = ev.seq;
108
+ }
109
+ }
110
+ else if (name !== undefined && evidenceNames.has(name)) {
111
+ // a fresh evidence receipt supersedes both the older evidence
112
+ // AND any staleness its own start incurred.
113
+ lastEvidenceSeq = ev.seq;
114
+ staleBySeq = null;
115
+ }
116
+ }
117
+ }
118
+ const allClaimedDone = claims.length > 0 && claims.every((c) => c.status === "done");
119
+ let evidence;
120
+ if (unreadable !== null) {
121
+ evidence = { kind: "unreadable", atSeq: unreadable.atSeq, reason: unreadable.reason };
122
+ }
123
+ else if (lastEvidenceSeq === null) {
124
+ evidence = { kind: "none" };
125
+ }
126
+ else if (staleBySeq !== null) {
127
+ evidence = { kind: "stale", evidenceSeq: lastEvidenceSeq, invalidatedBySeq: staleBySeq };
128
+ }
129
+ else {
130
+ evidence = { kind: "verified", evidenceSeq: lastEvidenceSeq };
131
+ }
132
+ return { claims, allClaimedDone, lastTaskSetSeq, lastMutationSeq, evidence };
133
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "kiso runtime — durable multi-turn agent sessions: AgentDefinition, AgentRuntime, AgentSession, Run, append-only JSONL store.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,11 +25,11 @@
25
25
  "test": "vitest run"
26
26
  },
27
27
  "dependencies": {
28
- "@vincemakes/kiso-core": "0.13.0"
28
+ "@vincemakes/kiso-core": "0.14.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@vincemakes/kiso-provider-anthropic": "0.13.0",
32
- "@vincemakes/kiso-provider-openai": "0.13.0"
31
+ "@vincemakes/kiso-provider-anthropic": "0.14.0",
32
+ "@vincemakes/kiso-provider-openai": "0.14.0"
33
33
  },
34
34
  "peerDependenciesMeta": {
35
35
  "@vincemakes/kiso-provider-anthropic": {
@@ -40,7 +40,7 @@
40
40
  }
41
41
  },
42
42
  "devDependencies": {
43
- "@vincemakes/kiso-evals": "0.13.0",
43
+ "@vincemakes/kiso-evals": "0.14.0",
44
44
  "@types/node": "^26.1.2",
45
45
  "typescript": "^5.7.2",
46
46
  "vitest": "^3.0.0"