@lucascouts/claude-agent-acp-plus 0.11.1 → 0.13.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/accounts.d.ts +143 -0
- package/dist/accounts.d.ts.map +1 -0
- package/dist/accounts.js +196 -0
- package/dist/acp-agent.d.ts +76 -2
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +395 -94
- package/dist/context-compaction-meta.d.ts +73 -0
- package/dist/context-compaction-meta.d.ts.map +1 -0
- package/dist/context-compaction-meta.js +53 -0
- package/dist/context-compaction.d.ts +160 -0
- package/dist/context-compaction.d.ts.map +1 -0
- package/dist/context-compaction.js +275 -0
- package/dist/ultracode.d.ts +100 -0
- package/dist/ultracode.d.ts.map +1 -0
- package/dist/ultracode.js +103 -0
- package/dist/usage-markdown.d.ts +92 -0
- package/dist/usage-markdown.d.ts.map +1 -0
- package/dist/usage-markdown.js +373 -0
- package/package.json +9 -9
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The versioned `_meta` extension that carries context-compaction facts on the
|
|
3
|
+
* synthetic ACP tool call (story 010, R2.1/D3).
|
|
4
|
+
*
|
|
5
|
+
* Why an extension rather than the protocol's own variant: ACP 1.4 does define
|
|
6
|
+
* a `compaction_update`, but the Rust crate the packaged Zed links
|
|
7
|
+
* (`agent-client-protocol 2.0.0`) carries no `compaction` symbol at all, so
|
|
8
|
+
* that variant cannot be deserialised no matter what this adapter sends. The
|
|
9
|
+
* reachable pattern is the one downstream patch `0011` already proves: emit a
|
|
10
|
+
* `_meta` key no ACP variant claims, and let a downstream patch read it. The
|
|
11
|
+
* tool call renders with `ToolKind::Think`'s icon even with no patch present,
|
|
12
|
+
* so R2.1 ships and works on its own.
|
|
13
|
+
*
|
|
14
|
+
* Why the version field is not optional (D3): it is what makes the two halves
|
|
15
|
+
* independently releasable in BOTH directions — a newer adapter against an
|
|
16
|
+
* older Zed degrades to generic tool rendering, and an older adapter against a
|
|
17
|
+
* newer Zed simply never sends the key. A payload with no version is a payload
|
|
18
|
+
* a reader cannot degrade from, so it can only be guessed at.
|
|
19
|
+
*
|
|
20
|
+
* Kept self-contained — no import from `acp-agent.ts`, mirroring
|
|
21
|
+
* `thinking-option.ts` and `rewind-command.ts`. The reason is merge cost:
|
|
22
|
+
* `acp-agent.ts` is a ~9,700-line file that changes upstream almost daily, so
|
|
23
|
+
* every coupling to it is paid again at each sync.
|
|
24
|
+
*/
|
|
25
|
+
/** The `_meta` key the downstream Zed patch looks for. */
|
|
26
|
+
export declare const CONTEXT_COMPACTION_META_KEY = "contextCompaction";
|
|
27
|
+
/**
|
|
28
|
+
* Schema version of the payload under {@link CONTEXT_COMPACTION_META_KEY}.
|
|
29
|
+
* Bump it whenever a field's meaning changes; a reader that does not know the
|
|
30
|
+
* value it receives must fall back to generic tool rendering rather than
|
|
31
|
+
* interpret unknown fields.
|
|
32
|
+
*/
|
|
33
|
+
export declare const CONTEXT_COMPACTION_META_VERSION = 1;
|
|
34
|
+
/**
|
|
35
|
+
* Why the compaction ran: `"manual"` for a user-issued `/compact`,
|
|
36
|
+
* `"automatic"` when the SDK compacted on its own to stay inside the window.
|
|
37
|
+
* Provider-neutral by design — the SDK spells the automatic case `"auto"`.
|
|
38
|
+
*/
|
|
39
|
+
export type ContextCompactionTrigger = "manual" | "automatic";
|
|
40
|
+
/**
|
|
41
|
+
* Compaction-specific facts carried alongside the tool call. Every field
|
|
42
|
+
* except `version` is optional because the SDK frame that produced the event
|
|
43
|
+
* may not carry it: a terminal-only `status` message has no token counts, and
|
|
44
|
+
* an older CLI's `compact_boundary` omits `post_tokens`/`duration_ms`.
|
|
45
|
+
*/
|
|
46
|
+
export interface ContextCompactionMetadata {
|
|
47
|
+
/** Always {@link CONTEXT_COMPACTION_META_VERSION}; see D3 above. */
|
|
48
|
+
version: typeof CONTEXT_COMPACTION_META_VERSION;
|
|
49
|
+
/** Manual `/compact` or an automatic window-pressure compaction. */
|
|
50
|
+
trigger?: ContextCompactionTrigger;
|
|
51
|
+
/** Tokens occupying the context window before compaction. */
|
|
52
|
+
preTokens?: number;
|
|
53
|
+
/** Tokens occupying it after — absent on CLIs that don't report it. */
|
|
54
|
+
postTokens?: number;
|
|
55
|
+
/** How long the compaction took, in milliseconds. */
|
|
56
|
+
durationMs?: number;
|
|
57
|
+
/** Failure reason, present only on a `failed` lifecycle. */
|
|
58
|
+
error?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Provider-neutral metadata for a synthetic ACP context-compaction tool call.
|
|
62
|
+
*
|
|
63
|
+
* The standard `toolCallId` and `status` fields own lifecycle identity and
|
|
64
|
+
* phase; this extension deliberately carries only compaction-specific facts,
|
|
65
|
+
* so a client that ignores `_meta` entirely still sees a correct tool
|
|
66
|
+
* lifecycle.
|
|
67
|
+
*
|
|
68
|
+
* @param metadata The facts known at this point in the lifecycle; the version
|
|
69
|
+
* is stamped here rather than by each call site, so no caller can emit an
|
|
70
|
+
* unversioned payload.
|
|
71
|
+
*/
|
|
72
|
+
export declare function createContextCompactionMeta(metadata?: Omit<ContextCompactionMetadata, "version">): Record<typeof CONTEXT_COMPACTION_META_KEY, ContextCompactionMetadata>;
|
|
73
|
+
//# sourceMappingURL=context-compaction-meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compaction-meta.d.ts","sourceRoot":"","sources":["../src/context-compaction-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,0DAA0D;AAC1D,eAAO,MAAM,2BAA2B,sBAAsB,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,oEAAoE;IACpE,OAAO,EAAE,OAAO,+BAA+B,CAAC;IAChD,oEAAoE;IACpE,OAAO,CAAC,EAAE,wBAAwB,CAAC;IACnC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,GAAE,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAM,GACxD,MAAM,CAAC,OAAO,2BAA2B,EAAE,yBAAyB,CAAC,CAOvE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The versioned `_meta` extension that carries context-compaction facts on the
|
|
3
|
+
* synthetic ACP tool call (story 010, R2.1/D3).
|
|
4
|
+
*
|
|
5
|
+
* Why an extension rather than the protocol's own variant: ACP 1.4 does define
|
|
6
|
+
* a `compaction_update`, but the Rust crate the packaged Zed links
|
|
7
|
+
* (`agent-client-protocol 2.0.0`) carries no `compaction` symbol at all, so
|
|
8
|
+
* that variant cannot be deserialised no matter what this adapter sends. The
|
|
9
|
+
* reachable pattern is the one downstream patch `0011` already proves: emit a
|
|
10
|
+
* `_meta` key no ACP variant claims, and let a downstream patch read it. The
|
|
11
|
+
* tool call renders with `ToolKind::Think`'s icon even with no patch present,
|
|
12
|
+
* so R2.1 ships and works on its own.
|
|
13
|
+
*
|
|
14
|
+
* Why the version field is not optional (D3): it is what makes the two halves
|
|
15
|
+
* independently releasable in BOTH directions — a newer adapter against an
|
|
16
|
+
* older Zed degrades to generic tool rendering, and an older adapter against a
|
|
17
|
+
* newer Zed simply never sends the key. A payload with no version is a payload
|
|
18
|
+
* a reader cannot degrade from, so it can only be guessed at.
|
|
19
|
+
*
|
|
20
|
+
* Kept self-contained — no import from `acp-agent.ts`, mirroring
|
|
21
|
+
* `thinking-option.ts` and `rewind-command.ts`. The reason is merge cost:
|
|
22
|
+
* `acp-agent.ts` is a ~9,700-line file that changes upstream almost daily, so
|
|
23
|
+
* every coupling to it is paid again at each sync.
|
|
24
|
+
*/
|
|
25
|
+
/** The `_meta` key the downstream Zed patch looks for. */
|
|
26
|
+
export const CONTEXT_COMPACTION_META_KEY = "contextCompaction";
|
|
27
|
+
/**
|
|
28
|
+
* Schema version of the payload under {@link CONTEXT_COMPACTION_META_KEY}.
|
|
29
|
+
* Bump it whenever a field's meaning changes; a reader that does not know the
|
|
30
|
+
* value it receives must fall back to generic tool rendering rather than
|
|
31
|
+
* interpret unknown fields.
|
|
32
|
+
*/
|
|
33
|
+
export const CONTEXT_COMPACTION_META_VERSION = 1;
|
|
34
|
+
/**
|
|
35
|
+
* Provider-neutral metadata for a synthetic ACP context-compaction tool call.
|
|
36
|
+
*
|
|
37
|
+
* The standard `toolCallId` and `status` fields own lifecycle identity and
|
|
38
|
+
* phase; this extension deliberately carries only compaction-specific facts,
|
|
39
|
+
* so a client that ignores `_meta` entirely still sees a correct tool
|
|
40
|
+
* lifecycle.
|
|
41
|
+
*
|
|
42
|
+
* @param metadata The facts known at this point in the lifecycle; the version
|
|
43
|
+
* is stamped here rather than by each call site, so no caller can emit an
|
|
44
|
+
* unversioned payload.
|
|
45
|
+
*/
|
|
46
|
+
export function createContextCompactionMeta(metadata = {}) {
|
|
47
|
+
return {
|
|
48
|
+
[CONTEXT_COMPACTION_META_KEY]: {
|
|
49
|
+
version: CONTEXT_COMPACTION_META_VERSION,
|
|
50
|
+
...metadata,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context compaction as ONE idempotent ACP tool lifecycle (story 010, R2.1/R2.2).
|
|
3
|
+
*
|
|
4
|
+
* What this replaces: the adapter used to infer compaction from a
|
|
5
|
+
* `compactionInProgress` boolean and narrate it as assistant text
|
|
6
|
+
* ("Compacting…", "Compacting completed."). The flag existed only because, in
|
|
7
|
+
* the adapter's own comment, the SDK's two terminal `status` messages were
|
|
8
|
+
* "indistinguishable" — a guess, not a signal. Once the lifecycle is explicit
|
|
9
|
+
* the guess is not merely redundant, it double-reports: the tool call says the
|
|
10
|
+
* compaction finished and the banner says so again. So the inference is
|
|
11
|
+
* DELETED, not left inert (D4) — a flag that no longer decides anything is a
|
|
12
|
+
* thing the next reader has to prove is inert.
|
|
13
|
+
*
|
|
14
|
+
* What replaces it: a small state machine that folds every event shape the SDK
|
|
15
|
+
* can produce onto a single tool call.
|
|
16
|
+
*
|
|
17
|
+
* SDK event → lifecycle call
|
|
18
|
+
* ------------------------------------------- -----------------------------
|
|
19
|
+
* `status` = "compacting" → start() tool_call, in_progress
|
|
20
|
+
* `stream_event` compaction block/delta → heartbeat() tool_call_update, in_progress
|
|
21
|
+
* `status` with compact_result success/failed → finish() tool_call_update, terminal
|
|
22
|
+
* `compact_boundary` (token counts) → finish(enrich) tool_call_update, metadata only
|
|
23
|
+
* a terminal with no preceding "compacting" → finish() tool_call, terminal (standalone)
|
|
24
|
+
* a DUPLICATED terminal → finish() nothing
|
|
25
|
+
*
|
|
26
|
+
* The two hard cases pull in opposite directions and both are real:
|
|
27
|
+
*
|
|
28
|
+
* - The SDK duplicates the terminal `compact_result` message for a single
|
|
29
|
+
* failed compaction. Two terminals for one compaction must stay ONE report.
|
|
30
|
+
* - One model turn can legitimately compact more than once. Two genuine
|
|
31
|
+
* compactions must stay TWO distinguishable reports.
|
|
32
|
+
*
|
|
33
|
+
* The state machine separates them by phase rather than by identity: a
|
|
34
|
+
* terminal that arrives when the lifecycle has already terminated is a
|
|
35
|
+
* duplicate and changes nothing, while a fresh `compacting` status AFTER a
|
|
36
|
+
* terminal opens a new lifecycle with a new tool call id. A rule that instead
|
|
37
|
+
* suppressed by matching ids or payloads would satisfy one case by breaking
|
|
38
|
+
* the other.
|
|
39
|
+
*
|
|
40
|
+
* State lives until the owning turn's `result` (or an abort) rather than being
|
|
41
|
+
* cleared at each terminal, because the SDK can also omit the opening status
|
|
42
|
+
* on replay — the terminal has to be able to stand alone.
|
|
43
|
+
*
|
|
44
|
+
* Kept self-contained — no import from `acp-agent.ts`, mirroring
|
|
45
|
+
* `thinking-option.ts` and `rewind-command.ts`; what it needs from the adapter
|
|
46
|
+
* (the send chokepoint) is injected, which also leaves the whole state machine
|
|
47
|
+
* unit-testable without a live SDK session. The reason is merge cost:
|
|
48
|
+
* `acp-agent.ts` is a ~9,700-line file that changes upstream almost daily, so
|
|
49
|
+
* every coupling to it is paid again at each sync.
|
|
50
|
+
*/
|
|
51
|
+
import { SessionNotification } from "@agentclientprotocol/sdk";
|
|
52
|
+
import { ContextCompactionMetadata } from "./context-compaction-meta.js";
|
|
53
|
+
/** The phases a compaction can END in. `in_progress` is not terminal. */
|
|
54
|
+
type CompactionStatus = "completed" | "failed";
|
|
55
|
+
/** One compaction's identity and phase. */
|
|
56
|
+
type CompactionState = {
|
|
57
|
+
/** The ACP tool call id every update for this compaction carries. */
|
|
58
|
+
toolCallId: string;
|
|
59
|
+
/** Set once the lifecycle has reported an outcome; the duplicate guard. */
|
|
60
|
+
terminalStatus?: CompactionStatus;
|
|
61
|
+
/** Stream heartbeats are collapsed to one keep-alive per lifecycle. */
|
|
62
|
+
heartbeatSent: boolean;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* The adapter's send chokepoint, injected. Deliberately the ACP SDK's
|
|
66
|
+
* `SessionNotification` rather than an adapter-local alias, so this module
|
|
67
|
+
* needs nothing from `acp-agent.ts`.
|
|
68
|
+
*/
|
|
69
|
+
type SendUpdate = (notification: SessionNotification) => Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Translates Claude's compaction signals into one idempotent ACP tool
|
|
72
|
+
* lifecycle. One instance per consumer; `reset()` at each turn boundary.
|
|
73
|
+
*/
|
|
74
|
+
export declare class ContextCompactionLifecycle {
|
|
75
|
+
private readonly sendUpdate;
|
|
76
|
+
private activeCompaction;
|
|
77
|
+
private outputDelivered;
|
|
78
|
+
private duplicateErrorOutput;
|
|
79
|
+
constructor(sendUpdate: SendUpdate);
|
|
80
|
+
/**
|
|
81
|
+
* Whether this turn already showed the user something about a compaction.
|
|
82
|
+
*
|
|
83
|
+
* The adapter reads it for two decisions the deleted banners used to make
|
|
84
|
+
* implicitly, by counting as delivered assistant text: an echo-less turn
|
|
85
|
+
* that only compacted (e.g. `/compact`) must not have its result text
|
|
86
|
+
* re-emitted by the issue-#453 fallback, and a replayed synthetic
|
|
87
|
+
* local-command frame from an earlier compact attempt must not be forwarded
|
|
88
|
+
* on top of the lifecycle.
|
|
89
|
+
*/
|
|
90
|
+
get hasDeliveredOutput(): boolean;
|
|
91
|
+
/** Return to the unstarted state; call at each turn boundary. */
|
|
92
|
+
reset(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Claude also emits a failed manual compaction's error as local-command
|
|
95
|
+
* stdout. Consume that one duplicate after the tool lifecycle already
|
|
96
|
+
* carried it, without hiding unrelated command output.
|
|
97
|
+
*
|
|
98
|
+
* Matching is by exact (trimmed) text and consumes at most once, so a
|
|
99
|
+
* command that genuinely prints the same string later still reaches the
|
|
100
|
+
* client.
|
|
101
|
+
*
|
|
102
|
+
* @returns `true` when the caller should drop this content.
|
|
103
|
+
*/
|
|
104
|
+
consumeDuplicateErrorOutput(content: string): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Open the tool call the client renders. Idempotent while a compaction is
|
|
107
|
+
* still running (a repeated `compacting` status re-uses the open call); a
|
|
108
|
+
* `compacting` that arrives AFTER a terminal starts a fresh lifecycle, which
|
|
109
|
+
* is how two real compactions in one turn stay two reports.
|
|
110
|
+
*
|
|
111
|
+
* @param toolCallId The SDK message uuid — replay-stable, so a re-delivered
|
|
112
|
+
* frame addresses the same tool call rather than inventing one.
|
|
113
|
+
*/
|
|
114
|
+
start(sessionId: string, toolCallId: string): Promise<CompactionState>;
|
|
115
|
+
/**
|
|
116
|
+
* Keep the open call alive while compaction streams. Sent at most once per
|
|
117
|
+
* lifecycle: the deltas carry the generated summary, which stays internal to
|
|
118
|
+
* the agent, so repeating them would add noise and no information.
|
|
119
|
+
*
|
|
120
|
+
* @param fallbackId Used only when the opening status never arrived (replay).
|
|
121
|
+
*/
|
|
122
|
+
heartbeat(sessionId: string, fallbackId: string): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Report the outcome — exactly once per compaction.
|
|
125
|
+
*
|
|
126
|
+
* Three shapes converge here:
|
|
127
|
+
* - no lifecycle open (the SDK omitted the opening status on replay): emit a
|
|
128
|
+
* standalone terminal `tool_call`, so the event is reported rather than
|
|
129
|
+
* dropped for lack of a start;
|
|
130
|
+
* - the first terminal for an open lifecycle: a `tool_call_update` carrying
|
|
131
|
+
* the status;
|
|
132
|
+
* - a duplicate terminal: nothing at all.
|
|
133
|
+
*
|
|
134
|
+
* @param fallbackId Tool call id for the standalone shape.
|
|
135
|
+
* @param metadata Compaction facts; also emitted as `rawOutput` when non-empty,
|
|
136
|
+
* so a client that ignores `_meta` can still show something.
|
|
137
|
+
* @param enrichTerminal Allow a SECOND update on an already-terminal
|
|
138
|
+
* lifecycle that adds facts without re-reporting the outcome. Used by
|
|
139
|
+
* `compact_boundary`, which arrives after the terminal `status` and is the
|
|
140
|
+
* only frame carrying the token counts. The status field is omitted on that
|
|
141
|
+
* update, so the client still sees exactly one terminal transition.
|
|
142
|
+
*/
|
|
143
|
+
finish(sessionId: string, fallbackId: string, status: CompactionStatus, metadata?: Omit<ContextCompactionMetadata, "version">, enrichTerminal?: boolean): Promise<void>;
|
|
144
|
+
/** Arm {@link consumeDuplicateErrorOutput} for the stdout copy Claude emits. */
|
|
145
|
+
private rememberDuplicateErrorOutput;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Map the SDK's `compact_boundary` metadata onto the provider-neutral names of
|
|
149
|
+
* {@link ContextCompactionMetadata}. Fields an older CLI omits stay omitted
|
|
150
|
+
* rather than becoming `undefined` keys, so `rawOutput` and `_meta` never
|
|
151
|
+
* advertise a number nobody reported.
|
|
152
|
+
*/
|
|
153
|
+
export declare function contextCompactionMetadataFromBoundary(compactMetadata: {
|
|
154
|
+
trigger: "manual" | "auto";
|
|
155
|
+
pre_tokens: number;
|
|
156
|
+
post_tokens?: number;
|
|
157
|
+
duration_ms?: number;
|
|
158
|
+
}): Omit<ContextCompactionMetadata, "version">;
|
|
159
|
+
export {};
|
|
160
|
+
//# sourceMappingURL=context-compaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-compaction.d.ts","sourceRoot":"","sources":["../src/context-compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EACL,yBAAyB,EAE1B,MAAM,8BAA8B,CAAC;AAEtC,yEAAyE;AACzE,KAAK,gBAAgB,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE/C,2CAA2C;AAC3C,KAAK,eAAe,GAAG;IACrB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,uEAAuE;IACvE,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF;;;;GAIG;AACH,KAAK,UAAU,GAAG,CAAC,YAAY,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvE;;;GAGG;AACH,qBAAa,0BAA0B;IAKzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJvC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,oBAAoB,CAAqB;gBAEpB,UAAU,EAAE,UAAU;IAEnD;;;;;;;;;OASG;IACH,IAAI,kBAAkB,IAAI,OAAO,CAEhC;IAED,iEAAiE;IACjE,KAAK,IAAI,IAAI;IAMb;;;;;;;;;;OAUG;IACH,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAWrD;;;;;;;;OAQG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAqB5E;;;;;;OAMG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBrE;;;;;;;;;;;;;;;;;;;OAmBG;IACG,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,gBAAgB,EACxB,QAAQ,GAAE,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAM,EACzD,cAAc,UAAQ,GACrB,OAAO,CAAC,IAAI,CAAC;IAgDhB,gFAAgF;IAChF,OAAO,CAAC,4BAA4B;CAQrC;AAED;;;;;GAKG;AACH,wBAAgB,qCAAqC,CAAC,eAAe,EAAE;IACrE,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAC,CAW7C"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context compaction as ONE idempotent ACP tool lifecycle (story 010, R2.1/R2.2).
|
|
3
|
+
*
|
|
4
|
+
* What this replaces: the adapter used to infer compaction from a
|
|
5
|
+
* `compactionInProgress` boolean and narrate it as assistant text
|
|
6
|
+
* ("Compacting…", "Compacting completed."). The flag existed only because, in
|
|
7
|
+
* the adapter's own comment, the SDK's two terminal `status` messages were
|
|
8
|
+
* "indistinguishable" — a guess, not a signal. Once the lifecycle is explicit
|
|
9
|
+
* the guess is not merely redundant, it double-reports: the tool call says the
|
|
10
|
+
* compaction finished and the banner says so again. So the inference is
|
|
11
|
+
* DELETED, not left inert (D4) — a flag that no longer decides anything is a
|
|
12
|
+
* thing the next reader has to prove is inert.
|
|
13
|
+
*
|
|
14
|
+
* What replaces it: a small state machine that folds every event shape the SDK
|
|
15
|
+
* can produce onto a single tool call.
|
|
16
|
+
*
|
|
17
|
+
* SDK event → lifecycle call
|
|
18
|
+
* ------------------------------------------- -----------------------------
|
|
19
|
+
* `status` = "compacting" → start() tool_call, in_progress
|
|
20
|
+
* `stream_event` compaction block/delta → heartbeat() tool_call_update, in_progress
|
|
21
|
+
* `status` with compact_result success/failed → finish() tool_call_update, terminal
|
|
22
|
+
* `compact_boundary` (token counts) → finish(enrich) tool_call_update, metadata only
|
|
23
|
+
* a terminal with no preceding "compacting" → finish() tool_call, terminal (standalone)
|
|
24
|
+
* a DUPLICATED terminal → finish() nothing
|
|
25
|
+
*
|
|
26
|
+
* The two hard cases pull in opposite directions and both are real:
|
|
27
|
+
*
|
|
28
|
+
* - The SDK duplicates the terminal `compact_result` message for a single
|
|
29
|
+
* failed compaction. Two terminals for one compaction must stay ONE report.
|
|
30
|
+
* - One model turn can legitimately compact more than once. Two genuine
|
|
31
|
+
* compactions must stay TWO distinguishable reports.
|
|
32
|
+
*
|
|
33
|
+
* The state machine separates them by phase rather than by identity: a
|
|
34
|
+
* terminal that arrives when the lifecycle has already terminated is a
|
|
35
|
+
* duplicate and changes nothing, while a fresh `compacting` status AFTER a
|
|
36
|
+
* terminal opens a new lifecycle with a new tool call id. A rule that instead
|
|
37
|
+
* suppressed by matching ids or payloads would satisfy one case by breaking
|
|
38
|
+
* the other.
|
|
39
|
+
*
|
|
40
|
+
* State lives until the owning turn's `result` (or an abort) rather than being
|
|
41
|
+
* cleared at each terminal, because the SDK can also omit the opening status
|
|
42
|
+
* on replay — the terminal has to be able to stand alone.
|
|
43
|
+
*
|
|
44
|
+
* Kept self-contained — no import from `acp-agent.ts`, mirroring
|
|
45
|
+
* `thinking-option.ts` and `rewind-command.ts`; what it needs from the adapter
|
|
46
|
+
* (the send chokepoint) is injected, which also leaves the whole state machine
|
|
47
|
+
* unit-testable without a live SDK session. The reason is merge cost:
|
|
48
|
+
* `acp-agent.ts` is a ~9,700-line file that changes upstream almost daily, so
|
|
49
|
+
* every coupling to it is paid again at each sync.
|
|
50
|
+
*/
|
|
51
|
+
import { createContextCompactionMeta, } from "./context-compaction-meta.js";
|
|
52
|
+
/**
|
|
53
|
+
* Translates Claude's compaction signals into one idempotent ACP tool
|
|
54
|
+
* lifecycle. One instance per consumer; `reset()` at each turn boundary.
|
|
55
|
+
*/
|
|
56
|
+
export class ContextCompactionLifecycle {
|
|
57
|
+
sendUpdate;
|
|
58
|
+
activeCompaction;
|
|
59
|
+
outputDelivered = false;
|
|
60
|
+
duplicateErrorOutput;
|
|
61
|
+
constructor(sendUpdate) {
|
|
62
|
+
this.sendUpdate = sendUpdate;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Whether this turn already showed the user something about a compaction.
|
|
66
|
+
*
|
|
67
|
+
* The adapter reads it for two decisions the deleted banners used to make
|
|
68
|
+
* implicitly, by counting as delivered assistant text: an echo-less turn
|
|
69
|
+
* that only compacted (e.g. `/compact`) must not have its result text
|
|
70
|
+
* re-emitted by the issue-#453 fallback, and a replayed synthetic
|
|
71
|
+
* local-command frame from an earlier compact attempt must not be forwarded
|
|
72
|
+
* on top of the lifecycle.
|
|
73
|
+
*/
|
|
74
|
+
get hasDeliveredOutput() {
|
|
75
|
+
return this.outputDelivered;
|
|
76
|
+
}
|
|
77
|
+
/** Return to the unstarted state; call at each turn boundary. */
|
|
78
|
+
reset() {
|
|
79
|
+
this.activeCompaction = undefined;
|
|
80
|
+
this.outputDelivered = false;
|
|
81
|
+
this.duplicateErrorOutput = undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Claude also emits a failed manual compaction's error as local-command
|
|
85
|
+
* stdout. Consume that one duplicate after the tool lifecycle already
|
|
86
|
+
* carried it, without hiding unrelated command output.
|
|
87
|
+
*
|
|
88
|
+
* Matching is by exact (trimmed) text and consumes at most once, so a
|
|
89
|
+
* command that genuinely prints the same string later still reaches the
|
|
90
|
+
* client.
|
|
91
|
+
*
|
|
92
|
+
* @returns `true` when the caller should drop this content.
|
|
93
|
+
*/
|
|
94
|
+
consumeDuplicateErrorOutput(content) {
|
|
95
|
+
if (this.duplicateErrorOutput === undefined ||
|
|
96
|
+
content.trim() !== this.duplicateErrorOutput.trim()) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
this.duplicateErrorOutput = undefined;
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Open the tool call the client renders. Idempotent while a compaction is
|
|
104
|
+
* still running (a repeated `compacting` status re-uses the open call); a
|
|
105
|
+
* `compacting` that arrives AFTER a terminal starts a fresh lifecycle, which
|
|
106
|
+
* is how two real compactions in one turn stay two reports.
|
|
107
|
+
*
|
|
108
|
+
* @param toolCallId The SDK message uuid — replay-stable, so a re-delivered
|
|
109
|
+
* frame addresses the same tool call rather than inventing one.
|
|
110
|
+
*/
|
|
111
|
+
async start(sessionId, toolCallId) {
|
|
112
|
+
if (this.activeCompaction && !this.activeCompaction.terminalStatus) {
|
|
113
|
+
return this.activeCompaction;
|
|
114
|
+
}
|
|
115
|
+
this.activeCompaction = { toolCallId, heartbeatSent: false };
|
|
116
|
+
this.outputDelivered = true;
|
|
117
|
+
await this.sendUpdate({
|
|
118
|
+
sessionId,
|
|
119
|
+
update: {
|
|
120
|
+
sessionUpdate: "tool_call",
|
|
121
|
+
toolCallId,
|
|
122
|
+
title: COMPACTION_TOOL_TITLE,
|
|
123
|
+
kind: "think",
|
|
124
|
+
status: "in_progress",
|
|
125
|
+
_meta: compactionToolMeta(),
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
return this.activeCompaction;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Keep the open call alive while compaction streams. Sent at most once per
|
|
132
|
+
* lifecycle: the deltas carry the generated summary, which stays internal to
|
|
133
|
+
* the agent, so repeating them would add noise and no information.
|
|
134
|
+
*
|
|
135
|
+
* @param fallbackId Used only when the opening status never arrived (replay).
|
|
136
|
+
*/
|
|
137
|
+
async heartbeat(sessionId, fallbackId) {
|
|
138
|
+
const state = this.activeCompaction ?? (await this.start(sessionId, fallbackId));
|
|
139
|
+
if (state.terminalStatus || state.heartbeatSent)
|
|
140
|
+
return;
|
|
141
|
+
state.heartbeatSent = true;
|
|
142
|
+
await this.sendUpdate({
|
|
143
|
+
sessionId,
|
|
144
|
+
update: {
|
|
145
|
+
sessionUpdate: "tool_call_update",
|
|
146
|
+
toolCallId: state.toolCallId,
|
|
147
|
+
status: "in_progress",
|
|
148
|
+
_meta: compactionToolMeta(),
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Report the outcome — exactly once per compaction.
|
|
154
|
+
*
|
|
155
|
+
* Three shapes converge here:
|
|
156
|
+
* - no lifecycle open (the SDK omitted the opening status on replay): emit a
|
|
157
|
+
* standalone terminal `tool_call`, so the event is reported rather than
|
|
158
|
+
* dropped for lack of a start;
|
|
159
|
+
* - the first terminal for an open lifecycle: a `tool_call_update` carrying
|
|
160
|
+
* the status;
|
|
161
|
+
* - a duplicate terminal: nothing at all.
|
|
162
|
+
*
|
|
163
|
+
* @param fallbackId Tool call id for the standalone shape.
|
|
164
|
+
* @param metadata Compaction facts; also emitted as `rawOutput` when non-empty,
|
|
165
|
+
* so a client that ignores `_meta` can still show something.
|
|
166
|
+
* @param enrichTerminal Allow a SECOND update on an already-terminal
|
|
167
|
+
* lifecycle that adds facts without re-reporting the outcome. Used by
|
|
168
|
+
* `compact_boundary`, which arrives after the terminal `status` and is the
|
|
169
|
+
* only frame carrying the token counts. The status field is omitted on that
|
|
170
|
+
* update, so the client still sees exactly one terminal transition.
|
|
171
|
+
*/
|
|
172
|
+
async finish(sessionId, fallbackId, status, metadata = {}, enrichTerminal = false) {
|
|
173
|
+
const rawOutput = Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
174
|
+
if (!this.activeCompaction) {
|
|
175
|
+
this.activeCompaction = {
|
|
176
|
+
toolCallId: fallbackId,
|
|
177
|
+
heartbeatSent: false,
|
|
178
|
+
terminalStatus: status,
|
|
179
|
+
};
|
|
180
|
+
this.outputDelivered = true;
|
|
181
|
+
this.rememberDuplicateErrorOutput(status, metadata);
|
|
182
|
+
await this.sendUpdate({
|
|
183
|
+
sessionId,
|
|
184
|
+
update: {
|
|
185
|
+
sessionUpdate: "tool_call",
|
|
186
|
+
toolCallId: fallbackId,
|
|
187
|
+
title: COMPACTION_TOOL_TITLE,
|
|
188
|
+
kind: "think",
|
|
189
|
+
status,
|
|
190
|
+
...compactionErrorFields(status, metadata),
|
|
191
|
+
...(rawOutput ? { rawOutput } : {}),
|
|
192
|
+
_meta: compactionToolMeta(metadata),
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const state = this.activeCompaction;
|
|
198
|
+
// The duplicate guard: a terminal on an already-terminal lifecycle is the
|
|
199
|
+
// SDK repeating itself, and must change nothing the client can see.
|
|
200
|
+
if (state.terminalStatus && !enrichTerminal)
|
|
201
|
+
return;
|
|
202
|
+
const firstTerminal = state.terminalStatus === undefined;
|
|
203
|
+
if (firstTerminal)
|
|
204
|
+
state.terminalStatus = status;
|
|
205
|
+
this.rememberDuplicateErrorOutput(status, metadata);
|
|
206
|
+
await this.sendUpdate({
|
|
207
|
+
sessionId,
|
|
208
|
+
update: {
|
|
209
|
+
sessionUpdate: "tool_call_update",
|
|
210
|
+
toolCallId: state.toolCallId,
|
|
211
|
+
...(firstTerminal ? { status } : {}),
|
|
212
|
+
...compactionErrorFields(status, metadata),
|
|
213
|
+
...(rawOutput ? { rawOutput } : {}),
|
|
214
|
+
_meta: compactionToolMeta(metadata),
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/** Arm {@link consumeDuplicateErrorOutput} for the stdout copy Claude emits. */
|
|
219
|
+
rememberDuplicateErrorOutput(status, metadata) {
|
|
220
|
+
if (status === "failed" && metadata.error) {
|
|
221
|
+
this.duplicateErrorOutput = metadata.error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Map the SDK's `compact_boundary` metadata onto the provider-neutral names of
|
|
227
|
+
* {@link ContextCompactionMetadata}. Fields an older CLI omits stay omitted
|
|
228
|
+
* rather than becoming `undefined` keys, so `rawOutput` and `_meta` never
|
|
229
|
+
* advertise a number nobody reported.
|
|
230
|
+
*/
|
|
231
|
+
export function contextCompactionMetadataFromBoundary(compactMetadata) {
|
|
232
|
+
return {
|
|
233
|
+
trigger: compactMetadata.trigger === "auto" ? "automatic" : "manual",
|
|
234
|
+
preTokens: compactMetadata.pre_tokens,
|
|
235
|
+
...(compactMetadata.post_tokens !== undefined
|
|
236
|
+
? { postTokens: compactMetadata.post_tokens }
|
|
237
|
+
: {}),
|
|
238
|
+
...(compactMetadata.duration_ms !== undefined
|
|
239
|
+
? { durationMs: compactMetadata.duration_ms }
|
|
240
|
+
: {}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/** The title every compaction tool call carries; part of the R2.1 contract. */
|
|
244
|
+
const COMPACTION_TOOL_TITLE = "Compact conversation";
|
|
245
|
+
/**
|
|
246
|
+
* The `_meta` payload for a compaction tool update: the versioned extension
|
|
247
|
+
* plus the `claudeCode.toolName` every other tool call in this adapter carries,
|
|
248
|
+
* so a client keying off it treats the synthetic call like any real one.
|
|
249
|
+
*
|
|
250
|
+
* Returns a plain record rather than the adapter's `ToolUpdateMeta` to keep the
|
|
251
|
+
* module free of `acp-agent.ts` imports; the adapter widens `ToolUpdateMeta`
|
|
252
|
+
* with the matching optional field for its own call sites.
|
|
253
|
+
*/
|
|
254
|
+
function compactionToolMeta(metadata = {}) {
|
|
255
|
+
return {
|
|
256
|
+
...createContextCompactionMeta(metadata),
|
|
257
|
+
claudeCode: { toolName: "compact" },
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* The failure reason as renderable tool content — `_meta` is optional for a
|
|
262
|
+
* client, the reason for a failure is not.
|
|
263
|
+
*/
|
|
264
|
+
function compactionErrorFields(status, metadata) {
|
|
265
|
+
if (status !== "failed" || !metadata.error)
|
|
266
|
+
return {};
|
|
267
|
+
return {
|
|
268
|
+
content: [
|
|
269
|
+
{
|
|
270
|
+
type: "content",
|
|
271
|
+
content: { type: "text", text: `Compaction failed: ${metadata.error}` },
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "Ultracode" entry of the Effort picker.
|
|
3
|
+
*
|
|
4
|
+
* **Ultracode is not a sixth effort level, and reading it as one produces the
|
|
5
|
+
* wrong control.** Claude Code's VS Code extension models it as a boolean that
|
|
6
|
+
* turns on two things at once — `xhigh` effort *and* standing dynamic-workflow
|
|
7
|
+
* orchestration — and its own tooltip says so: `"Ultracode - xhigh +
|
|
8
|
+
* workflows"`. Its five real levels are `low` `medium` `high` `xhigh` `max`;
|
|
9
|
+
* Ultracode is drawn as one extra notch past the last of them, styled
|
|
10
|
+
* differently, and selecting any ordinary level clears it.
|
|
11
|
+
*
|
|
12
|
+
* ACP has no compound control to carry that, so the adapter projects it the way
|
|
13
|
+
* the extension's UI reads rather than the way its state is stored: one more
|
|
14
|
+
* entry in the `effort` select. The compound stays here, in one module, instead
|
|
15
|
+
* of being spread across the three places `acp-agent.ts` applies effort.
|
|
16
|
+
*
|
|
17
|
+
* The SDK carries both halves and names the channel: `Settings.ultracode` is
|
|
18
|
+
* *"xhigh effort plus standing dynamic-workflow orchestration. Session-scoped —
|
|
19
|
+
* typically provided via --settings or the apply_flag_settings control
|
|
20
|
+
* request"*. `apply_flag_settings` is already how the effort option is applied,
|
|
21
|
+
* so no new transport is involved.
|
|
22
|
+
*
|
|
23
|
+
* Kept self-contained — no import from `acp-agent.ts` — for the reason
|
|
24
|
+
* `thinking-option.ts` and `rewind-command.ts` state: that file is ~9,900 lines
|
|
25
|
+
* and moves upstream almost daily, so every coupling to it is paid again at
|
|
26
|
+
* each sync.
|
|
27
|
+
*/
|
|
28
|
+
import type { EffortLevel, Settings } from "@anthropic-ai/claude-agent-sdk";
|
|
29
|
+
/** Value of the synthetic "Ultracode" entry in the effort picker. It is a
|
|
30
|
+
* reserved sentinel: no `EffortLevel` is spelled this way, so it cannot
|
|
31
|
+
* collide with a level the SDK reports in `supportedEffortLevels`. */
|
|
32
|
+
export declare const ULTRACODE_OPTION_VALUE = "ultracode";
|
|
33
|
+
/** Display name of that entry, matching the extension's own label. */
|
|
34
|
+
export declare const ULTRACODE_OPTION_NAME = "Ultracode";
|
|
35
|
+
/** The effort level Ultracode implies. Not configurable: the extension hardcodes
|
|
36
|
+
* `xhigh` in `enableUltracode()`, and the SDK's own description of the setting
|
|
37
|
+
* names the same level. A different level here would silently mean something
|
|
38
|
+
* else than it does in the reference client. */
|
|
39
|
+
export declare const ULTRACODE_EFFORT_LEVEL: EffortLevel;
|
|
40
|
+
/** Display state for the Ultracode entry, resolved by the caller. Mirrors the
|
|
41
|
+
* shape `FastModeOptionState` uses for the same job — availability and current
|
|
42
|
+
* value are two different questions, and folding them into one boolean loses
|
|
43
|
+
* the case where the entry should render but not be selected. */
|
|
44
|
+
export interface UltracodeOptionState {
|
|
45
|
+
/** Whether the entry should appear at all. */
|
|
46
|
+
available: boolean;
|
|
47
|
+
/** Whether it is the currently selected effort. */
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether the Ultracode entry may be offered, under the same two conditions the
|
|
52
|
+
* extension checks in its own `ultracodeAvailable`:
|
|
53
|
+
*
|
|
54
|
+
* 1. workflows are not disabled — Ultracode *is* the workflow half, so offering
|
|
55
|
+
* it where `disableWorkflows` is set would advertise something that cannot
|
|
56
|
+
* happen;
|
|
57
|
+
* 2. the current model supports `xhigh` — Ultracode has no meaning on a model
|
|
58
|
+
* that cannot reach the level it implies.
|
|
59
|
+
*
|
|
60
|
+
* Both are read rather than assumed: an unknown settings shape leaves
|
|
61
|
+
* `disableWorkflows` undefined, which is *not* `true`, so the gate opens — the
|
|
62
|
+
* same direction the extension's `=== !0` comparison takes.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isUltracodeAvailable(supportedEffortLevels: readonly string[], settings: Pick<Settings, "disableWorkflows"> | undefined): boolean;
|
|
65
|
+
/** Whether a picker value denotes Ultracode rather than a plain effort level. */
|
|
66
|
+
export declare function isUltracodeValue(value: string | undefined): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* The `apply_flag_settings` payload for an effort selection, Ultracode included.
|
|
69
|
+
*
|
|
70
|
+
* **The `null` is the load-bearing part.** The SDK shallow-merges flag settings
|
|
71
|
+
* and only clears a key when an explicit `null` arrives — `undefined` is dropped
|
|
72
|
+
* by JSON transport and leaves the previous value standing. So every selection
|
|
73
|
+
* has to say something about `ultracode`, not just the one that turns it on:
|
|
74
|
+
* without the clear, picking `High` after Ultracode would lower the effort and
|
|
75
|
+
* leave workflow orchestration running, which is a state no picker entry names.
|
|
76
|
+
* The same reasoning applies to `effortLevel` — this function absorbed the older
|
|
77
|
+
* `toSdkEffortLevel` helper for exactly that reason: both keys are decided in
|
|
78
|
+
* one place, so neither can be forgotten on a path the other takes.
|
|
79
|
+
*
|
|
80
|
+
* @param value the picker value — `"default"`, an `EffortLevel`, or the
|
|
81
|
+
* Ultracode sentinel. `undefined` means the option is absent for this model.
|
|
82
|
+
*/
|
|
83
|
+
export declare function ultracodeFlagSettings(value: string | undefined): {
|
|
84
|
+
effortLevel: EffortLevel | null;
|
|
85
|
+
ultracode: boolean | null;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The picker value to display, given the persisted effort level and whether the
|
|
89
|
+
* Ultracode flag is on.
|
|
90
|
+
*
|
|
91
|
+
* The flag wins over the level, and only when it is *available*: a session that
|
|
92
|
+
* carries `ultracode: true` from settings while the current model cannot reach
|
|
93
|
+
* `xhigh` must not show an entry the picker is not rendering. The extension
|
|
94
|
+
* reaches the same state through `adoptAppliedEffort`, which reads
|
|
95
|
+
* `applied.ultracode` first and falls back to inferring it from `xhigh`; we do
|
|
96
|
+
* not infer, because `xhigh` alone is a legitimate selection that is not
|
|
97
|
+
* Ultracode, and guessing would silently turn workflows on in the label.
|
|
98
|
+
*/
|
|
99
|
+
export declare function effortOptionValue(effortLevel: string | undefined, ultracode: UltracodeOptionState | undefined): string | undefined;
|
|
100
|
+
//# sourceMappingURL=ultracode.d.ts.map
|