@vincemakes/kiso-runtime 0.1.38 → 0.2.1
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/agent.d.ts +1 -0
- package/dist/agent.js +2 -0
- package/dist/index.d.ts +32 -11
- package/dist/index.js +41 -11
- package/dist/internal.d.ts +25 -0
- package/dist/internal.js +25 -0
- package/dist/lock-adapter.js +46 -1
- package/dist/run.js +40 -5
- package/dist/session.d.ts +9 -0
- package/dist/session.js +8 -0
- package/dist/trace/analyze.d.ts +35 -0
- package/dist/trace/analyze.js +51 -0
- package/dist/trace/guard.d.ts +52 -0
- package/dist/trace/guard.js +224 -0
- package/dist/trace/hash.d.ts +22 -0
- package/dist/trace/hash.js +34 -0
- package/dist/trace/manifest.d.ts +36 -0
- package/dist/trace/manifest.js +100 -0
- package/dist/trace/record.d.ts +145 -0
- package/dist/trace/record.js +257 -0
- package/dist/trace/rent.d.ts +81 -0
- package/dist/trace/rent.js +96 -0
- package/dist/trace/writer.d.ts +43 -0
- package/dist/trace/writer.js +154 -0
- package/dist/usage/canonical.d.ts +123 -0
- package/dist/usage/canonical.js +152 -0
- package/package.json +10 -6
package/dist/agent.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface AgentDefinition {
|
|
|
62
62
|
* loop's policy chain. */
|
|
63
63
|
readonly extensions?: readonly KisoExtension[];
|
|
64
64
|
}
|
|
65
|
+
/** @deprecated the canonical name is `Agent` (root export, 1.1.0); this alias is removed in the next major. */
|
|
65
66
|
export declare class AgentRuntime {
|
|
66
67
|
#private;
|
|
67
68
|
constructor(definition: AgentDefinition);
|
package/dist/agent.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { EventLog, ToolRegistry } from "@vincemakes/kiso-core";
|
|
15
15
|
import { AgentSession } from "./session.js";
|
|
16
|
+
/** @deprecated the canonical name is `Agent` (root export, 1.1.0); this alias is removed in the next major. */
|
|
16
17
|
export class AgentRuntime {
|
|
17
18
|
#definition;
|
|
18
19
|
#registry;
|
|
@@ -54,6 +55,7 @@ export class AgentRuntime {
|
|
|
54
55
|
const adapter = await this.#adapterPromise;
|
|
55
56
|
const config = {
|
|
56
57
|
model: this.#definition.model,
|
|
58
|
+
...(this.#definition.provider !== undefined ? { provider: this.#definition.provider } : {}),
|
|
57
59
|
...(this.#definition.systemPrompt !== undefined ? { systemPrompt: this.#definition.systemPrompt } : {}),
|
|
58
60
|
registry: this.#registry,
|
|
59
61
|
...(this.#definition.permissionPolicy !== undefined || this.#definition.hooks !== undefined
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The SDK surface — the curated root manifest (S1, 2026-08-12).
|
|
3
|
+
*
|
|
4
|
+
* Every name here is a review-approved contract: adding or removing one
|
|
5
|
+
* WITHOUT the ritual (the review's sign-off + a public-surface.json
|
|
6
|
+
* refresh) turns the public-surface gate red. See docs/sdk.md for the
|
|
7
|
+
* contract, the aliases, and what deliberately lives behind the
|
|
8
|
+
* first-party door.
|
|
9
|
+
*
|
|
10
|
+
* What is NOT here is not the SDK. The recovery / compose / summarize /
|
|
11
|
+
* lock-adapter machinery and buildAdapter live behind "./internal"
|
|
12
|
+
* (unstable, first-party only — see internal.ts).
|
|
13
|
+
*
|
|
14
|
+
* Canonical names: Agent = AgentRuntime, Session = AgentSession. The old
|
|
15
|
+
* names are deprecated aliases, removed in the next major (additive,
|
|
16
|
+
* ADR-0051 Amendment 1 — release rounds move the cli minor).
|
|
17
|
+
*/
|
|
18
|
+
export { AgentRuntime, AgentRuntime as Agent, createAgent } from "./agent.js";
|
|
19
|
+
export type { AgentDefinition, PermissionPolicy, PermissionRule } from "./agent.js";
|
|
20
|
+
export { AgentSession, AgentSession as Session } from "./session.js";
|
|
21
|
+
export { PoisonedSessionError, ResumeBlockedError } from "./session.js";
|
|
22
|
+
export type { ApprovalRequest, CompactInfo, SessionConfig, SummarizeResult } from "./session.js";
|
|
23
|
+
export { Run } from "./run.js";
|
|
24
|
+
export { SessionStore, StaleWriterError, StoreCorruptionError } from "./store.js";
|
|
25
|
+
export type { Event, SessionMeta, StoreRecord } from "./store.js";
|
|
26
|
+
export { disposeExtensions, loadExtensions, loadProjectExtensions } from "./extensions.js";
|
|
27
|
+
export type { KisoExtension } from "./extensions.js";
|
|
28
|
+
export { executionForCallId, executionLedger } from "./ledger.js";
|
|
29
|
+
export type { ExecutionRecord, ExecutionStatus } from "./ledger.js";
|
|
30
|
+
export { kisoHome, projectArtifacts, recordTrust, trustFor } from "./trust.js";
|
|
31
|
+
export type { ProjectArtifact, ProjectArtifacts, TrustDecision, TrustRecord } from "./trust.js";
|
|
32
|
+
export { canonicalizeUsage } from "./usage/canonical.js";
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The SDK surface — the curated root manifest (S1, 2026-08-12).
|
|
3
|
+
*
|
|
4
|
+
* Every name here is a review-approved contract: adding or removing one
|
|
5
|
+
* WITHOUT the ritual (the review's sign-off + a public-surface.json
|
|
6
|
+
* refresh) turns the public-surface gate red. See docs/sdk.md for the
|
|
7
|
+
* contract, the aliases, and what deliberately lives behind the
|
|
8
|
+
* first-party door.
|
|
9
|
+
*
|
|
10
|
+
* What is NOT here is not the SDK. The recovery / compose / summarize /
|
|
11
|
+
* lock-adapter machinery and buildAdapter live behind "./internal"
|
|
12
|
+
* (unstable, first-party only — see internal.ts).
|
|
13
|
+
*
|
|
14
|
+
* Canonical names: Agent = AgentRuntime, Session = AgentSession. The old
|
|
15
|
+
* names are deprecated aliases, removed in the next major (additive,
|
|
16
|
+
* ADR-0051 Amendment 1 — release rounds move the cli minor).
|
|
17
|
+
*/
|
|
18
|
+
// agent
|
|
19
|
+
export { AgentRuntime, AgentRuntime as Agent, createAgent } from "./agent.js";
|
|
20
|
+
// session
|
|
21
|
+
export { AgentSession, AgentSession as Session } from "./session.js";
|
|
22
|
+
export { PoisonedSessionError, ResumeBlockedError } from "./session.js";
|
|
23
|
+
// run
|
|
24
|
+
export { Run } from "./run.js";
|
|
25
|
+
// store
|
|
26
|
+
export { SessionStore, StaleWriterError, StoreCorruptionError } from "./store.js";
|
|
27
|
+
// extensions
|
|
28
|
+
export { disposeExtensions, loadExtensions, loadProjectExtensions } from "./extensions.js";
|
|
29
|
+
// ledger
|
|
30
|
+
export { executionForCallId, executionLedger } from "./ledger.js";
|
|
31
|
+
// trust
|
|
32
|
+
export { kisoHome, projectArtifacts, recordTrust, trustFor } from "./trust.js";
|
|
33
|
+
// usage — the canonical accounting schema (E2/1.3.0, R4b-1 ruling:
|
|
34
|
+
// additive minor, one function; the signature is frozen the moment this
|
|
35
|
+
// line lands — changing it is a MAJOR ritual). R5b-④a set the final
|
|
36
|
+
// shape at freeze time: the trailing `table?: PricingTable` injection
|
|
37
|
+
// slot defaults to the builtin v1 table, so the R5a-1-commercial table
|
|
38
|
+
// rides day one without ever widening this signature. Raw stays
|
|
39
|
+
// provider observation; canonicalizeUsage derives at the accounting
|
|
40
|
+
// boundary (R4 Case B — the frozen usage union does not move).
|
|
41
|
+
export { canonicalizeUsage } from "./usage/canonical.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The FIRST-PARTY door — NOT part of the public SDK contract (S1,
|
|
3
|
+
* 2026-08-12, adjudicated).
|
|
4
|
+
*
|
|
5
|
+
* Unstable: anything here can move or vanish in any release without a
|
|
6
|
+
* major bump. External consumers must import the curated root
|
|
7
|
+
* (index.ts); this file exists for the in-repo consumers (apps/cli,
|
|
8
|
+
* extensions, tests) that need machinery the SDK deliberately does not
|
|
9
|
+
* expose — the recovery / compose / summarize / lock-adapter internals
|
|
10
|
+
* and buildAdapter.
|
|
11
|
+
*
|
|
12
|
+
* This is today's pre-S1 index.ts verbatim: removing the door would be a
|
|
13
|
+
* breaking change for the first-party consumers mid-migration.
|
|
14
|
+
*/
|
|
15
|
+
export * from "./agent.js";
|
|
16
|
+
export * from "./session.js";
|
|
17
|
+
export * from "./run.js";
|
|
18
|
+
export * from "./recovery.js";
|
|
19
|
+
export * from "./compose.js";
|
|
20
|
+
export * from "./summarize.js";
|
|
21
|
+
export * from "./store.js";
|
|
22
|
+
export * from "./lock-adapter.js";
|
|
23
|
+
export * from "./ledger.js";
|
|
24
|
+
export * from "./extensions.js";
|
|
25
|
+
export * from "./trust.js";
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The FIRST-PARTY door — NOT part of the public SDK contract (S1,
|
|
3
|
+
* 2026-08-12, adjudicated).
|
|
4
|
+
*
|
|
5
|
+
* Unstable: anything here can move or vanish in any release without a
|
|
6
|
+
* major bump. External consumers must import the curated root
|
|
7
|
+
* (index.ts); this file exists for the in-repo consumers (apps/cli,
|
|
8
|
+
* extensions, tests) that need machinery the SDK deliberately does not
|
|
9
|
+
* expose — the recovery / compose / summarize / lock-adapter internals
|
|
10
|
+
* and buildAdapter.
|
|
11
|
+
*
|
|
12
|
+
* This is today's pre-S1 index.ts verbatim: removing the door would be a
|
|
13
|
+
* breaking change for the first-party consumers mid-migration.
|
|
14
|
+
*/
|
|
15
|
+
export * from "./agent.js";
|
|
16
|
+
export * from "./session.js";
|
|
17
|
+
export * from "./run.js";
|
|
18
|
+
export * from "./recovery.js";
|
|
19
|
+
export * from "./compose.js";
|
|
20
|
+
export * from "./summarize.js";
|
|
21
|
+
export * from "./store.js";
|
|
22
|
+
export * from "./lock-adapter.js";
|
|
23
|
+
export * from "./ledger.js";
|
|
24
|
+
export * from "./extensions.js";
|
|
25
|
+
export * from "./trust.js";
|
package/dist/lock-adapter.js
CHANGED
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
* pauses plus SIGSTOP/SIGCONT, and locate the freeze points via
|
|
62
62
|
* ready-marker files (ADR-0050 §test affordances).
|
|
63
63
|
*/
|
|
64
|
+
import { execFileSync } from "node:child_process";
|
|
64
65
|
import { closeSync, fsyncSync, linkSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
65
66
|
import { randomUUID } from "node:crypto";
|
|
66
67
|
import { join } from "node:path";
|
|
@@ -78,14 +79,58 @@ export class LockUnavailableError extends Error {
|
|
|
78
79
|
this.name = "LockUnavailableError";
|
|
79
80
|
}
|
|
80
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* ADR-0050 Amendment 1 (Finding R-I-1): the liveness probe is
|
|
84
|
+
* state-aware. A dead holder can linger in the process table after a
|
|
85
|
+
* kill — the exiting state (STAT E on macOS: the kill landing while a
|
|
86
|
+
* pty syscall is blocked, the dead session's terminal left open) or an
|
|
87
|
+
* un-reaped zombie (STAT Z). POSIX reports BOTH alive to kill(pid, 0)
|
|
88
|
+
* (they exist until reaped), so the takeover refused a holder that can
|
|
89
|
+
* never execute another session write. Both states are probed and
|
|
90
|
+
* judged DEAD. A probe failure (unreadable state) maintains the
|
|
91
|
+
* pre-amendment behavior — alive, the fail-safe refusal (prefer a false
|
|
92
|
+
* refusal over a double-write). Live-process semantics and the PID-reuse
|
|
93
|
+
* rules do not move: a live foreign writer is still refused.
|
|
94
|
+
*
|
|
95
|
+
* ADR-0050 Amendment 2 (Finding R-I-p-3): the state letters are matched
|
|
96
|
+
* ANYWHERE in the state string, not as the first character. The
|
|
97
|
+
* exit-path linger's ps output is "?E" — the first character is the
|
|
98
|
+
* no-controlling-terminal marker "?", with the E sitting AFTER it — so
|
|
99
|
+
* first-character matching judged the finding's own documented shape
|
|
100
|
+
* alive. The ps state alphabet (macOS + Linux) has no flag letters
|
|
101
|
+
* "E"/"Z": an occurrence anywhere is the process-state code, and the
|
|
102
|
+
* holder is dead.
|
|
103
|
+
*/
|
|
81
104
|
function isAlive(pid) {
|
|
82
105
|
try {
|
|
83
106
|
process.kill(pid, 0);
|
|
84
|
-
return true;
|
|
85
107
|
}
|
|
86
108
|
catch (err) {
|
|
87
109
|
return err.code === "EPERM";
|
|
88
110
|
}
|
|
111
|
+
const state = processState(pid);
|
|
112
|
+
// A null state (probe failure) keeps the pre-amendment behavior —
|
|
113
|
+
// judged alive, the fail-safe refusal.
|
|
114
|
+
return state === null || (!state.includes("E") && !state.includes("Z"));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The process state via `ps -o state= -p <pid>` (macOS/Linux) — the
|
|
118
|
+
* whole state string (a multi-char string like "Ss+" or "?E" is a
|
|
119
|
+
* combined flag set; the E/Z process-state codes may sit after the "?"
|
|
120
|
+
* no-tty marker or other flag letters, so the matching in isAlive scans
|
|
121
|
+
* the whole string). An empty or unreadable state — the process
|
|
122
|
+
* vanished between the kill and the probe, or ps itself failed —
|
|
123
|
+
* returns null; isAlive's fail-safe branch then judges the holder alive
|
|
124
|
+
* (the pre-amendment behavior).
|
|
125
|
+
*/
|
|
126
|
+
function processState(pid) {
|
|
127
|
+
try {
|
|
128
|
+
const state = execFileSync("ps", ["-o", "state=", "-p", String(pid)], { encoding: "utf8" }).trim();
|
|
129
|
+
return state.length > 0 ? state : null;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
89
134
|
}
|
|
90
135
|
/**
|
|
91
136
|
* Read a lock file's holder identity (round 4 formats, unchanged — the
|
package/dist/run.js
CHANGED
|
@@ -8,6 +8,8 @@ import { ABORTED, MergedSignal, abortable, openRunId } from "./recovery.js";
|
|
|
8
8
|
import { deriveRecoveryPlan, invocationSeqOf } from "./recovery-plan.js";
|
|
9
9
|
import { composeApprovalChain, composeSystemPrompt, composeToolTable, microcompactFor } from "./compose.js";
|
|
10
10
|
import { truncationGuard } from "./truncation-guard.js";
|
|
11
|
+
import { RequestTracer, traceGuard } from "./trace/guard.js";
|
|
12
|
+
import { runtimeVersion } from "./trace/writer.js";
|
|
11
13
|
import { ResumeBlockedError } from "./session.js";
|
|
12
14
|
/**
|
|
13
15
|
* A single turn. Async-iterable, so `for await (const ev of session.run(x))`
|
|
@@ -47,6 +49,7 @@ export class Run {
|
|
|
47
49
|
// The WHOLE body is one try/finally: a consumer that abandons the
|
|
48
50
|
// run at ANY yield (even the user_input one) must release the
|
|
49
51
|
// session's single-run slot and its approval resolvers.
|
|
52
|
+
let tracer = null;
|
|
50
53
|
try {
|
|
51
54
|
// round 4: health is re-checked when the iterator ACTUALLY starts —
|
|
52
55
|
// a run constructed before the session was poisoned must fail
|
|
@@ -54,10 +57,10 @@ export class Run {
|
|
|
54
57
|
this.#session.ensureHealthy();
|
|
55
58
|
this.#session.beginRun(this);
|
|
56
59
|
const log = this.#session.log;
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
|
|
60
|
+
// The static prompt parts — computed ONCE, before the tracer, and
|
|
61
|
+
// reused for both the composed string (below) and the rent ledger
|
|
62
|
+
// (E3): a single evaluation keeps the model-visible byte stream
|
|
63
|
+
// byte-identical to the pre-E3 run (I6, trace-bytes.test.ts).
|
|
61
64
|
// 0.1.40 (R-C item 1): the tool substitution table — the ACTIVE tool
|
|
62
65
|
// set's vocabulary, snippets, and guidelines — sits BETWEEN the
|
|
63
66
|
// session's base prompt and the extension appends: generated
|
|
@@ -67,6 +70,35 @@ export class Run {
|
|
|
67
70
|
const basePrompt = toolTable === "" ? this.#config.systemPrompt
|
|
68
71
|
: this.#config.systemPrompt === undefined ? toolTable
|
|
69
72
|
: `${this.#config.systemPrompt}\n\n${toolTable}`;
|
|
73
|
+
// E3 — the ledger's parts: the base as CONFIGURED (what the CLI
|
|
74
|
+
// handed the runtime — the tool table is generated machinery, R3)
|
|
75
|
+
// and the extension appends in load order (R4 attribution). The
|
|
76
|
+
// composed string below is their result; the ledger counts the
|
|
77
|
+
// parts, observation-only. exactOptionalPropertyTypes: an absent
|
|
78
|
+
// surface is an absent key — never an explicit undefined (R9).
|
|
79
|
+
const rentParts = {
|
|
80
|
+
...(this.#config.systemPrompt !== undefined ? { base: this.#config.systemPrompt } : {}),
|
|
81
|
+
appends: (this.#config.extensions ?? []).flatMap((e) => e.systemPrompt?.append === undefined ? [] : [{ name: e.name, text: e.systemPrompt.append }]),
|
|
82
|
+
};
|
|
83
|
+
// E1 (1.2.0): the request tracer — the observation ledger. It
|
|
84
|
+
// sits at the adapter boundary; the model-visible byte stream is
|
|
85
|
+
// untouched (I6, trace-bytes.test.ts). Soft-fail: a degraded
|
|
86
|
+
// writer costs one stderr line and the run goes on.
|
|
87
|
+
tracer = new RequestTracer({
|
|
88
|
+
root: this.#store.root,
|
|
89
|
+
sessionId: this.#session.id,
|
|
90
|
+
runId: this.runId,
|
|
91
|
+
provider: this.#config.provider ?? "adapter",
|
|
92
|
+
model: this.#config.model,
|
|
93
|
+
adapterVersion: runtimeVersion(),
|
|
94
|
+
log: log.all,
|
|
95
|
+
rentParts,
|
|
96
|
+
});
|
|
97
|
+
tracer.init();
|
|
98
|
+
const signal = this.#externalSignal ? new MergedSignal(this.#abort.signal, this.#externalSignal) : this.#abort.signal;
|
|
99
|
+
// E2: the session's own microcompact wins; otherwise the FIRST
|
|
100
|
+
// extension providing a compaction config supplies it.
|
|
101
|
+
const microcompact = microcompactFor(this.#config);
|
|
70
102
|
// E2: the session's own systemPrompt first, then every extension
|
|
71
103
|
// append in LOAD order — deterministic (same extensions → same
|
|
72
104
|
// prompt); no appends → byte-identical to the extension-less run.
|
|
@@ -75,7 +107,7 @@ export class Run {
|
|
|
75
107
|
const loopConfig = () => ({
|
|
76
108
|
// 0.1.40 (R-C item 3): the truncation guard gates the model
|
|
77
109
|
// stream — a truncated turn's tool batch never executes.
|
|
78
|
-
adapter: truncationGuard(this.#adapter),
|
|
110
|
+
adapter: traceGuard(tracer, truncationGuard(this.#adapter)), // tracer assigned above, before loopConfig
|
|
79
111
|
model: this.#config.model,
|
|
80
112
|
sessionId: this.#session.id, // P3: tools see their session (ToolContext.sessionId)
|
|
81
113
|
...(systemPrompt !== undefined ? { systemPrompt } : {}),
|
|
@@ -230,6 +262,9 @@ export class Run {
|
|
|
230
262
|
for (const executionId of this.#uncertaintyIds) {
|
|
231
263
|
this.#session.dropUncertaintyResolver(executionId);
|
|
232
264
|
}
|
|
265
|
+
// E1: the run's ledger story — the run_end lands synchronously
|
|
266
|
+
// (a killed run leaves no run_end, and the next init marks it).
|
|
267
|
+
tracer?.finishRun();
|
|
233
268
|
this.#session.endRun(this);
|
|
234
269
|
}
|
|
235
270
|
}
|
package/dist/session.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export interface CompactInfo {
|
|
|
71
71
|
/** The covered content's estimated tokens (the chars/4 proxy). */
|
|
72
72
|
readonly tokens: number;
|
|
73
73
|
}
|
|
74
|
+
/** @deprecated the canonical name is `Session` (root export, 1.1.0); this alias is removed in the next major. */
|
|
74
75
|
export declare class AgentSession {
|
|
75
76
|
#private;
|
|
76
77
|
readonly id: string;
|
|
@@ -97,6 +98,11 @@ export declare class AgentSession {
|
|
|
97
98
|
* turns (dispatch's /model), never mid-run.
|
|
98
99
|
*/
|
|
99
100
|
setAdapter(adapter: Adapter): void;
|
|
101
|
+
/** E2: the adapter identity ("anthropic" | "openai-compat") — the route
|
|
102
|
+
* key the canonical consumer (CLI usage, the trace block) keys on. The
|
|
103
|
+
* per-run tracer reads the SAME #config.provider; one source, one
|
|
104
|
+
* route — the CLI and the trace can never disagree. */
|
|
105
|
+
get provider(): "anthropic" | "openai-compat" | undefined;
|
|
100
106
|
/** Run one user turn. Iterate to consume; `run.abort()` cancels. */
|
|
101
107
|
run(input: string, options?: {
|
|
102
108
|
signal?: AbortSignalLike;
|
|
@@ -178,6 +184,9 @@ export declare class AgentSession {
|
|
|
178
184
|
}
|
|
179
185
|
export interface SessionConfig {
|
|
180
186
|
readonly model: string;
|
|
187
|
+
/** E1: the adapter identity ("anthropic" | "openai-compat") — trace
|
|
188
|
+
* provenance, additive (S1 surface untouched: type-only, optional). */
|
|
189
|
+
readonly provider?: "anthropic" | "openai-compat";
|
|
181
190
|
readonly systemPrompt?: string;
|
|
182
191
|
readonly tools?: readonly Tool<any>[];
|
|
183
192
|
readonly registry: import("@vincemakes/kiso-core").ToolRegistry;
|
package/dist/session.js
CHANGED
|
@@ -54,6 +54,7 @@ export class ResumeBlockedError extends Error {
|
|
|
54
54
|
this.uncertain = uncertain;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
/** @deprecated the canonical name is `Session` (root export, 1.1.0); this alias is removed in the next major. */
|
|
57
58
|
export class AgentSession {
|
|
58
59
|
id;
|
|
59
60
|
log;
|
|
@@ -154,6 +155,13 @@ export class AgentSession {
|
|
|
154
155
|
setAdapter(adapter) {
|
|
155
156
|
this.#adapter = adapter;
|
|
156
157
|
}
|
|
158
|
+
/** E2: the adapter identity ("anthropic" | "openai-compat") — the route
|
|
159
|
+
* key the canonical consumer (CLI usage, the trace block) keys on. The
|
|
160
|
+
* per-run tracer reads the SAME #config.provider; one source, one
|
|
161
|
+
* route — the CLI and the trace can never disagree. */
|
|
162
|
+
get provider() {
|
|
163
|
+
return this.#config.provider;
|
|
164
|
+
}
|
|
157
165
|
/** Run one user turn. Iterate to consume; `run.abort()` cancels. */
|
|
158
166
|
run(input, options) {
|
|
159
167
|
this.ensureHealthy();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1 (1.2.0) — slice 4, the cache-break derivation (proposal §4, ruling
|
|
3
|
+
* R4b: per-segment hashes + prefix fingerprint; the BREAK COUNT is an
|
|
4
|
+
* analysis-side derivation, never a recorded field).
|
|
5
|
+
*
|
|
6
|
+
* The cacheable prefix is every segment that is NOT the current turn —
|
|
7
|
+
* freshness "fresh" is the boundary (manifest.ts). `cacheableHashes`
|
|
8
|
+
* pairs the manifest segments 1:1 with the per-segment hashes and
|
|
9
|
+
* drops the fresh tail, so the fingerprint and the break derivation
|
|
10
|
+
* share one boundary by construction: a current-turn change alone
|
|
11
|
+
* never moves the fingerprint and never counts a break.
|
|
12
|
+
*
|
|
13
|
+
* `prefixBreak` compares two adjacent requests' cacheable prefixes:
|
|
14
|
+
* the first differing segment is the break, at its (0-based) depth;
|
|
15
|
+
* a prefix that merely GREW (a new turn joined) breaks at the old
|
|
16
|
+
* length. Unchanged prefixes → null (0 breaks). This is what
|
|
17
|
+
* bench/trace-report.mjs and the bench render per-request (slice 5).
|
|
18
|
+
*/
|
|
19
|
+
import type { TraceSegment } from "./record.js";
|
|
20
|
+
/** The cacheable-prefix hashes: segments[i] ↔ hashes[i], dropping every
|
|
21
|
+
* freshness "fresh" segment (the current turn). */
|
|
22
|
+
export declare function cacheableHashes(segments: readonly TraceSegment[], hashes: readonly string[]): string[];
|
|
23
|
+
export interface PrefixBreak {
|
|
24
|
+
/** 0-based segment index within the cacheable prefix where the
|
|
25
|
+
* prefix first diverges (depth 0 = the system prompt). */
|
|
26
|
+
readonly depth: number;
|
|
27
|
+
}
|
|
28
|
+
/** R4b: compare two adjacent requests' cacheable prefixes. null = the
|
|
29
|
+
* prefix is unchanged (0 breaks). A prefix that grew breaks at the old
|
|
30
|
+
* length — the new segment is where caching can no longer attach. */
|
|
31
|
+
export declare function prefixBreak(prev: readonly string[], next: readonly string[]): PrefixBreak | null;
|
|
32
|
+
/** Per-request breaks across a run's request sequence: request k's
|
|
33
|
+
* break is relative to request k−1; the first request has no
|
|
34
|
+
* predecessor (null). */
|
|
35
|
+
export declare function deriveBreaks(requests: readonly (readonly string[])[]): (PrefixBreak | null)[];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1 (1.2.0) — slice 4, the cache-break derivation (proposal §4, ruling
|
|
3
|
+
* R4b: per-segment hashes + prefix fingerprint; the BREAK COUNT is an
|
|
4
|
+
* analysis-side derivation, never a recorded field).
|
|
5
|
+
*
|
|
6
|
+
* The cacheable prefix is every segment that is NOT the current turn —
|
|
7
|
+
* freshness "fresh" is the boundary (manifest.ts). `cacheableHashes`
|
|
8
|
+
* pairs the manifest segments 1:1 with the per-segment hashes and
|
|
9
|
+
* drops the fresh tail, so the fingerprint and the break derivation
|
|
10
|
+
* share one boundary by construction: a current-turn change alone
|
|
11
|
+
* never moves the fingerprint and never counts a break.
|
|
12
|
+
*
|
|
13
|
+
* `prefixBreak` compares two adjacent requests' cacheable prefixes:
|
|
14
|
+
* the first differing segment is the break, at its (0-based) depth;
|
|
15
|
+
* a prefix that merely GREW (a new turn joined) breaks at the old
|
|
16
|
+
* length. Unchanged prefixes → null (0 breaks). This is what
|
|
17
|
+
* bench/trace-report.mjs and the bench render per-request (slice 5).
|
|
18
|
+
*/
|
|
19
|
+
/** The cacheable-prefix hashes: segments[i] ↔ hashes[i], dropping every
|
|
20
|
+
* freshness "fresh" segment (the current turn). */
|
|
21
|
+
export function cacheableHashes(segments, hashes) {
|
|
22
|
+
const out = [];
|
|
23
|
+
for (let i = 0; i < segments.length; i++) {
|
|
24
|
+
if (segments[i].freshness !== "fresh")
|
|
25
|
+
out.push(hashes[i]);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/** R4b: compare two adjacent requests' cacheable prefixes. null = the
|
|
30
|
+
* prefix is unchanged (0 breaks). A prefix that grew breaks at the old
|
|
31
|
+
* length — the new segment is where caching can no longer attach. */
|
|
32
|
+
export function prefixBreak(prev, next) {
|
|
33
|
+
const shared = Math.min(prev.length, next.length);
|
|
34
|
+
for (let i = 0; i < shared; i++) {
|
|
35
|
+
if (prev[i] !== next[i])
|
|
36
|
+
return { depth: i };
|
|
37
|
+
}
|
|
38
|
+
if (prev.length !== next.length)
|
|
39
|
+
return { depth: shared };
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/** Per-request breaks across a run's request sequence: request k's
|
|
43
|
+
* break is relative to request k−1; the first request has no
|
|
44
|
+
* predecessor (null). */
|
|
45
|
+
export function deriveBreaks(requests) {
|
|
46
|
+
const out = [];
|
|
47
|
+
for (let k = 0; k < requests.length; k++) {
|
|
48
|
+
out.push(k === 0 ? null : prefixBreak(requests[k - 1], requests[k]));
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1 (1.2.0) — slice 3, the request tracer (the guard).
|
|
3
|
+
*
|
|
4
|
+
* One TraceRecord per adapter call, settled when the stream ends. The
|
|
5
|
+
* guard lives AT the adapter boundary (run.ts wires
|
|
6
|
+
* truncationGuard(traceGuard(tracer, adapter))): the kernel, the log,
|
|
7
|
+
* and the model-visible byte stream are untouched (I6 — pinned by
|
|
8
|
+
* trace-bytes.test.ts). retryAttempt counts prior calls in this run
|
|
9
|
+
* with an identical contextHash — the loop re-streams the SAME messages
|
|
10
|
+
* array on retry (loop.ts), so an identical hash is exactly "same
|
|
11
|
+
* request, retried" (§1.4).
|
|
12
|
+
*
|
|
13
|
+
* Soft-fail: any failure in trace assembly (never expected — hashes and
|
|
14
|
+
* the manifest cannot throw today) marks the request ABSENT rather than
|
|
15
|
+
* breaking the stream; the writer's own degradation covers I/O.
|
|
16
|
+
*
|
|
17
|
+
* Usage is provider-raw, never normalized (E2's job): openai-compat
|
|
18
|
+
* reports input TOTAL (fresh = input − cacheRead); anthropic's
|
|
19
|
+
* input_tokens is ALREADY fresh-only (the trace records it as-is).
|
|
20
|
+
* A request that settles with NO usage data records the quartet as
|
|
21
|
+
* zeros with cacheWrite null — "0 = unknown", documented at record.ts;
|
|
22
|
+
* the honest nullable quartet is a schema bump, deferred.
|
|
23
|
+
*/
|
|
24
|
+
import type { Adapter, AdapterEvent, Event, StreamOptions } from "@vincemakes/kiso-core";
|
|
25
|
+
import { type RentParts } from "./rent.js";
|
|
26
|
+
export interface RequestTracerDeps {
|
|
27
|
+
root: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
runId: string;
|
|
30
|
+
provider: string;
|
|
31
|
+
model: string;
|
|
32
|
+
/** The adapter contract's implementation version (the runtime's own),
|
|
33
|
+
* resolved once at tracer init; null on failure (soft-fail). */
|
|
34
|
+
adapterVersion?: string | null;
|
|
35
|
+
/** The session log — the manifest's seqRange pointers derive from it. */
|
|
36
|
+
log: readonly Event[];
|
|
37
|
+
/** E3 — the rent ledger's inputs: the base prompt as configured and
|
|
38
|
+
* the per-extension appends in load order (the adapter's composed
|
|
39
|
+
* systemPrompt is their RESULT — the parts are what the ledger
|
|
40
|
+
* counts; the composed string itself is unchanged, I6). */
|
|
41
|
+
rentParts?: RentParts;
|
|
42
|
+
}
|
|
43
|
+
export declare class RequestTracer {
|
|
44
|
+
#private;
|
|
45
|
+
constructor(deps: RequestTracerDeps);
|
|
46
|
+
init(): void;
|
|
47
|
+
wrap(options: StreamOptions, upstream: AsyncIterable<AdapterEvent>): AsyncIterable<AdapterEvent>;
|
|
48
|
+
/** Clean-settle marking for the whole run. */
|
|
49
|
+
finishRun(): void;
|
|
50
|
+
}
|
|
51
|
+
/** Wrap the adapter so every stream() call settles a trace record. */
|
|
52
|
+
export declare function traceGuard(tracer: RequestTracer, adapter: Adapter): Adapter;
|