@vincemakes/kiso-code 0.34.0 → 0.37.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/auth/adapter-options.d.ts +22 -0
- package/dist/auth/adapter-options.js +57 -0
- package/dist/auth/credentials.d.ts +23 -0
- package/dist/auth/credentials.js +39 -9
- package/dist/breaker.d.ts +2 -2
- package/dist/breaker.js +2 -2
- package/dist/builtin.d.ts +6 -1
- package/dist/builtin.js +7 -2
- package/dist/chat.d.ts +101 -14
- package/dist/chat.js +194 -53
- package/dist/config.d.ts +16 -1
- package/dist/config.js +48 -21
- package/dist/dispatch.js +33 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +129 -28
- package/dist/mode.d.ts +10 -1
- package/dist/mode.js +15 -6
- package/dist/state.d.ts +10 -0
- package/dist/state.js +16 -1
- package/package.json +16 -16
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ModelProfile } from "../config.js";
|
|
2
|
+
/** The credential shape both call sites can produce: `authForProfile`'s
|
|
3
|
+
* return value, and the startup path's already-narrowed `resolved`. */
|
|
4
|
+
export type AdapterAuth = {
|
|
5
|
+
readonly type: "oauth";
|
|
6
|
+
readonly providerId: string;
|
|
7
|
+
} | {
|
|
8
|
+
readonly type: "api-key";
|
|
9
|
+
readonly apiKey: string;
|
|
10
|
+
};
|
|
11
|
+
/** Exactly the runtime `buildAdapter` option set. */
|
|
12
|
+
export interface AdapterOptions {
|
|
13
|
+
readonly apiKey?: string;
|
|
14
|
+
readonly baseUrl?: string;
|
|
15
|
+
readonly promptCaching?: boolean;
|
|
16
|
+
readonly oauth?: () => Promise<{
|
|
17
|
+
readonly access: string;
|
|
18
|
+
readonly accountId: string;
|
|
19
|
+
}>;
|
|
20
|
+
readonly promptCacheKey?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function adapterOptionsFor(profile: ModelProfile, auth: AdapterAuth, sessionId?: string): AdapterOptions;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astra F1 (P0) — THE ONE PLACE THE ADAPTER'S WIRE CONFIG IS BUILT.
|
|
3
|
+
*
|
|
4
|
+
* The P0's mechanism is a single sentence: the CLI hands the adapter an
|
|
5
|
+
* EXPLICIT endpoint, so the provider SDKs never read `ANTHROPIC_BASE_URL` /
|
|
6
|
+
* `OPENAI_BASE_URL` for themselves while `authForProfile` has already
|
|
7
|
+
* chosen the STORED VENDOR KEY for the vendor's own origin.
|
|
8
|
+
*
|
|
9
|
+
* That sentence had no home. It was a spread written out twice — at startup
|
|
10
|
+
* in index.ts and on the `/model` switch in dispatch.ts — which is F1's own
|
|
11
|
+
* defect class: one rule, two copies, and the copies drift. The first fix
|
|
12
|
+
* for this finding edited both copies. This gives the rule somewhere to
|
|
13
|
+
* live, and a test somewhere to point.
|
|
14
|
+
*
|
|
15
|
+
* The extraction found the two sites had ALREADY drifted:
|
|
16
|
+
*
|
|
17
|
+
* - `promptCacheKey` was passed at startup whenever the session had an
|
|
18
|
+
* id, but on `/model` only for an OAuth profile. Both sites pass it
|
|
19
|
+
* identically now, which is a tidiness fix and NOT a wire change —
|
|
20
|
+
* finding IA-0360-F1 corrects the claim this comment used to make.
|
|
21
|
+
* The Responses adapter serializes it on the ChatGPT (OAuth) target
|
|
22
|
+
* ALONE; the first-party API-key target builds an empty `extraBody`
|
|
23
|
+
* (provider-openai-responses `resolveTarget`), so it never carried a
|
|
24
|
+
* cache key and still does not. Passing an internal option at two
|
|
25
|
+
* construction sites is not evidence that the adapter puts it on the
|
|
26
|
+
* wire on both authentication paths — I traced the field as far as the
|
|
27
|
+
* provider's constructor and stopped one level short of the branch
|
|
28
|
+
* that drops it.
|
|
29
|
+
* - `streamIdleMs` is set at startup and not on `/model`. It is NOT an
|
|
30
|
+
* adapter option — it is an agent-definition field the runtime reads —
|
|
31
|
+
* so it is out of this function's scope and stays where it is, noted
|
|
32
|
+
* here so the next reader does not think it was missed.
|
|
33
|
+
*/
|
|
34
|
+
import { effectiveBaseUrl } from "./credentials.js";
|
|
35
|
+
import { oauthTokenThunk } from "./token.js";
|
|
36
|
+
export function adapterOptionsFor(profile, auth, sessionId) {
|
|
37
|
+
// OR-1: exactly one sign-in shape reaches the adapter. An OAuth profile
|
|
38
|
+
// has no key to pass — it passes the thunk the adapter re-resolves per
|
|
39
|
+
// request instead. PH-1c (PH-F19): a keyless profile is an
|
|
40
|
+
// unauthenticated endpoint and its placeholder satisfies the SDK's ctor.
|
|
41
|
+
const credential = auth.type === "oauth" ? { oauth: oauthTokenThunk(auth.providerId) } : { apiKey: auth.apiKey };
|
|
42
|
+
// Astra F1 (P0): ALWAYS EXPLICIT. Passing nothing let the SDK read its
|
|
43
|
+
// own environment variable, and a profile with no baseUrl then sent the
|
|
44
|
+
// stored vendor key wherever that variable pointed.
|
|
45
|
+
const url = effectiveBaseUrl(profile.kind, profile.baseUrl);
|
|
46
|
+
return {
|
|
47
|
+
...credential,
|
|
48
|
+
...(url !== undefined ? { baseUrl: url } : {}),
|
|
49
|
+
// OR-1: the ChatGPT backend's cache lane is the SESSION — one
|
|
50
|
+
// conversation's requests share a key, different conversations never
|
|
51
|
+
// do. The one entry point with no session id is `kiso sessions`, a
|
|
52
|
+
// read-only listing that streams nothing, so its absence costs no
|
|
53
|
+
// cache.
|
|
54
|
+
...(sessionId !== undefined ? { promptCacheKey: sessionId } : {}),
|
|
55
|
+
...(profile.promptCaching !== undefined ? { promptCaching: profile.promptCaching } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -23,6 +23,29 @@ export declare class AuthError extends Error {
|
|
|
23
23
|
export declare const KNOWN_PROVIDERS: readonly string[];
|
|
24
24
|
/** The providers whose sign-in is an OAuth flow rather than a key. */
|
|
25
25
|
export declare const OAUTH_PROVIDERS: readonly string[];
|
|
26
|
+
/**
|
|
27
|
+
* Astra F1 (P0) — THE ONE RESOLVED EFFECTIVE ENDPOINT.
|
|
28
|
+
*
|
|
29
|
+
* `providerIdOf` read an ABSENT `baseUrl` as the vendor's own endpoint and
|
|
30
|
+
* returned the vendor id, so `authForProfile` handed back the STORED vendor
|
|
31
|
+
* key. Correct in isolation. But the adapter factories passed `baseURL` to the
|
|
32
|
+
* SDK only when the profile NAMED one, and with none named the Anthropic and
|
|
33
|
+
* OpenAI SDKs read `ANTHROPIC_BASE_URL` / `OPENAI_BASE_URL` from the
|
|
34
|
+
* environment themselves — so the saved official key went to whatever the
|
|
35
|
+
* environment named.
|
|
36
|
+
*
|
|
37
|
+
* Two sides disagreed about what "no baseUrl" meant. THE FIX IS TO RESOLVE IT
|
|
38
|
+
* ONCE, here, and give both sides the same answer: the credential resolver
|
|
39
|
+
* decides which key may go, the adapter is handed an EXPLICIT url so the SDK
|
|
40
|
+
* never consults its environment.
|
|
41
|
+
*
|
|
42
|
+
* 0.32.2's R1 bound the explicit-URL path. This is the absent-URL path, which
|
|
43
|
+
* is the common configuration.
|
|
44
|
+
*/
|
|
45
|
+
export declare const VENDOR_DEFAULT: Readonly<Record<string, string>>;
|
|
46
|
+
/** The endpoint a profile ACTUALLY talks to: its own, or the vendor's
|
|
47
|
+
* default for its kind. Never the environment's — that is the defect. */
|
|
48
|
+
export declare function effectiveBaseUrl(kind: string, baseUrl?: string): string | undefined;
|
|
26
49
|
/** The provider identity a profile's credential is stored under, or null
|
|
27
50
|
* for an origin nobody recognizes (custom: env var only). */
|
|
28
51
|
export declare function providerIdOf(kind: string, baseUrl?: string): string | null;
|
package/dist/auth/credentials.js
CHANGED
|
@@ -50,6 +50,35 @@ const CHATGPT_ORIGIN = "https://chatgpt.com";
|
|
|
50
50
|
* the dialect it speaks. */
|
|
51
51
|
const ANTHROPIC_ORIGIN = "https://api.anthropic.com";
|
|
52
52
|
const OPENAI_ORIGIN = "https://api.openai.com";
|
|
53
|
+
/**
|
|
54
|
+
* Astra F1 (P0) — THE ONE RESOLVED EFFECTIVE ENDPOINT.
|
|
55
|
+
*
|
|
56
|
+
* `providerIdOf` read an ABSENT `baseUrl` as the vendor's own endpoint and
|
|
57
|
+
* returned the vendor id, so `authForProfile` handed back the STORED vendor
|
|
58
|
+
* key. Correct in isolation. But the adapter factories passed `baseURL` to the
|
|
59
|
+
* SDK only when the profile NAMED one, and with none named the Anthropic and
|
|
60
|
+
* OpenAI SDKs read `ANTHROPIC_BASE_URL` / `OPENAI_BASE_URL` from the
|
|
61
|
+
* environment themselves — so the saved official key went to whatever the
|
|
62
|
+
* environment named.
|
|
63
|
+
*
|
|
64
|
+
* Two sides disagreed about what "no baseUrl" meant. THE FIX IS TO RESOLVE IT
|
|
65
|
+
* ONCE, here, and give both sides the same answer: the credential resolver
|
|
66
|
+
* decides which key may go, the adapter is handed an EXPLICIT url so the SDK
|
|
67
|
+
* never consults its environment.
|
|
68
|
+
*
|
|
69
|
+
* 0.32.2's R1 bound the explicit-URL path. This is the absent-URL path, which
|
|
70
|
+
* is the common configuration.
|
|
71
|
+
*/
|
|
72
|
+
export const VENDOR_DEFAULT = {
|
|
73
|
+
anthropic: ANTHROPIC_ORIGIN,
|
|
74
|
+
"openai-compat": `${OPENAI_ORIGIN}/v1`,
|
|
75
|
+
"openai-responses": `${OPENAI_ORIGIN}/v1`,
|
|
76
|
+
};
|
|
77
|
+
/** The endpoint a profile ACTUALLY talks to: its own, or the vendor's
|
|
78
|
+
* default for its kind. Never the environment's — that is the defect. */
|
|
79
|
+
export function effectiveBaseUrl(kind, baseUrl) {
|
|
80
|
+
return baseUrl ?? VENDOR_DEFAULT[kind];
|
|
81
|
+
}
|
|
53
82
|
/** The provider identity a profile's credential is stored under, or null
|
|
54
83
|
* for an origin nobody recognizes (custom: env var only). */
|
|
55
84
|
export function providerIdOf(kind, baseUrl) {
|
|
@@ -74,23 +103,24 @@ export function providerIdOf(kind, baseUrl) {
|
|
|
74
103
|
// The compat kind was already right — an unrecognized origin resolves
|
|
75
104
|
// to null and the env var pays — so this makes the other two agree with
|
|
76
105
|
// the one that had the rule.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
//
|
|
107
|
+
// Astra F1: THE SAME RESOLVER THE ADAPTER USES. An absent baseUrl used to
|
|
108
|
+
// be a special case here — three `if (baseUrl === undefined) return "<the
|
|
109
|
+
// vendor>"` branches — and a DIFFERENT special case at the adapter, where
|
|
110
|
+
// it meant "let the SDK read the environment". Two copies of one rule is
|
|
111
|
+
// how the two sides came to disagree. Resolved once, this function is left
|
|
112
|
+
// with the single question it exists to ask: does that origin belong to
|
|
113
|
+
// the vendor?
|
|
114
|
+
const origin = originOf(effectiveBaseUrl(kind, baseUrl));
|
|
115
|
+
if (kind === "anthropic")
|
|
81
116
|
return origin === ANTHROPIC_ORIGIN ? "anthropic" : null;
|
|
82
|
-
}
|
|
83
117
|
if (kind === "openai-responses") {
|
|
84
|
-
if (baseUrl === undefined)
|
|
85
|
-
return "openai"; // the vendor's own default endpoint
|
|
86
118
|
if (origin === CHATGPT_ORIGIN)
|
|
87
119
|
return "chatgpt";
|
|
88
120
|
return origin === OPENAI_ORIGIN ? "openai" : null;
|
|
89
121
|
}
|
|
90
122
|
if (kind !== "openai-compat")
|
|
91
123
|
return null;
|
|
92
|
-
if (baseUrl === undefined)
|
|
93
|
-
return "openai";
|
|
94
124
|
return origin === null ? null : (ORIGIN_TO_PROVIDER[origin] ?? null);
|
|
95
125
|
}
|
|
96
126
|
function originOf(baseUrl) {
|
package/dist/breaker.d.ts
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* deny beat `bypass` — and the model receives it as the call's result with
|
|
19
19
|
* the reason (the 1c grammar: refuse with a reason and continue), so the
|
|
20
20
|
* next turn has to change course. The human sees the denial on the
|
|
21
|
-
* transcript with `decidedBy: breaker
|
|
22
|
-
*
|
|
21
|
+
* transcript with `decidedBy: breaker`. LT2B-F1 removes the periodic
|
|
22
|
+
* turn-count question; this evidence-based refusal remains independent.
|
|
23
23
|
*
|
|
24
24
|
* Counting: CONSECUTIVE failed results of one (tool, input) key within a
|
|
25
25
|
* run. A success, a different call, or the run's terminal resets it. The
|
package/dist/breaker.js
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* deny beat `bypass` — and the model receives it as the call's result with
|
|
19
19
|
* the reason (the 1c grammar: refuse with a reason and continue), so the
|
|
20
20
|
* next turn has to change course. The human sees the denial on the
|
|
21
|
-
* transcript with `decidedBy: breaker
|
|
22
|
-
*
|
|
21
|
+
* transcript with `decidedBy: breaker`. LT2B-F1 removes the periodic
|
|
22
|
+
* turn-count question; this evidence-based refusal remains independent.
|
|
23
23
|
*
|
|
24
24
|
* Counting: CONSECUTIVE failed results of one (tool, input) key within a
|
|
25
25
|
* run. A success, a different call, or the run's terminal resets it. The
|
package/dist/builtin.d.ts
CHANGED
|
@@ -5,4 +5,9 @@ export declare function builtInLayer(user: readonly KisoExtension[], project: re
|
|
|
5
5
|
* bridge exists (an interactive TTY). No bridge, no fourth built-in:
|
|
6
6
|
* a headless session never pays the rent for a question nobody could
|
|
7
7
|
* answer, and its tool table cannot mention ask_user. */
|
|
8
|
-
ask?: AskUI
|
|
8
|
+
ask?: AskUI,
|
|
9
|
+
/** Astra F7: the env-var NAMES the configured profiles authenticate with.
|
|
10
|
+
* The mcp extension spawns its stdio children while it is being
|
|
11
|
+
* constructed, so the names have to arrive here — after construction is
|
|
12
|
+
* too late. Only the config knows them; nothing else can. */
|
|
13
|
+
secretEnvNames?: readonly string[]): Promise<readonly KisoExtension[]>;
|
package/dist/builtin.js
CHANGED
|
@@ -30,8 +30,13 @@ export async function builtInLayer(user, project,
|
|
|
30
30
|
* bridge exists (an interactive TTY). No bridge, no fourth built-in:
|
|
31
31
|
* a headless session never pays the rent for a question nobody could
|
|
32
32
|
* answer, and its tool table cannot mention ask_user. */
|
|
33
|
-
ask
|
|
34
|
-
|
|
33
|
+
ask,
|
|
34
|
+
/** Astra F7: the env-var NAMES the configured profiles authenticate with.
|
|
35
|
+
* The mcp extension spawns its stdio children while it is being
|
|
36
|
+
* constructed, so the names have to arrive here — after construction is
|
|
37
|
+
* too late. Only the config knows them; nothing else can. */
|
|
38
|
+
secretEnvNames = []) {
|
|
39
|
+
const all = await Promise.all([createMcp({ secretEnvNames }), createSkills(), createSubagent(), ...(ask === undefined ? [] : [createAsk(ask)])]);
|
|
35
40
|
const shadowed = all.filter((b) => user.some((u) => u.name === b.name));
|
|
36
41
|
for (const s of shadowed) {
|
|
37
42
|
console.error(`[extensions] user extension "${s.name}" shadows the built-in — the built-in is not loaded`);
|
package/dist/chat.d.ts
CHANGED
|
@@ -26,8 +26,58 @@ export declare function autoCompactFromEnv(): AutoCompact | undefined;
|
|
|
26
26
|
* config window (merge round B), both beat the 200k default. The microcompact
|
|
27
27
|
* threshold is derived from it (50%), and the status line's ~ctx estimate
|
|
28
28
|
* is measured against it — one source of truth for the window.
|
|
29
|
+
*
|
|
30
|
+
* CTX-1: with no argument this reads the LIVE binding, which is what every
|
|
31
|
+
* display caller wants. `/model` needs the window of the model it is
|
|
32
|
+
* switching TO, before that binding is live, so it passes the pair
|
|
33
|
+
* explicitly. Reading module state a caller is about to change is how the
|
|
34
|
+
* threshold got stuck in the first place.
|
|
35
|
+
*
|
|
36
|
+
* The argument is a PAIR, never two optional halves: a profile with no
|
|
37
|
+
* `baseUrl` is endpoint-less, and a half-given argument would have it
|
|
38
|
+
* inherit the OUTGOING model's endpoint — the same bug one field over.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* THE WINDOW SOMEBODY STATED, or null when nobody has.
|
|
42
|
+
*
|
|
43
|
+
* Three sources, in order: the config/profile window, KISO_CONTEXT_WINDOW,
|
|
44
|
+
* and the metadata registry. Each is a claim someone made and dated. When
|
|
45
|
+
* none of them speaks, this returns NULL rather than a number — because
|
|
46
|
+
* the question "how much of the window is left" has no answer without a
|
|
47
|
+
* window, and every display that shows a percentage needs this one, not
|
|
48
|
+
* the policy value below.
|
|
49
|
+
*
|
|
50
|
+
* DeepSeek is the live case: its /models endpoint returns ids only, the
|
|
51
|
+
* responses carry no window, and the registry records null on purpose. The
|
|
52
|
+
* display used to divide by a hardcoded 200,000 anyway and print a
|
|
53
|
+
* confident `ctx left ~82%` against a figure nobody had measured.
|
|
54
|
+
*/
|
|
55
|
+
export declare function knownContextWindow(of?: {
|
|
56
|
+
readonly model: string;
|
|
57
|
+
readonly baseUrl?: string;
|
|
58
|
+
}): number | null;
|
|
59
|
+
export declare function contextWindowTokens(of?: {
|
|
60
|
+
readonly model: string;
|
|
61
|
+
readonly baseUrl?: string;
|
|
62
|
+
}): number;
|
|
63
|
+
/**
|
|
64
|
+
* CTX-1: THE ONE derivation of the compaction threshold from the window.
|
|
65
|
+
*
|
|
66
|
+
* Startup (`index.ts`) and the `/model` switch (`dispatch.ts`) both need
|
|
67
|
+
* this number, and two call sites computing the same thing is how
|
|
68
|
+
* `promptCacheKey` drifted before `adapterOptionsFor` collected it. The
|
|
69
|
+
* policy — half the window — lives here and nowhere else.
|
|
70
|
+
*
|
|
71
|
+
* CAPACITY is not POLICY. The window is what the model can hold; this is
|
|
72
|
+
* when we choose to clear old tool results. They are 2:1 today because
|
|
73
|
+
* that ratio has never been measured, not because it is derived from
|
|
74
|
+
* anything. Moving the policy means changing this line, and the whole
|
|
75
|
+
* product moves with it.
|
|
29
76
|
*/
|
|
30
|
-
export declare function
|
|
77
|
+
export declare function microcompactThresholdFor(of?: {
|
|
78
|
+
readonly model: string;
|
|
79
|
+
readonly baseUrl?: string;
|
|
80
|
+
}): number;
|
|
31
81
|
/**
|
|
32
82
|
* B area: approximate context ratio — chars/4 of the projected messages vs
|
|
33
83
|
* the model window. Marked ~ everywhere it is shown; no counting API.
|
|
@@ -95,6 +145,56 @@ export interface UsageDelta {
|
|
|
95
145
|
* total convention (INPUT_CONVENTIONS), never a crash.
|
|
96
146
|
*/
|
|
97
147
|
export declare function usageFromEvent(route: string | undefined, ev: import("@vincemakes/kiso-core").Usage, prevTotal: number | null, model?: string, endpoint?: string): UsageDelta;
|
|
148
|
+
/** W22 (owner, 2026-09-14) — the TURN's usage, summed across its calls.
|
|
149
|
+
*
|
|
150
|
+
* EXISTING-BEHAVIOR CHANGE — declared, never a silent side-fix:
|
|
151
|
+
* the recap and the running row carried the LAST call's figures
|
|
152
|
+
* (`usage = delta.usage`), and read as the turn's by everyone who saw
|
|
153
|
+
* them: the owner's own dogfood, a nine-call turn that had spent 120,358
|
|
154
|
+
* prompt tokens and 4,608 output tokens, reported `in 412 out 924 ·
|
|
155
|
+
* cache 98%` — the ninth call's fresh input and the ninth call's output.
|
|
156
|
+
* The variables are the same and the definitions are unchanged; the SCOPE
|
|
157
|
+
* is the turn's.
|
|
158
|
+
*
|
|
159
|
+
* Two rules, inherited rather than invented:
|
|
160
|
+
* - `in` stays the canonical FRESH count (E2, R2a-1): the turn's sum is
|
|
161
|
+
* the input the turn actually bought at full price. `cache` is summed
|
|
162
|
+
* too, but only the recap's ratio reads it (cache/(in+cache)) — the raw
|
|
163
|
+
* total is the ledger's business, never the row's, because a sum over
|
|
164
|
+
* calls counts the same prefix once per call.
|
|
165
|
+
* - an unmeasured term makes the sum unmeasured (OR-10's rule, applied to
|
|
166
|
+
* the sum): `null` propagates, and one call with `known: false` makes
|
|
167
|
+
* the turn's figure unknown rather than a lower bound dressed as a
|
|
168
|
+
* total. The row then says nothing, as it always did for an unknown
|
|
169
|
+
* call.
|
|
170
|
+
*
|
|
171
|
+
* The miss estimate and the decode rate stay PER CALL: `missed` is the
|
|
172
|
+
* overlap of consecutive prompts (a total-side quantity), and the rate is
|
|
173
|
+
* the call that just settled timing itself. */
|
|
174
|
+
export declare function accumulateUsage(prev: RunUsage, delta: RunUsage): RunUsage;
|
|
175
|
+
/**
|
|
176
|
+
* W22-R1 (Astra): the turn's usage ledger, as ONE definition.
|
|
177
|
+
*
|
|
178
|
+
* A TURN is the sum over its CALLS; a CALL is its LATEST report. Both
|
|
179
|
+
* halves are load-bearing: the openai-compat adapter yields a usage event
|
|
180
|
+
* from either of two stream shapes and its `usageSent` flag only decides
|
|
181
|
+
* whether to append a trailing unknown — it does not stop both from
|
|
182
|
+
* firing. W22 substituted, so a second report silently replaced the first
|
|
183
|
+
* and no screen changed; summing without the call boundary turned the same
|
|
184
|
+
* stream into a WRONG NUMBER (a ledger of fresh 80 / out 10 shown as
|
|
185
|
+
* 160 / 15).
|
|
186
|
+
*
|
|
187
|
+
* It is a function rather than three variables in the loop because the rule
|
|
188
|
+
* then has one place to be right — the same reason `microcompactThresholdFor`
|
|
189
|
+
* exists, and the drift `promptCacheKey` demonstrated when it did not.
|
|
190
|
+
*/
|
|
191
|
+
export declare function turnUsageLedger(): {
|
|
192
|
+
observe: (ev: {
|
|
193
|
+
readonly type: string;
|
|
194
|
+
}) => void;
|
|
195
|
+
report: (u: RunUsage) => void;
|
|
196
|
+
total: () => RunUsage | null;
|
|
197
|
+
};
|
|
98
198
|
/** v2b: the spinner merged into the STATUS BAR (the v2a standalone glyph
|
|
99
199
|
* is gone) — docked only, 200ms rotation between the request and the
|
|
100
200
|
* first event. */
|
|
@@ -170,19 +270,6 @@ export declare function parseSaferOptions(text: string): SaferOption[] | null;
|
|
|
170
270
|
* echo is UI, the chip is the record; the momentary double-render is
|
|
171
271
|
* the design's explicit point).
|
|
172
272
|
*/
|
|
173
|
-
/** LT-2b — the turn checkpoint (kiso-doc/kiso-lt1-mini-spec-2026-09-09.md,
|
|
174
|
-
* part two). R3e stands: no hard turn limit on an interactive session. What
|
|
175
|
-
* a long run gets instead is a QUESTION every CHECKPOINT_TURNS model turns —
|
|
176
|
-
* the run is paused between two model turns (the generator is simply not
|
|
177
|
-
* pulled while the panel is up; the kernel does nothing meanwhile) and the
|
|
178
|
-
* human answers. "Keep going" resumes it, the next question one checkpoint
|
|
179
|
-
* later; "Stop here" aborts the run — `aborted by user`, resumable, never
|
|
180
|
-
* silent. A decline (esc) is "keep going": the run was already running, and
|
|
181
|
-
* esc on a panel is the panel's decline, not the run's abort. Headless
|
|
182
|
-
* entries never see it: the panel needs the dock, and a pipe, `-p` or a
|
|
183
|
-
* task file has none. 50 is named here; a finding moves it, never a stray
|
|
184
|
-
* edit. */
|
|
185
|
-
export declare const CHECKPOINT_TURNS = 50;
|
|
186
273
|
export declare function consumeRun(session: AgentSession, run: Run, input: LineInput, turnNo: number, faux: boolean, statusCb: ((usage: RunUsage, ctxRatio: number, costUsd?: number | null, tokPerSec?: number | null) => void) | null,
|
|
187
274
|
/** W21: the amend words ("Yes + feedback") ride the NEXT user turn —
|
|
188
275
|
* threaded from chat's submitTurn; absent in the recovery flow
|