@aparte/engine 0.2.0-alpha.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/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/agent/parsers/artifact-xml-state-machine.d.ts +86 -0
- package/dist/agent/parsers/artifact-xml-state-machine.d.ts.map +1 -0
- package/dist/agent/stream-events.contract.d.ts +55 -0
- package/dist/agent/stream-events.contract.d.ts.map +1 -0
- package/dist/agent/stream-events.d.ts +209 -0
- package/dist/agent/stream-events.d.ts.map +1 -0
- package/dist/agent/stream-run.d.ts +59 -0
- package/dist/agent/stream-run.d.ts.map +1 -0
- package/dist/conversation/compactor.d.ts +175 -0
- package/dist/conversation/compactor.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +545 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aparté
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @aparte/engine
|
|
2
|
+
|
|
3
|
+
The **headless agent loop** behind `@aparte/*` — framework-agnostic, zero runtime
|
|
4
|
+
dependencies, runs in the browser or Node.
|
|
5
|
+
|
|
6
|
+
Its core export is **`runStreamAgent`**: a DOM-free structured-stream loop that turns a
|
|
7
|
+
transport's token stream into high-level run events (text, thinking, tool calls, artifacts),
|
|
8
|
+
drives the tool-calling loop (with optional human-in-the-loop approval), and reports usage.
|
|
9
|
+
|
|
10
|
+
`@aparte/core` embeds the same loop inline (`AparteClient._streamLoop`) so **core works
|
|
11
|
+
without this package**. `@aparte/engine` is the *recommended path*: inject `runStreamAgent`
|
|
12
|
+
via `AparteClientOptions.streamRunner` and core renders its events through
|
|
13
|
+
`createStreamAdapter`. Parity between the two is proven by this package's `stream-parity`
|
|
14
|
+
suite (it drives core's real `_streamLoop` and `runStreamAgent` against the same scripted
|
|
15
|
+
transport and asserts identical output).
|
|
16
|
+
|
|
17
|
+
`@aparte/core` is an **optional peer** — `runStreamAgent` and its parsers import nothing from
|
|
18
|
+
it; the orchestrator/memory helpers use core's config/types when present.
|
|
19
|
+
|
|
20
|
+
> Part of the [aparté](https://github.com/apartejs/aparte) monorepo. ESM-only.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* artifact-xml-state-machine.ts — streaming `<artifact>` XML parser (pure).
|
|
3
|
+
*
|
|
4
|
+
* The framework-free extraction of the XML-artifact branch inside
|
|
5
|
+
* `AparteClient._streamLoop` (aparte-client.ts :1268-1392 + finalize :1658-1669).
|
|
6
|
+
* Small models emit artifacts as `…chat text… <artifact mimeType="…" title="…">
|
|
7
|
+
* BODY</artifact> …more text…`, streamed in arbitrary chunks that can split the
|
|
8
|
+
* opening tag, the body, or the closing tag across delta boundaries.
|
|
9
|
+
*
|
|
10
|
+
* This module owns only the PARSING (state + buffering); it emits DOM-free
|
|
11
|
+
* micro-events. The `@aparte/core` adapter turns them into segment/lifecycle calls
|
|
12
|
+
* (addSegment / updateSegment / _dispatchArtifactLifecycle) exactly as
|
|
13
|
+
* `_streamLoop` does — so no DOM, no `@aparte/core` import here.
|
|
14
|
+
*
|
|
15
|
+
* Ported faithfully, including quirks: a `<artifact` split as `<arti`+`fact`
|
|
16
|
+
* (so `indexOf('<artifact')` misses it) is treated as chat text, matching the
|
|
17
|
+
* source. Providers emit the tag atomically in practice.
|
|
18
|
+
*/
|
|
19
|
+
/** Where the streaming parser is between artifacts. */
|
|
20
|
+
export type XmlArtifactState = 'normal' | 'scanning' | 'in-artifact';
|
|
21
|
+
/** Fallback mime/kind for an artifact whose open tag omits the attribute. */
|
|
22
|
+
export interface XmlArtifactHint {
|
|
23
|
+
mimeType: string;
|
|
24
|
+
kind: string;
|
|
25
|
+
}
|
|
26
|
+
/** DOM-free micro-events the machine emits; the adapter renders them. */
|
|
27
|
+
export type XmlArtifactEvent = {
|
|
28
|
+
type: 'chat-text';
|
|
29
|
+
text: string;
|
|
30
|
+
reduced?: boolean;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'artifact-open';
|
|
33
|
+
id: string;
|
|
34
|
+
mimeType: string;
|
|
35
|
+
kind: string;
|
|
36
|
+
title: string;
|
|
37
|
+
} | {
|
|
38
|
+
type: 'artifact-chunk';
|
|
39
|
+
id: string;
|
|
40
|
+
content: string;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'artifact-close';
|
|
43
|
+
id: string;
|
|
44
|
+
content: string;
|
|
45
|
+
inline: boolean;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Map an artifact mimeType to a renderer kind. Byte-identical copy of core's
|
|
49
|
+
* canonical `deriveArtifactKind` (parsers/aparte-stream-parser.ts) — the
|
|
50
|
+
* duplicate stays because `@aparte/core` is an OPTIONAL peer of the engine, so
|
|
51
|
+
* no runtime import is possible. Kept in sync mechanically by
|
|
52
|
+
* `__tests__/derive-artifact-kind-parity.test.ts`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function deriveArtifactKind(mimeType: string, fallback?: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* A stateful streaming parser for one turn's `<artifact>` blocks. Feed it each
|
|
57
|
+
* text delta; drain the returned events. Call {@link finalize} when the stream
|
|
58
|
+
* ends to flush a truncated (unclosed) artifact.
|
|
59
|
+
*/
|
|
60
|
+
export declare class ArtifactXmlStateMachine {
|
|
61
|
+
private readonly hint;
|
|
62
|
+
private state;
|
|
63
|
+
/** Buffers the opening tag until its `>` arrives (may span deltas). */
|
|
64
|
+
private scanBuf;
|
|
65
|
+
/** Buffers the tail that might be the start of a split `</artifact>`. */
|
|
66
|
+
private closeBuf;
|
|
67
|
+
private segId;
|
|
68
|
+
private content;
|
|
69
|
+
private mime;
|
|
70
|
+
private kind;
|
|
71
|
+
private title;
|
|
72
|
+
private seq;
|
|
73
|
+
private readonly idGen;
|
|
74
|
+
constructor(hint: XmlArtifactHint, idGen?: () => string);
|
|
75
|
+
/** Feed one text delta; returns the ordered micro-events it produced. */
|
|
76
|
+
feed(delta: string): XmlArtifactEvent[];
|
|
77
|
+
/**
|
|
78
|
+
* Flush a truncated artifact: if the stream ended mid-body (model cut off
|
|
79
|
+
* before `</artifact>`), emit a close with whatever was buffered. Mirrors
|
|
80
|
+
* `_streamLoop`'s finalize block (:1658-1669).
|
|
81
|
+
*/
|
|
82
|
+
finalize(): XmlArtifactEvent[];
|
|
83
|
+
/** Current parser state (for the adapter to decide finalize routing). */
|
|
84
|
+
get currentState(): XmlArtifactState;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=artifact-xml-state-machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-xml-state-machine.d.ts","sourceRoot":"","sources":["../../../src/agent/parsers/artifact-xml-state-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC;AAErE,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAMtB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpF;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAO/E;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,SAAY,GAAG,MAAM,CAqBjF;AAED;;;;GAIG;AACH,qBAAa,uBAAuB;IAcpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAbjC,OAAO,CAAC,KAAK,CAA8B;IAC3C,uEAAuE;IACvE,OAAO,CAAC,OAAO,CAAM;IACrB,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAER,IAAI,EAAE,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,MAAM;IAIxE,yEAAyE;IACzE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE;IA4DvC;;;;OAIG;IACH,QAAQ,IAAI,gBAAgB,EAAE;IAY9B,yEAAyE;IACzE,IAAI,YAAY,IAAI,gBAAgB,CAEnC;CACJ"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stream-events.contract.ts — COMPILE-TIME guard for the core↔engine run-event mirror.
|
|
3
|
+
*
|
|
4
|
+
* `StreamRunEvent` (this package) and `AparteStreamRunEvent` (@aparte/core's adapter)
|
|
5
|
+
* are hand-mirrored across the zero-import boundary: core is the zero-dep leaf and must
|
|
6
|
+
* never import engine, and engine keeps its stream contract standalone (see
|
|
7
|
+
* `stream-events.ts`). That leaves the two unions synced BY HAND — the seam's one
|
|
8
|
+
* unguarded soft spot. A silent drift here (a renamed/added variant, a changed payload
|
|
9
|
+
* field) would corrupt streaming with no CI signal.
|
|
10
|
+
*
|
|
11
|
+
* This file makes that drift a TYPECHECK ERROR. It ships nothing: it only declares
|
|
12
|
+
* type aliases (erased) and `import type`s core (erased → no runtime dep, so the
|
|
13
|
+
* runtime zero-import rule still holds; core is present as engine's dev/peer dep at
|
|
14
|
+
* typecheck time). It is not imported by the barrel, so it stays out of the bundle.
|
|
15
|
+
*
|
|
16
|
+
* The ONE intentional difference is `run-done.usage`: core carries the rich
|
|
17
|
+
* `AparteUsage` (named provider-timing fields — ttft/decode/phases/…), engine an
|
|
18
|
+
* opaque `StreamUsage` passthrough (five common fields + an index signature). We
|
|
19
|
+
* normalize that single field to compare the rest of the contract for EXACT equality,
|
|
20
|
+
* and separately assert the usage stays forwardable engine→core (the seam direction:
|
|
21
|
+
* core's adapter consumes engine's `run-done` and forwards its usage to `setUsage`).
|
|
22
|
+
*/
|
|
23
|
+
import type { AparteStreamRunEvent, AparteStreamRunEmitter } from '@aparte/core';
|
|
24
|
+
import type { StreamRunEvent, StreamRunEmitter } from './stream-events.js';
|
|
25
|
+
/** Invariant type-equality — distinguishes optional vs required and index signatures. */
|
|
26
|
+
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;
|
|
27
|
+
/** Compiles only when its argument is exactly `true`. */
|
|
28
|
+
type Expect<T extends true> = T;
|
|
29
|
+
/** Distributes over a union `A`: `true` iff every member is assignable to `B`. */
|
|
30
|
+
type Assignable<A, B> = A extends B ? true : false;
|
|
31
|
+
/** Erase the sole intentional difference (run-done.usage) before the equality check. */
|
|
32
|
+
type NormalizeRunDone<E> = E extends {
|
|
33
|
+
type: 'run-done';
|
|
34
|
+
} ? {
|
|
35
|
+
type: 'run-done';
|
|
36
|
+
usage?: unknown;
|
|
37
|
+
} : E;
|
|
38
|
+
/**
|
|
39
|
+
* Each element compiles only if its contract holds; a drift turns one into a
|
|
40
|
+
* `false`, which fails `Expect<...>` and breaks the typecheck. Exported so it isn't
|
|
41
|
+
* flagged as unused — this file is not re-exported by the barrel, so it never reaches
|
|
42
|
+
* `@aparte/engine`'s public surface.
|
|
43
|
+
*
|
|
44
|
+
* 1. Every variant except `run-done.usage` is structurally identical (exact equality).
|
|
45
|
+
* 2. The `run-done` usage stays forwardable engine→core (lets core's adapter treat it
|
|
46
|
+
* as `AparteUsage`).
|
|
47
|
+
* 3. The emitter core injects into `runStreamAgent` satisfies engine's emitter contract.
|
|
48
|
+
*/
|
|
49
|
+
export type StreamEventContract = [
|
|
50
|
+
Expect<Equal<NormalizeRunDone<StreamRunEvent>, NormalizeRunDone<AparteStreamRunEvent>>>,
|
|
51
|
+
Expect<Assignable<StreamRunEvent, AparteStreamRunEvent>>,
|
|
52
|
+
Expect<Assignable<AparteStreamRunEmitter, StreamRunEmitter>>
|
|
53
|
+
];
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=stream-events.contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-events.contract.d.ts","sourceRoot":"","sources":["../../src/agent/stream-events.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3E,yFAAyF;AACzF,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC,IACX,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AACzF,yDAAyD;AACzD,KAAK,MAAM,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;AAChC,kFAAkF;AAClF,KAAK,UAAU,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AAEnD,wFAAwF;AACxF,KAAK,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,CAAC,CAAC;AAEtG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACvF,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,oBAAoB,CAAC,CAAC;IACxD,MAAM,CAAC,UAAU,CAAC,sBAAsB,EAAE,gBAAgB,CAAC,CAAC;CAC/D,CAAC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stream-events.ts — contract for the framework-free structured-stream agent loop.
|
|
3
|
+
*
|
|
4
|
+
* `runStreamAgent` ({@link ./stream-run}) is the cloud-structured sibling of
|
|
5
|
+
* `runAgent` ({@link ./agent-loop}): where `runAgent` drives a text-only
|
|
6
|
+
* provider (`chat → string`, tool calls re-parsed from text), `runStreamAgent`
|
|
7
|
+
* consumes a **structured** `AsyncIterable<StreamChatEvent>` (text / thinking /
|
|
8
|
+
* tool_use / done / error) — the exact stream `AparteClient._streamLoop` reads
|
|
9
|
+
* today. It is the extraction target for that 700-line DOM-coupled loop.
|
|
10
|
+
*
|
|
11
|
+
* DOM stays out of here. The loop emits high-level {@link StreamRunEvent}s in the
|
|
12
|
+
* exact order `_streamLoop` performs its `targetElement.*` calls; a thin adapter
|
|
13
|
+
* (in `@aparte/core`, where the parser and renderers live) translates each event
|
|
14
|
+
* into the imperative viewport surface. So this module — and {@link ./stream-run}
|
|
15
|
+
* — import **nothing** from `@aparte/core`: the types below structurally mirror the
|
|
16
|
+
* core types (`AparteStreamEvent`, `AparteUsage`, `AparteChatMessage`, `AparteToolCall`)
|
|
17
|
+
* so the adapter passes the real objects through with zero runtime conversion.
|
|
18
|
+
*
|
|
19
|
+
* SCOPE: text · thinking · tool_use (+ HITL approval) · done · error · artifacts
|
|
20
|
+
* (raw / XML state machine / create_artifact) · multi-phase pipeline · synthetic
|
|
21
|
+
* toolChoice bypass. Code-fence promotion is the only `_streamLoop` mechanism
|
|
22
|
+
* left out here — it is adapter-side (it needs the core parser).
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Token usage. Structurally a superset-compatible mirror of `AparteUsage`: the
|
|
26
|
+
* five common fields plus an index signature that carries the provider-specific
|
|
27
|
+
* rest (ttft/decode/phases/…) opaquely — the loop transports usage, never reads
|
|
28
|
+
* past these five.
|
|
29
|
+
*/
|
|
30
|
+
export interface StreamUsage {
|
|
31
|
+
inputTokens: number;
|
|
32
|
+
outputTokens: number;
|
|
33
|
+
totalTokens?: number;
|
|
34
|
+
cacheReadTokens?: number;
|
|
35
|
+
durationMs?: number;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
/** One tool call as surfaced by the provider stream (mirrors `AparteToolCall`). */
|
|
39
|
+
export interface StreamToolCall {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
input: unknown;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A structured stream event from the transport (mirrors `AparteStreamEvent`). The
|
|
46
|
+
* `tool_use` variant spreads {@link StreamToolCall} exactly like core's does.
|
|
47
|
+
*/
|
|
48
|
+
export type StreamChatEvent = {
|
|
49
|
+
type: 'text';
|
|
50
|
+
delta: string;
|
|
51
|
+
} | {
|
|
52
|
+
type: 'thinking';
|
|
53
|
+
delta: string;
|
|
54
|
+
} | {
|
|
55
|
+
type: 'tool_use';
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
input: unknown;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'error';
|
|
61
|
+
message: string;
|
|
62
|
+
} | {
|
|
63
|
+
type: 'done';
|
|
64
|
+
usage?: StreamUsage;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* A conversation message (mirrors `AparteChatMessage`). `role` is left open
|
|
68
|
+
* (`string`) so the loop can push the `'tool_call'` / `'tool_result'` envelope
|
|
69
|
+
* roles `_streamLoop` uses without importing core's union.
|
|
70
|
+
*/
|
|
71
|
+
export interface StreamAgentMessage {
|
|
72
|
+
role: string;
|
|
73
|
+
content: string;
|
|
74
|
+
/** Present on a `'tool_call'` envelope — the whole turn's calls, grouped. */
|
|
75
|
+
toolCalls?: StreamToolCall[];
|
|
76
|
+
/** Present on a `'tool_result'` message — which call it answers. */
|
|
77
|
+
toolCallId?: string;
|
|
78
|
+
/** Assistant text that preceded the tool call(s) this turn. */
|
|
79
|
+
precedingText?: string;
|
|
80
|
+
[key: string]: unknown;
|
|
81
|
+
}
|
|
82
|
+
/** The request handed to the transport each turn (mirrors `AparteChatRequest`). */
|
|
83
|
+
export interface StreamChatRequest {
|
|
84
|
+
messages: StreamAgentMessage[];
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
}
|
|
87
|
+
/** A tool handler (mirrors the resolved `AparteToolHandler`). */
|
|
88
|
+
export type StreamToolHandler = (call: StreamToolCall, signal: AbortSignal) => Promise<{
|
|
89
|
+
content: string;
|
|
90
|
+
}>;
|
|
91
|
+
/** Per-tool loop configuration (mirrors the `AparteTool` subset the loop reads). */
|
|
92
|
+
export interface StreamToolConfig {
|
|
93
|
+
maxTurns?: number;
|
|
94
|
+
needsApproval?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Resolves a human-in-the-loop approval (mirrors core's
|
|
98
|
+
* `AparteToolApprovalResolver`). Injected so the loop stays headless.
|
|
99
|
+
*/
|
|
100
|
+
export type StreamApprovalResolver = (toolCallId: string, signal: AbortSignal) => Promise<{
|
|
101
|
+
approved: boolean;
|
|
102
|
+
payload?: unknown;
|
|
103
|
+
}>;
|
|
104
|
+
/**
|
|
105
|
+
* High-level, DOM-free events isomorphic to `_streamLoop`'s `targetElement.*`
|
|
106
|
+
* call sequence. Emitted **synchronously and in order** (see {@link StreamRunEmitter})
|
|
107
|
+
* so the adapter reproduces the exact streaming update order.
|
|
108
|
+
*
|
|
109
|
+
* Mapping to `_streamLoop` (aparte-client.ts) for the adapter:
|
|
110
|
+
* - `run-start` → updateMessage(status:'streaming') once at loop entry (the leading write before turn 1)
|
|
111
|
+
* - `turn-start` → reset the per-turn parser / thinking / streaming-segment state (no DOM); one per turn
|
|
112
|
+
* - `text-delta` → parser-driven addSegment/updateSegment, else typeName/updateLastMessage
|
|
113
|
+
* - `text-flush` → textParser.finalize() then addSegment/updateSegment the finalized segments;
|
|
114
|
+
* one per turn, after the inner SSE loop ends (surfaced by the spike — a turn-boundary flush)
|
|
115
|
+
* - `thinking-delta` → addSegment('thinking') then updateSegment(content); first `text-delta` after
|
|
116
|
+
* thinking collapses it (updateSegment collapsed:true)
|
|
117
|
+
* - `tool-start` → renderer lookup + per-tool-name CSS inject into document.head + addSegment
|
|
118
|
+
* - `tool-awaiting-approval` → updateSegment('awaiting-approval') + dispatch `aparte-tool-approval-request`
|
|
119
|
+
* - `tool-approved` → updateSegment('pending')
|
|
120
|
+
* - `tool-rejected` → updateSegment('rejected', result)
|
|
121
|
+
* - `tool-resolved` → updateSegment('resolved', result)
|
|
122
|
+
* - `tool-aborted` → updateSegment('aborted') (no-handler path, timeout/abort path, or per-tool maxTurns path)
|
|
123
|
+
* - `turn-limit-exceeded` scope:'global' → addSegment(error 'MAX_TURNS_EXCEEDED');
|
|
124
|
+
* scope:'tool' → updateSegment('aborted')
|
|
125
|
+
* - `phase-advance` → addSegment({type:'pipeline-waiting'}); the loop has already
|
|
126
|
+
* pushed the phase's reply into history and bumped the phase index
|
|
127
|
+
* - `run-aborted` → dispatch `aparte-message-aborted` (from the inner-loop abort check or the outer turn-boundary abort check)
|
|
128
|
+
* - `run-done` → updateMessage(status:'completed') always + setUsage if usage
|
|
129
|
+
*/
|
|
130
|
+
export type StreamRunEvent = {
|
|
131
|
+
type: 'run-start';
|
|
132
|
+
} | {
|
|
133
|
+
type: 'turn-start';
|
|
134
|
+
} | {
|
|
135
|
+
type: 'text-delta';
|
|
136
|
+
delta: string;
|
|
137
|
+
reduced?: boolean;
|
|
138
|
+
} | {
|
|
139
|
+
type: 'text-flush';
|
|
140
|
+
} | {
|
|
141
|
+
type: 'thinking-delta';
|
|
142
|
+
delta: string;
|
|
143
|
+
} | {
|
|
144
|
+
type: 'artifact-open';
|
|
145
|
+
id: string;
|
|
146
|
+
mimeType: string;
|
|
147
|
+
kind: string;
|
|
148
|
+
title: string;
|
|
149
|
+
} | {
|
|
150
|
+
type: 'artifact-chunk';
|
|
151
|
+
id: string;
|
|
152
|
+
content: string;
|
|
153
|
+
} | {
|
|
154
|
+
type: 'artifact-close';
|
|
155
|
+
id: string;
|
|
156
|
+
content: string;
|
|
157
|
+
inline: boolean;
|
|
158
|
+
} | {
|
|
159
|
+
type: 'artifact-ready';
|
|
160
|
+
id: string;
|
|
161
|
+
mimeType: string;
|
|
162
|
+
kind: string;
|
|
163
|
+
title: string;
|
|
164
|
+
content: string;
|
|
165
|
+
} | {
|
|
166
|
+
type: 'tool-start';
|
|
167
|
+
toolCallId: string;
|
|
168
|
+
name: string;
|
|
169
|
+
input: unknown;
|
|
170
|
+
} | {
|
|
171
|
+
type: 'tool-awaiting-approval';
|
|
172
|
+
toolCallId: string;
|
|
173
|
+
name: string;
|
|
174
|
+
input: unknown;
|
|
175
|
+
} | {
|
|
176
|
+
type: 'tool-approved';
|
|
177
|
+
toolCallId: string;
|
|
178
|
+
} | {
|
|
179
|
+
type: 'tool-rejected';
|
|
180
|
+
toolCallId: string;
|
|
181
|
+
reason: string;
|
|
182
|
+
} | {
|
|
183
|
+
type: 'tool-resolved';
|
|
184
|
+
toolCallId: string;
|
|
185
|
+
result: string;
|
|
186
|
+
} | {
|
|
187
|
+
type: 'tool-aborted';
|
|
188
|
+
toolCallId: string;
|
|
189
|
+
} | {
|
|
190
|
+
type: 'turn-limit-exceeded';
|
|
191
|
+
scope: 'global' | 'tool';
|
|
192
|
+
limit: number;
|
|
193
|
+
toolCallId?: string;
|
|
194
|
+
} | {
|
|
195
|
+
type: 'phase-advance';
|
|
196
|
+
index: number;
|
|
197
|
+
} | {
|
|
198
|
+
type: 'run-aborted';
|
|
199
|
+
} | {
|
|
200
|
+
type: 'run-done';
|
|
201
|
+
usage?: StreamUsage;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Synchronous event sink — mirrors `AGUIEmitter`. Synchronous by contract: the
|
|
205
|
+
* loop must never yield between emitting an event and its ordered successor, or
|
|
206
|
+
* the adapter's streaming updates would interleave out of order.
|
|
207
|
+
*/
|
|
208
|
+
export type StreamRunEmitter = (event: StreamRunEvent) => void;
|
|
209
|
+
//# sourceMappingURL=stream-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-events.d.ts","sourceRoot":"","sources":["../../src/agent/stream-events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stream-run.ts — framework-free structured-stream agent loop.
|
|
3
|
+
*
|
|
4
|
+
* The headless extraction of `AparteClient._streamLoop`: a `while(tool_use)` loop
|
|
5
|
+
* that consumes a structured `AsyncIterable<StreamChatEvent>` from a transport,
|
|
6
|
+
* runs approved tools, feeds their results back into the history, and re-calls
|
|
7
|
+
* the transport until the model stops asking for tools. It performs **no DOM
|
|
8
|
+
* work** — it emits {@link StreamRunEvent}s (in `_streamLoop`'s exact order) that
|
|
9
|
+
* an adapter in `@aparte/core` turns into `targetElement.*` calls.
|
|
10
|
+
*
|
|
11
|
+
* Parity target: `@aparte/core`'s `AparteClient._streamLoop`.
|
|
12
|
+
* Scope: text · thinking · tool_use (+ HITL) · done · error · artifacts (raw /
|
|
13
|
+
* XML / create_artifact) · multi-phase pipeline · synthetic toolChoice bypass.
|
|
14
|
+
* Code-fence promotion stays adapter-side (it needs the core parser).
|
|
15
|
+
*/
|
|
16
|
+
import type { StreamRunEmitter, StreamChatEvent, StreamChatRequest, StreamToolHandler, StreamToolConfig, StreamApprovalResolver, StreamUsage } from './stream-events.js';
|
|
17
|
+
export interface StreamRunOptions {
|
|
18
|
+
/** Id of the assistant message being streamed (opaque; carried in events). */
|
|
19
|
+
messageId: string;
|
|
20
|
+
/** Turn-1 request; the loop clones its `messages` and enriches them per turn. */
|
|
21
|
+
baseRequest: StreamChatRequest;
|
|
22
|
+
/**
|
|
23
|
+
* Calls the transport with the (possibly enriched) request. Returns the
|
|
24
|
+
* structured stream, or a plain string for a non-streaming provider. Mirrors
|
|
25
|
+
* `getTransport().chat(provider, request, auth, ctx)` with provider/auth/ctx
|
|
26
|
+
* closed over by the adapter.
|
|
27
|
+
*/
|
|
28
|
+
transportCall: (request: StreamChatRequest) => Promise<AsyncIterable<StreamChatEvent> | string>;
|
|
29
|
+
/** Resolves a tool's handler by name (mirrors `AparteConfig.getToolHandler`). */
|
|
30
|
+
toolLookup: (name: string) => StreamToolHandler | undefined;
|
|
31
|
+
/** Resolves a tool's loop config by name (maxTurns / needsApproval). */
|
|
32
|
+
toolConfigLookup?: (name: string) => StreamToolConfig | undefined;
|
|
33
|
+
/** HITL approval resolver for `needsApproval` tools (default: never called). */
|
|
34
|
+
approvalResolver?: StreamApprovalResolver;
|
|
35
|
+
/** Synchronous, ordered event sink consumed by the adapter. */
|
|
36
|
+
emitter: StreamRunEmitter;
|
|
37
|
+
/** Single abort signal composing `_isAborted` + the stream controller. */
|
|
38
|
+
signal: AbortSignal;
|
|
39
|
+
/** Global turn cap. @default 10 */
|
|
40
|
+
maxTurns?: number;
|
|
41
|
+
/** Per-tool-call handler timeout in ms. @default 300000 */
|
|
42
|
+
toolTimeoutMs?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Generates artifact segment ids (`prefix` is e.g. `'artifact-raw'`). The
|
|
45
|
+
* default is a deterministic per-run counter; the adapter injects a
|
|
46
|
+
* crypto-based one to match `_streamLoop`'s `artifact-*-<uuid>`. (Tool ids
|
|
47
|
+
* still flow from the stream; only artifacts need generated ids.)
|
|
48
|
+
*/
|
|
49
|
+
idGen?: (prefix: string) => string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Run the structured-stream agent loop. Resolves the last turn's usage (the
|
|
53
|
+
* `done{usage}` last-write-wins, mirroring `_streamLoop`'s return), or
|
|
54
|
+
* `undefined`. Throws on a stream `error` event or a non-abort tool failure —
|
|
55
|
+
* the caller (adapter) routes that to its lifecycle-error handler, exactly as
|
|
56
|
+
* `_handleSend`/`_handleRetry`/`_handleEdit` catch `_streamLoop`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function runStreamAgent(opts: StreamRunOptions): Promise<StreamUsage | undefined>;
|
|
59
|
+
//# sourceMappingURL=stream-run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-run.d.ts","sourceRoot":"","sources":["../../src/agent/stream-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACR,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAGjB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACd,MAAM,oBAAoB,CAAC;AAmB5B,MAAM,WAAW,gBAAgB;IAC7B,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,WAAW,EAAE,iBAAiB,CAAC;IAC/B;;;;;OAKG;IACH,aAAa,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,CAAC;IAChG,iFAAiF;IACjF,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,GAAG,SAAS,CAAC;IAC5D,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,gBAAgB,GAAG,SAAS,CAAC;IAClE,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,EAAE,gBAAgB,CAAC;IAC1B,0EAA0E;IAC1E,MAAM,EAAE,WAAW,CAAC;IACpB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAmU7F"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* conversation/compactor.ts — adaptive budgeting + context-window assembly.
|
|
3
|
+
*
|
|
4
|
+
* Conversation history budgeting and sliding-window assembly —
|
|
5
|
+
* framework-free and tokenizer-free (char-count heuristic).
|
|
6
|
+
*
|
|
7
|
+
* Pattern (cf. Claude Code /context, OpenAI Agents SDK TrimmingSession):
|
|
8
|
+
* Budget = CONTEXT_WINDOW − systemPrompt − tools − reservedThinking
|
|
9
|
+
* − reservedGeneration − autocompactBuffer − safetyMargin
|
|
10
|
+
*
|
|
11
|
+
* The history budget is then split into:
|
|
12
|
+
* - summary (global summary, LLM-generated, ~10%)
|
|
13
|
+
* - ragHist (older turns retrieved by cosine similarity, ~25%)
|
|
14
|
+
* - window (verbatim sliding window, the rest)
|
|
15
|
+
*
|
|
16
|
+
* Drop priority on overflow:
|
|
17
|
+
* 1. summary (async-regenerable)
|
|
18
|
+
* 2. ragHist (retrievable on the next turn)
|
|
19
|
+
* 3. window oldest turns
|
|
20
|
+
* X. NEVER dropped: currentUser + lastAssistant (absolute working memory)
|
|
21
|
+
*
|
|
22
|
+
* Browser-portable: zero deps, just an estimateTokens heuristic.
|
|
23
|
+
*/
|
|
24
|
+
export interface CompactionMessage {
|
|
25
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
26
|
+
content: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RetrievedTurn {
|
|
29
|
+
role: 'user' | 'assistant';
|
|
30
|
+
content: string;
|
|
31
|
+
score?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CompactionConfig {
|
|
34
|
+
/** Total context window of the active model, in tokens. */
|
|
35
|
+
contextWindow: number;
|
|
36
|
+
/** Reserved budget for the model's thinking/reasoning block, in tokens. */
|
|
37
|
+
reservedThinking: number;
|
|
38
|
+
/** Reserved budget for the assistant response (max_new_tokens cap). */
|
|
39
|
+
reservedGeneration: number;
|
|
40
|
+
/** Fraction of context_window kept as autocompact buffer (0..1). */
|
|
41
|
+
autocompactBufferPct: number;
|
|
42
|
+
/** Hard safety margin in tokens. */
|
|
43
|
+
safetyMargin: number;
|
|
44
|
+
/** Floor for history budget — never compact below this. */
|
|
45
|
+
minHistoryBudget: number;
|
|
46
|
+
/** Ratio of history budget allocated to the summary block. */
|
|
47
|
+
summaryRatio: number;
|
|
48
|
+
/** Hard cap for summary tokens. */
|
|
49
|
+
summaryMaxTokens: number;
|
|
50
|
+
/** Ratio of history budget allocated to the RAG-retrieved old turns. */
|
|
51
|
+
ragHistRatio: number;
|
|
52
|
+
/** Hard cap for ragHist tokens. */
|
|
53
|
+
ragHistMaxTokens: number;
|
|
54
|
+
/** Trigger summarization once history usage reaches this % of the budget. */
|
|
55
|
+
triggerSummaryThresholdPct: number;
|
|
56
|
+
/** Re-run summarization every N user turns. */
|
|
57
|
+
summarizeEveryNTurns: number;
|
|
58
|
+
/** Header prepended to the summary block injected into the model context. English
|
|
59
|
+
* by default — override to localise (the compactor ships no locale system). */
|
|
60
|
+
summaryLabel: string;
|
|
61
|
+
/** Header prepended to the RAG-retrieved old turns injected into the context. */
|
|
62
|
+
ragIntroLabel: string;
|
|
63
|
+
}
|
|
64
|
+
export interface BudgetBreakdown {
|
|
65
|
+
contextWindow: number;
|
|
66
|
+
systemPrompt: number;
|
|
67
|
+
tools: number;
|
|
68
|
+
reservedThinking: number;
|
|
69
|
+
reservedGeneration: number;
|
|
70
|
+
autocompactBuffer: number;
|
|
71
|
+
safetyMargin: number;
|
|
72
|
+
historyAvailable: number;
|
|
73
|
+
}
|
|
74
|
+
export interface BudgetResult {
|
|
75
|
+
historyBudget: number;
|
|
76
|
+
breakdown: BudgetBreakdown;
|
|
77
|
+
config: CompactionConfig;
|
|
78
|
+
}
|
|
79
|
+
export interface SplitBudget {
|
|
80
|
+
summary: number;
|
|
81
|
+
ragHist: number;
|
|
82
|
+
window: number;
|
|
83
|
+
}
|
|
84
|
+
export interface UsageBreakdown {
|
|
85
|
+
system: number;
|
|
86
|
+
summary: number;
|
|
87
|
+
ragHist: number;
|
|
88
|
+
window: number;
|
|
89
|
+
}
|
|
90
|
+
export interface DroppedBreakdown {
|
|
91
|
+
ragHits: number;
|
|
92
|
+
oldTurns: number;
|
|
93
|
+
summary: boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface FullBreakdown extends BudgetBreakdown {
|
|
96
|
+
historyAllocated: SplitBudget;
|
|
97
|
+
historyUsed: UsageBreakdown;
|
|
98
|
+
totalUsed: number;
|
|
99
|
+
/** Free tokens left in the context window after all reservations + actual use. */
|
|
100
|
+
free: number;
|
|
101
|
+
dropped: DroppedBreakdown;
|
|
102
|
+
}
|
|
103
|
+
export interface CompactionInput {
|
|
104
|
+
messages: CompactionMessage[];
|
|
105
|
+
systemPrompt: string;
|
|
106
|
+
/** Tools array passed to apply_chat_template (or null/undefined when no tools). */
|
|
107
|
+
toolsArray?: unknown;
|
|
108
|
+
/** Running summary text (regenerable). */
|
|
109
|
+
summary?: string;
|
|
110
|
+
/** RAG-retrieved old turns (older than the sliding window). */
|
|
111
|
+
retrievedTurns?: RetrievedTurn[];
|
|
112
|
+
/** Partial override of the default config. */
|
|
113
|
+
config?: Partial<CompactionConfig>;
|
|
114
|
+
}
|
|
115
|
+
export interface CompactionResult {
|
|
116
|
+
compactedMessages: CompactionMessage[];
|
|
117
|
+
breakdown: FullBreakdown;
|
|
118
|
+
}
|
|
119
|
+
export declare const DEFAULT_COMPACTION_CONFIG: CompactionConfig;
|
|
120
|
+
/**
|
|
121
|
+
* Token heuristic (FR ~3.5 chars/tok, EN ~4 chars/tok).
|
|
122
|
+
* Accurate to ±10% — enough for budgeting, and it avoids `tokenizer.encode()`,
|
|
123
|
+
* which costs ~5ms per message × N (too slow to run on every turn).
|
|
124
|
+
*/
|
|
125
|
+
export declare function estimateTokens(text: string | null | undefined): number;
|
|
126
|
+
/**
|
|
127
|
+
* Estimate tokens for a JSON-serializable structure (e.g. tools array).
|
|
128
|
+
*/
|
|
129
|
+
export declare function estimateTokensJson(obj: unknown): number;
|
|
130
|
+
/**
|
|
131
|
+
* Compute the available history budget after subtracting fixed costs.
|
|
132
|
+
*/
|
|
133
|
+
export declare function computeHistoryBudget(input: {
|
|
134
|
+
systemPrompt: string;
|
|
135
|
+
toolsArray?: unknown;
|
|
136
|
+
config?: Partial<CompactionConfig>;
|
|
137
|
+
}): BudgetResult;
|
|
138
|
+
/**
|
|
139
|
+
* Split history budget into summary / ragHist / window slots.
|
|
140
|
+
*/
|
|
141
|
+
export declare function splitHistoryBudget(historyBudget: number, cfg?: CompactionConfig): SplitBudget;
|
|
142
|
+
interface AssembleParams {
|
|
143
|
+
messages: CompactionMessage[];
|
|
144
|
+
summary?: string;
|
|
145
|
+
retrievedTurns?: RetrievedTurn[];
|
|
146
|
+
windowBudget: number;
|
|
147
|
+
summaryBudget: number;
|
|
148
|
+
ragBudget: number;
|
|
149
|
+
systemContent?: string;
|
|
150
|
+
/** Context-injection headers; default to English (DEFAULT_COMPACTION_CONFIG). */
|
|
151
|
+
summaryLabel?: string;
|
|
152
|
+
ragIntroLabel?: string;
|
|
153
|
+
}
|
|
154
|
+
interface AssembleResult {
|
|
155
|
+
compactedMessages: CompactionMessage[];
|
|
156
|
+
used: UsageBreakdown;
|
|
157
|
+
dropped: DroppedBreakdown;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Assemble compacted message list given a budget and conversation state.
|
|
161
|
+
*
|
|
162
|
+
* Output order:
|
|
163
|
+
* [system?] → [summary system msg?] → [ragHist system msg?] → window verbatim
|
|
164
|
+
*
|
|
165
|
+
* Always keeps the last 2 non-system messages verbatim (working memory),
|
|
166
|
+
* regardless of windowBudget — these are the floor that can never be dropped.
|
|
167
|
+
*/
|
|
168
|
+
export declare function assembleCompacted(params: AssembleParams): AssembleResult;
|
|
169
|
+
/**
|
|
170
|
+
* Compute budget + assemble in one call. The primary entry point for
|
|
171
|
+
* consumers (a browser request interceptor, mobile, Node tests).
|
|
172
|
+
*/
|
|
173
|
+
export declare function compactConversation(input: CompactionInput): CompactionResult;
|
|
174
|
+
export {};
|
|
175
|
+
//# sourceMappingURL=compactor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compactor.d.ts","sourceRoot":"","sources":["../../src/conversation/compactor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oEAAoE;IACpE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,0BAA0B,EAAE,MAAM,CAAC;IACnC,+CAA+C;IAC/C,oBAAoB,EAAE,MAAM,CAAC;IAC7B;oFACgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAc,SAAQ,eAAe;IAClD,gBAAgB,EAAE,WAAW,CAAC;IAC9B,WAAW,EAAE,cAAc,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,gBAAgB,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC7B,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;IACvC,SAAS,EAAE,aAAa,CAAC;CAC5B;AAID,eAAO,MAAM,yBAAyB,EAAE,gBAoBvC,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGtE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAOvD;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACtC,GAAG,YAAY,CA2Bf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAC9B,aAAa,EAAE,MAAM,EACrB,GAAG,GAAE,gBAA4C,GAClD,WAAW,CAKb;AAID,UAAU,cAAc;IACpB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,UAAU,cAAc;IACpB,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;IACvC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,gBAAgB,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAmGxE;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CAsC5E"}
|