@vincemakes/kiso-runtime 0.39.2 → 0.40.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/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 | readonly ContentBlock[] | undefined, externalSignal: AbortSignalLike | undefined, resume: boolean, source?: import("@vincemakes/kiso-core").MessageSource);
16
+ constructor(store: SessionStore, adapter: Adapter, config: SessionConfig, session: AgentSession, input: string | readonly ContentBlock[] | undefined, externalSignal: AbortSignalLike | undefined, resume: boolean, source?: import("@vincemakes/kiso-core").MessageSource, via?: import("@vincemakes/kiso-core").UserInputVia);
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
@@ -7,7 +7,7 @@ import { denialResult, loop } from "@vincemakes/kiso-core";
7
7
  import { ABORTED, MergedSignal, abortable, openRunId } from "./recovery.js";
8
8
  import { resolveReasoning } from "./provider/metadata.js";
9
9
  import { deriveRecoveryPlan, invocationSeqOf } from "./recovery-plan.js";
10
- import { composeApprovalChain, composeSystemPrompt, composeToolTable, microcompactFor } from "./compose.js";
10
+ import { composeApprovalChain, composeSystemPrompt, runBasePrompt } from "./compose.js";
11
11
  import { truncationGuard } from "./truncation-guard.js";
12
12
  import { DEFAULT_STREAM_IDLE_MS, idleGuard } from "./idle-guard.js";
13
13
  import { RequestTracer, traceGuard } from "./trace/guard.js";
@@ -30,6 +30,7 @@ export class Run {
30
30
  #input;
31
31
  #resume;
32
32
  #source;
33
+ #via;
33
34
  #abort = new AbortController();
34
35
  #externalSignal;
35
36
  #decisionIds = [];
@@ -38,7 +39,10 @@ export class Run {
38
39
  // TV-1B: durable PROVENANCE for the input (e.g. the verification
39
40
  // seed's source:"system") — who produced the line, never a
40
41
  // provider-role escalation. Absent = plain user input.
41
- source) {
42
+ source,
43
+ // 0.40.0: how a person composed the turn (a skill + the typed line) —
44
+ // recorded for display, never projected into a request.
45
+ via) {
42
46
  this.#store = store;
43
47
  this.#adapter = adapter;
44
48
  this.#config = config;
@@ -47,6 +51,7 @@ export class Run {
47
51
  this.#externalSignal = externalSignal;
48
52
  this.#resume = resume;
49
53
  this.#source = source;
54
+ this.#via = via;
50
55
  this.runId = crypto.randomUUID();
51
56
  }
52
57
  /** Cancel the run: propagates to the adapter (SDK) and future executions. */
@@ -77,10 +82,7 @@ export class Run {
77
82
  // session's base prompt and the extension appends: generated
78
83
  // machinery never outranks the deliberate extension text (the E2
79
84
  // "append lands at the END" contract holds). "" when empty.
80
- const toolTable = composeToolTable(this.#config.registry);
81
- const basePrompt = toolTable === "" ? this.#config.systemPrompt
82
- : this.#config.systemPrompt === undefined ? toolTable
83
- : `${this.#config.systemPrompt}\n\n${toolTable}`;
85
+ const basePrompt = runBasePrompt(this.#config.systemPrompt, this.#config.registry);
84
86
  // E3 — the ledger's parts: the base as CONFIGURED (what the CLI
85
87
  // handed the runtime — the tool table is generated machinery, R3)
86
88
  // and the extension appends in load order (R4 attribution). The
@@ -113,12 +115,30 @@ export class Run {
113
115
  // extension providing a compaction config supplies it. E6: the
114
116
  // contextPolicy override beats the session's own, and its minTurns
115
117
  // no-fire guard may omit the config below the floor.
116
- const microcompact = microcompactFor(this.#config, log.all);
117
118
  // E2: the session's own systemPrompt first, then every extension
118
119
  // append in LOAD order — deterministic (same extensions → same
119
120
  // prompt); no appends → byte-identical to the extension-less run.
120
121
  const systemPrompt = composeSystemPrompt(basePrompt, this.#config.extensions ?? []);
121
122
  const approvalChain = composeApprovalChain(this.#config.extensions ?? []);
123
+ // ADR-0055 Amendment 1 (A1b): the compaction point the kernel asks
124
+ // before every request — the run's own prefix, so an in-band
125
+ // summary reads it at the cache-hit price.
126
+ // The loop resolves the same setting when it builds each request; a
127
+ // setting that cannot run refuses the run there, before any compaction.
128
+ let wireReasoning;
129
+ try {
130
+ wireReasoning = this.#config.reasoning !== undefined ? resolveReasoningOrThrow(this.#config.model, this.#config.reasoning, this.#config.baseUrl).reasoning : undefined;
131
+ }
132
+ catch {
133
+ wireReasoning = undefined;
134
+ }
135
+ const compact = this.#session.compactionPoint({
136
+ ...(systemPrompt !== undefined ? { systemPrompt } : {}),
137
+ tools: () => this.#config.registry.snapshot().specs,
138
+ ...(wireReasoning !== undefined ? { reasoning: wireReasoning } : {}),
139
+ signal,
140
+ ...(this.#config.maxRetries !== undefined ? { maxRetries: this.#config.maxRetries } : {}),
141
+ });
122
142
  const loopConfig = () => ({
123
143
  // 0.1.40 (R-C item 3): the truncation guard gates the model
124
144
  // stream — a truncated turn's tool batch never executes.
@@ -134,7 +154,7 @@ export class Run {
134
154
  ...(this.#config.maxTurns !== undefined ? { maxTurns: this.#config.maxTurns } : {}),
135
155
  ...(this.#config.maxTokens !== undefined ? { maxTokens: this.#config.maxTokens } : {}),
136
156
  ...(this.#config.temperature !== undefined ? { temperature: this.#config.temperature } : {}),
137
- ...(microcompact !== undefined ? { microcompact } : {}),
157
+ ...(compact !== undefined ? { compact } : {}),
138
158
  ...(this.#config.maxRetries !== undefined ? { maxRetries: this.#config.maxRetries } : {}),
139
159
  // MG-1 (A5): the kernel stamps committed envelopes with this.
140
160
  ...(this.#config.continuationScope !== undefined ? { continuationScope: this.#config.continuationScope } : {}),
@@ -265,8 +285,11 @@ export class Run {
265
285
  // session. The prompt is also the first event the consumer
266
286
  // sees, so what was asked and what happened live in the same
267
287
  // stream.
268
- const inputEvent = log.append({ type: "user_input", content: this.#input, ...(this.#source !== undefined ? { source: this.#source } : {}) });
288
+ const inputEvent = log.append({ type: "user_input", content: this.#input, ...(this.#source !== undefined ? { source: this.#source } : {}), ...(this.#via !== undefined ? { via: this.#via } : {}) });
269
289
  await this.#session.persist(this.runId, inputEvent);
290
+ // 0.40.0 dogfood: the list's row says "open" from here — a run
291
+ // killed mid-way reads as interrupted, which is the truth
292
+ this.#session.recordSummary(true);
270
293
  yield inputEvent;
271
294
  // 2. The loop projects from the session log — multi-turn context
272
295
  // is the projection, not a second copy.
@@ -285,6 +308,9 @@ export class Run {
285
308
  // the flush itself failed (poisoned session) — the error
286
309
  // already poisoned everything; nothing more can be done.
287
310
  }
311
+ // 0.40.0 dogfood: the row at the run's end — its terminal, or the
312
+ // pause it stopped at (pending asks counted); best-effort
313
+ this.#session.recordSummary(this.#session.log.all.at(-1)?.type !== "terminal");
288
314
  // The run is over (or abandoned): its unanswered approvals must
289
315
  // fall back to the direct-persist path, so a late approve() is
290
316
  // still durable.
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The 0.40.0 dogfood (item 2) — a session's SUMMARY, the one thing the
3
+ * session list reads.
4
+ *
5
+ * The list used to read every session's whole event log, three times, to
6
+ * draw one row each: on the owner's machine that is 118 sessions, 584 MB,
7
+ * 3.58M events, about 8.3 s before the picker appears. The sidecar the
8
+ * session already keeps (`<id>.meta.json`) is read in 2 ms for all 118. So
9
+ * the list reads the sidecar ONLY, and the sidecar carries what the row
10
+ * shows, as its own tenant beside the execution profile:
11
+ *
12
+ * title, turns, last activity, state (open or the terminal's outcome),
13
+ * uncertain executions, pending asks, whether the workspace is unknown,
14
+ * and where the summary came from (a run, or the one-time migration).
15
+ *
16
+ * The runtime writes it at a run's start (state open — a run killed there
17
+ * reads "interrupted" in the list, which is the truth) and at its end. It
18
+ * is OBSERVATION: nothing in recovery, projection or a request reads it
19
+ * (the §6 purity rule), and a failed write never fails a run.
20
+ *
21
+ * `sessionSummary` is a pure function of the durable records, shared by the
22
+ * live writes and the one-time migration so the two can never disagree.
23
+ */
24
+ import type { Event } from "@vincemakes/kiso-core";
25
+ import { type StoreRecord } from "./store.js";
26
+ export interface SessionSummary {
27
+ /** the session's first substantive prompt (`sessionTitle`); null when unknown */
28
+ readonly title: string | null;
29
+ /** user turns; null when unknown (an unreadable log) */
30
+ readonly turns: number | null;
31
+ /** ms since the epoch — the last record's stamp, or the file's mtime */
32
+ readonly updatedAt: number;
33
+ /** "open" while a run has no terminal event; else the terminal's outcome kind */
34
+ readonly state: string | null;
35
+ readonly uncertain: number;
36
+ readonly asks: number;
37
+ /** the profile records no workspace (a session from before 0.40.0) */
38
+ readonly workspaceUnknown: boolean;
39
+ /** "run" — written by a run; "migration" — the one-time pass over a legacy log */
40
+ readonly source: "run" | "migration";
41
+ }
42
+ /** The summary of a session's events — the core both writers share.
43
+ * `open`, `updatedAt` and `asks` come from whoever knows them: a live
44
+ * session knows its own run and its resolvers; the migration derives
45
+ * them from the records. */
46
+ export declare function summarizeEvents(events: readonly Event[], opts: {
47
+ open: boolean;
48
+ updatedAt: number;
49
+ asks: number;
50
+ workspaceUnknown: boolean;
51
+ source: "run" | "migration";
52
+ }): SessionSummary;
53
+ /** The summary of a session's durable RECORDS — the one-time migration's
54
+ * input (a legacy log read exactly once, never through a live session). */
55
+ export declare function sessionSummary(records: readonly StoreRecord[], opts: {
56
+ workspaceUnknown: boolean;
57
+ source: "run" | "migration";
58
+ fallbackUpdatedAt?: number;
59
+ }): SessionSummary;
60
+ /** The migration's marker: written once every legacy session has a
61
+ * summary. From then on the list never reads a log (lead's ruling A): a
62
+ * session still without a summary shows "no summary". */
63
+ export declare const SUMMARY_MIGRATION_MARKER = ".summaries-migrated";
64
+ export interface SessionListing {
65
+ readonly id: string;
66
+ /** the log file's mtime — the last-activity fallback, never a guess at content */
67
+ readonly mtime: number;
68
+ readonly summary: SessionSummary | null;
69
+ readonly workspace: string | null;
70
+ readonly profileName: string | null;
71
+ }
72
+ /** Every session in `root`, from the DIRECTORY and the sidecars only — no
73
+ * log is opened. The 0.40.0 dogfood's list path. */
74
+ export declare function listSessionSidecars(root: string): SessionListing[];
75
+ /** Whether the one-time migration still has work: the marker is absent. */
76
+ export declare function summaryMigrationPending(root: string): boolean;
77
+ /**
78
+ * The ONE-TIME migration (lead's ruling A). Each session without a summary
79
+ * has its log read exactly once — through the store's plain loader, never a
80
+ * live session, so the profile contract cannot throw it — and its summary
81
+ * written with `source: "migration"`. A log that cannot be read gets what
82
+ * CAN be said (last activity from the file's mtime; title and turns
83
+ * unknown) and never throws the list. Resumable: each summary is written on
84
+ * its own, so a crash mid-way leaves the done ones done; the marker lands
85
+ * only when every session has one.
86
+ */
87
+ export declare function migrateSummaries(root: string, load: (id: string) => readonly StoreRecord[], onEach?: (done: number, total: number) => void): number;
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The 0.40.0 dogfood (item 2) — a session's SUMMARY, the one thing the
3
+ * session list reads.
4
+ *
5
+ * The list used to read every session's whole event log, three times, to
6
+ * draw one row each: on the owner's machine that is 118 sessions, 584 MB,
7
+ * 3.58M events, about 8.3 s before the picker appears. The sidecar the
8
+ * session already keeps (`<id>.meta.json`) is read in 2 ms for all 118. So
9
+ * the list reads the sidecar ONLY, and the sidecar carries what the row
10
+ * shows, as its own tenant beside the execution profile:
11
+ *
12
+ * title, turns, last activity, state (open or the terminal's outcome),
13
+ * uncertain executions, pending asks, whether the workspace is unknown,
14
+ * and where the summary came from (a run, or the one-time migration).
15
+ *
16
+ * The runtime writes it at a run's start (state open — a run killed there
17
+ * reads "interrupted" in the list, which is the truth) and at its end. It
18
+ * is OBSERVATION: nothing in recovery, projection or a request reads it
19
+ * (the §6 purity rule), and a failed write never fails a run.
20
+ *
21
+ * `sessionSummary` is a pure function of the durable records, shared by the
22
+ * live writes and the one-time migration so the two can never disagree.
23
+ */
24
+ import { existsSync, readdirSync, statSync, writeFileSync } from "node:fs";
25
+ import { join } from "node:path";
26
+ import { readProfile, readSummary, writeSummary } from "./profile.js";
27
+ import { executionLedger } from "./ledger.js";
28
+ import { openRunId } from "./recovery.js";
29
+ import { sessionTitle } from "./store.js";
30
+ /** Pending asks from the records alone: a permission request of the OPEN
31
+ * run with no decision and no expiry — the session's own rule, restated
32
+ * over records so the migration needs no live session. */
33
+ function pendingAsks(records) {
34
+ const open = openRunId(records);
35
+ if (open === undefined)
36
+ return 0;
37
+ const decided = new Set();
38
+ const requested = new Set();
39
+ for (const r of records) {
40
+ const e = r.event;
41
+ if (e.type === "permission_requested" && r.runId === open && typeof e.decisionId === "string")
42
+ requested.add(e.decisionId);
43
+ else if ((e.type === "permission_decided" || e.type === "permission_expired") && typeof e.decisionId === "string")
44
+ decided.add(e.decisionId);
45
+ }
46
+ let n = 0;
47
+ for (const id of requested)
48
+ if (!decided.has(id))
49
+ n += 1;
50
+ return n;
51
+ }
52
+ /** The summary of a session's events — the core both writers share.
53
+ * `open`, `updatedAt` and `asks` come from whoever knows them: a live
54
+ * session knows its own run and its resolvers; the migration derives
55
+ * them from the records. */
56
+ export function summarizeEvents(events, opts) {
57
+ let state = opts.open ? "open" : null;
58
+ if (!opts.open) {
59
+ for (let i = events.length - 1; i >= 0; i -= 1) {
60
+ const e = events[i];
61
+ if (e.type === "terminal") {
62
+ state = e.outcome.kind;
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ const title = sessionTitle(events.map((event) => ({ runId: "", ts: 0, event })));
68
+ return {
69
+ title: title === "(no prompt)" ? null : title,
70
+ turns: events.filter((e) => e.type === "user_input").length,
71
+ updatedAt: opts.updatedAt,
72
+ state,
73
+ uncertain: [...executionLedger(events).values()].filter((x) => x.status === "uncertain").length,
74
+ asks: opts.asks,
75
+ workspaceUnknown: opts.workspaceUnknown,
76
+ source: opts.source,
77
+ };
78
+ }
79
+ /** The summary of a session's durable RECORDS — the one-time migration's
80
+ * input (a legacy log read exactly once, never through a live session). */
81
+ export function sessionSummary(records, opts) {
82
+ return summarizeEvents(records.map((r) => r.event), {
83
+ open: openRunId(records) !== undefined,
84
+ updatedAt: records.at(-1)?.ts ?? opts.fallbackUpdatedAt ?? 0,
85
+ asks: pendingAsks(records),
86
+ workspaceUnknown: opts.workspaceUnknown,
87
+ source: opts.source,
88
+ });
89
+ }
90
+ // ── the list's read path, and the one-time migration ───────────────────
91
+ /** The migration's marker: written once every legacy session has a
92
+ * summary. From then on the list never reads a log (lead's ruling A): a
93
+ * session still without a summary shows "no summary". */
94
+ export const SUMMARY_MIGRATION_MARKER = ".summaries-migrated";
95
+ /** Every session in `root`, from the DIRECTORY and the sidecars only — no
96
+ * log is opened. The 0.40.0 dogfood's list path. */
97
+ export function listSessionSidecars(root) {
98
+ let entries;
99
+ try {
100
+ entries = readdirSync(root);
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ const out = [];
106
+ for (const entry of entries.sort()) {
107
+ if (!entry.endsWith(".jsonl"))
108
+ continue;
109
+ const id = entry.slice(0, -".jsonl".length);
110
+ let mtime = 0;
111
+ try {
112
+ const st = statSync(join(root, entry));
113
+ if (st.size === 0)
114
+ continue; // an empty log is no session (the old list skipped it too)
115
+ mtime = st.mtimeMs;
116
+ }
117
+ catch {
118
+ continue;
119
+ }
120
+ const profile = readProfile(root, id);
121
+ out.push({
122
+ id,
123
+ mtime,
124
+ summary: readSummary(root, id),
125
+ workspace: profile.kind === "ok" ? profile.profile.workspace : null,
126
+ profileName: profile.kind === "ok" ? profile.profile.profileName : null,
127
+ });
128
+ }
129
+ return out;
130
+ }
131
+ /** Whether the one-time migration still has work: the marker is absent. */
132
+ export function summaryMigrationPending(root) {
133
+ return !existsSync(join(root, SUMMARY_MIGRATION_MARKER));
134
+ }
135
+ /**
136
+ * The ONE-TIME migration (lead's ruling A). Each session without a summary
137
+ * has its log read exactly once — through the store's plain loader, never a
138
+ * live session, so the profile contract cannot throw it — and its summary
139
+ * written with `source: "migration"`. A log that cannot be read gets what
140
+ * CAN be said (last activity from the file's mtime; title and turns
141
+ * unknown) and never throws the list. Resumable: each summary is written on
142
+ * its own, so a crash mid-way leaves the done ones done; the marker lands
143
+ * only when every session has one.
144
+ */
145
+ export function migrateSummaries(root, load, onEach) {
146
+ const todo = listSessionSidecars(root).filter((s) => s.summary === null);
147
+ todo.forEach((s, i) => {
148
+ let summary;
149
+ try {
150
+ summary = sessionSummary(load(s.id), { workspaceUnknown: s.workspace === null, source: "migration", fallbackUpdatedAt: s.mtime });
151
+ }
152
+ catch {
153
+ summary = { title: null, turns: null, updatedAt: s.mtime, state: null, uncertain: 0, asks: 0, workspaceUnknown: s.workspace === null, source: "migration" };
154
+ }
155
+ try {
156
+ writeSummary(root, s.id, summary);
157
+ }
158
+ catch {
159
+ // a sidecar that cannot be written leaves this session without a
160
+ // summary; the list says so, and the marker still lands below
161
+ }
162
+ onEach?.(i + 1, todo.length);
163
+ });
164
+ writeFileSync(join(root, SUMMARY_MIGRATION_MARKER), `${new Date().toISOString()}\n`);
165
+ return todo.length;
166
+ }
package/dist/session.d.ts CHANGED
@@ -27,7 +27,7 @@
27
27
  * support in recovery.ts, the E1/E2 composition helpers in compose.ts —
28
28
  * same package, same exports (index.ts re-exports all four).
29
29
  */
30
- import { EventLog, type AbortSignalLike, type Adapter, type Event, type KisoExtension, type Message, type PermissionDecision, type Tool } from "@vincemakes/kiso-core";
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 TaskAssessment } from "./task-assessment.js";
32
32
  import { type ContinuationScope } from "@vincemakes/kiso-core";
33
33
  import { type ReasoningSetting } from "./provider/metadata.js";
@@ -93,10 +93,17 @@ export declare class AgentSession {
93
93
  #private;
94
94
  readonly id: string;
95
95
  readonly log: EventLog;
96
+ /** 0.40.0: what this open's drift acknowledgement replaced — the CLI
97
+ * says so once. Null when the session opened without material drift. */
98
+ readonly driftAcknowledgement: import("./agent.js").DriftAcknowledgement | null;
99
+ /** 0.40.0: when the provider last billed this session (epoch ms) — the
100
+ * record's time on load, the write's time after. A provider's prompt
101
+ * cache expires with time, so an old bill means a cold prefix. */
102
+ get lastUsageAt(): number | undefined;
96
103
  /** Permanently invalidate the session after a rejected disk write (round 1). */
97
104
  poison(reason: string): void;
98
105
  ensureHealthy(): void;
99
- constructor(id: string, log: EventLog, store: SessionStore, adapter: Adapter, config: SessionConfig);
106
+ constructor(id: string, log: EventLog, store: SessionStore, adapter: Adapter, config: SessionConfig, lastUsageAt?: number);
100
107
  /** Write-ahead through the store; a rejected write POISONS the session
101
108
  * (round 1/round 4): the in-memory log no longer matches the disk — whatever
102
109
  * the cause (stale handle, corruption, a live external writer, an I/O
@@ -107,6 +114,37 @@ export declare class AgentSession {
107
114
  endRun(run: Run): void;
108
115
  /** The conversation so far, as the model sees it. */
109
116
  projected(): readonly Message[];
117
+ /** 0.40.0: the context as the last bill measured it, plus what the log
118
+ * appended since — or undefined when no bill describes it any more
119
+ * (context-anchor.ts). `events` defaults to the session's log. */
120
+ contextAnchor(events?: readonly Event[]): number | undefined;
121
+ /** 0.40.0: what the context holds — the anchored figure, or the estimate
122
+ * over the projection when no bill describes it. The thresholds and the
123
+ * ctx row read this, never the bare estimate. */
124
+ contextUsed(): number;
125
+ /**
126
+ * ADR-0055 Amendment 1 (A1b) — the kernel's compaction point for ONE run,
127
+ * or undefined when nothing in-run is armed (the kernel then never asks).
128
+ *
129
+ * With `contextPolicy.tiers`: the §1a figure against the tiers — soft
130
+ * waits for a phase end (A3), hard and emergency fire now, an overflow
131
+ * fires once. The summary is in-band (A2) with ONE fallback to the
132
+ * serialised form; if both fail, an emergency or an overflow may prune
133
+ * once (A4b), and otherwise nothing happens and the run goes on.
134
+ *
135
+ * Without tiers, an EXPLICITLY configured `microcompact` keeps its old
136
+ * standing prune here, for SDK callers — the kernel no longer has one.
137
+ */
138
+ compactionPoint(run: {
139
+ readonly systemPrompt?: string;
140
+ readonly tools: () => readonly ToolSpec[];
141
+ readonly reasoning?: {
142
+ readonly thinking?: "adaptive" | "enabled" | "disabled";
143
+ readonly effort?: string;
144
+ };
145
+ readonly signal?: AbortSignalLike;
146
+ readonly maxRetries?: number;
147
+ }): ((events: readonly Event[], messages: readonly Message[], why: "request" | "overflow") => Promise<readonly EventInput[]>) | undefined;
110
148
  /**
111
149
  * merge round B (/model): replace the adapter for SUBSEQUENT runs. The
112
150
  * kernel reads the adapter through the loop-config closure at each
@@ -140,12 +178,18 @@ export declare class AgentSession {
140
178
  /** XP-1: the reasoning axes travel with the binding too; absent =
141
179
  * fresh defaults (a new binding never inherits stale effort). */
142
180
  readonly reasoning?: ReasoningSetting;
181
+ /** 0.40.0: the config profile that named this binding; absent = none
182
+ * (a direct provider/model, or an SDK caller) — recorded as null. */
183
+ readonly profileName?: string | null;
143
184
  /** CTX-1: the compaction threshold follows the live model's window.
144
185
  * Absent KEEPS the current one — a caller that does not know the new
145
186
  * model's window must not silently reset the policy to nothing. */
146
187
  readonly microcompact?: {
147
188
  readonly thresholdTokens: number;
148
189
  };
190
+ /** A1b: the new model's window, for the in-run tiers. Absent keeps the
191
+ * current one, as `microcompact` does. */
192
+ readonly contextWindow?: number;
149
193
  }): void;
150
194
  /**
151
195
  * CTX-1: the compaction threshold alone, with NO profile revision.
@@ -164,6 +208,9 @@ export declare class AgentSession {
164
208
  * every open.
165
209
  */
166
210
  setMicrocompactThreshold(thresholdTokens: number): void;
211
+ /** A1b: the window the in-run tiers are drawn from, for a restored or
212
+ * re-bound session (the CLI's one binding step calls both). */
213
+ setContextWindow(windowTokens: number): void;
167
214
  /** E2: the adapter identity (anthropic / openai-compat / openai-responses) — the route
168
215
  * key the canonical consumer (CLI usage, the trace block) keys on. The
169
216
  * per-run tracer reads the SAME live binding; one source, one
@@ -190,6 +237,15 @@ export declare class AgentSession {
190
237
  };
191
238
  /** XP-1: the selected reasoning axes (resolution happens per request). */
192
239
  get reasoning(): ReasoningSetting;
240
+ /**
241
+ * The 0.40.0 dogfood (item 2): the session list's row, written into the
242
+ * sidecar's `summary` tenant at a run's start (`open`) and at its end.
243
+ * Computed from the IN-MEMORY log — never a re-read of the file — and
244
+ * best-effort: a summary that cannot be written leaves the list saying
245
+ * less, and never fails the run. Observation only: nothing that decides
246
+ * recovery, projection or a request reads it.
247
+ */
248
+ recordSummary(open: boolean): void;
193
249
  /**
194
250
  * TUI2-R3v2 ③ — ONE model request that belongs to no run (the
195
251
  * safer-options seam, adjudicated 2026-08-18).
@@ -238,6 +294,7 @@ export declare class AgentSession {
238
294
  run(input: string | readonly import("@vincemakes/kiso-core").ContentBlock[], options?: {
239
295
  signal?: AbortSignalLike;
240
296
  source?: import("@vincemakes/kiso-core").MessageSource;
297
+ via?: import("@vincemakes/kiso-core").UserInputVia;
241
298
  }): Run;
242
299
  /**
243
300
  * Continue the interrupted run (Area 2): apply durable decisions,
@@ -265,6 +322,7 @@ export declare class AgentSession {
265
322
  drop?: boolean;
266
323
  focus?: string;
267
324
  manualBudget?: boolean;
325
+ onProgress?: (progress: import("./summarize.js").SummaryProgress) => void;
268
326
  }): Promise<SummarizeResult | null>;
269
327
  /**
270
328
  * E6 — the run-start context policy (candidate A + the crux drop arm).
@@ -404,6 +462,19 @@ export interface ContextPolicy {
404
462
  readonly keepResults?: number;
405
463
  readonly minTurns?: number;
406
464
  };
465
+ /**
466
+ * ADR-0055 Amendment 1 (A1b) — the IN-RUN tiers, drawn from the window:
467
+ * soft `min(0.5·w, 400K)` waits for a phase end, hard `min(0.8·w, 700K)`
468
+ * and emergency `w − reserve` fire at the next request, an overflow once.
469
+ * Present, the kernel asks before every request; the run-start summary
470
+ * and the standing microcompact above do not apply. `isCheck` is the
471
+ * phase detector's rule 1 (default: the runner table).
472
+ */
473
+ readonly tiers?: {
474
+ readonly windowTokens: number;
475
+ readonly isCheck?: (command: string) => boolean;
476
+ readonly maxFailures?: number;
477
+ };
407
478
  }
408
479
  export interface SessionConfig {
409
480
  readonly model: string;
@@ -423,6 +494,10 @@ export interface SessionConfig {
423
494
  readonly reasoning?: import("./provider/metadata.js").ReasoningSetting;
424
495
  /** XP-1 internal: a legacy session's deferred revision-1 write. */
425
496
  readonly profilePending?: true;
497
+ /** 0.40.0: the config profile name in force — recorded per revision. */
498
+ readonly profileName?: string;
499
+ /** 0.40.0: set when this open acknowledged material drift. */
500
+ readonly driftAcknowledgement?: import("./agent.js").DriftAcknowledgement;
426
501
  readonly systemPrompt?: string;
427
502
  readonly tools?: readonly Tool<any>[];
428
503
  readonly registry: import("@vincemakes/kiso-core").ToolRegistry;