@autonome-research/thread-phase 3.0.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 +226 -0
- package/dist/agent/index.d.ts +28 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +28 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/openai-adapter.d.ts +15 -0
- package/dist/agent/openai-adapter.d.ts.map +1 -0
- package/dist/agent/openai-adapter.js +57 -0
- package/dist/agent/openai-adapter.js.map +1 -0
- package/dist/agent/parse-json.d.ts +12 -0
- package/dist/agent/parse-json.d.ts.map +1 -0
- package/dist/agent/parse-json.js +31 -0
- package/dist/agent/parse-json.js.map +1 -0
- package/dist/agent/retry.d.ts +15 -0
- package/dist/agent/retry.d.ts.map +1 -0
- package/dist/agent/retry.js +35 -0
- package/dist/agent/retry.js.map +1 -0
- package/dist/agent/runner.d.ts +25 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +270 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/stream-consumer.d.ts +57 -0
- package/dist/agent/stream-consumer.d.ts.map +1 -0
- package/dist/agent/stream-consumer.js +126 -0
- package/dist/agent/stream-consumer.js.map +1 -0
- package/dist/agent/types.d.ts +135 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +9 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agent-runner.d.ts +10 -0
- package/dist/agent-runner.d.ts.map +1 -0
- package/dist/agent-runner.js +10 -0
- package/dist/agent-runner.js.map +1 -0
- package/dist/agents/capability.d.ts +36 -0
- package/dist/agents/capability.d.ts.map +1 -0
- package/dist/agents/capability.js +51 -0
- package/dist/agents/capability.js.map +1 -0
- package/dist/agents/event-bus.d.ts +20 -0
- package/dist/agents/event-bus.d.ts.map +1 -0
- package/dist/agents/event-bus.js +40 -0
- package/dist/agents/event-bus.js.map +1 -0
- package/dist/agents/index.d.ts +23 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +33 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/inference-adapter.d.ts +52 -0
- package/dist/agents/inference-adapter.d.ts.map +1 -0
- package/dist/agents/inference-adapter.js +209 -0
- package/dist/agents/inference-adapter.js.map +1 -0
- package/dist/agents/job-store-bridge.d.ts +44 -0
- package/dist/agents/job-store-bridge.d.ts.map +1 -0
- package/dist/agents/job-store-bridge.js +58 -0
- package/dist/agents/job-store-bridge.js.map +1 -0
- package/dist/agents/memory.d.ts +40 -0
- package/dist/agents/memory.d.ts.map +1 -0
- package/dist/agents/memory.js +14 -0
- package/dist/agents/memory.js.map +1 -0
- package/dist/agents/protocol.d.ts +302 -0
- package/dist/agents/protocol.d.ts.map +1 -0
- package/dist/agents/protocol.js +36 -0
- package/dist/agents/protocol.js.map +1 -0
- package/dist/agents/run-helpers.d.ts +70 -0
- package/dist/agents/run-helpers.d.ts.map +1 -0
- package/dist/agents/run-helpers.js +131 -0
- package/dist/agents/run-helpers.js.map +1 -0
- package/dist/agents/serialize-error.d.ts +18 -0
- package/dist/agents/serialize-error.d.ts.map +1 -0
- package/dist/agents/serialize-error.js +27 -0
- package/dist/agents/serialize-error.js.map +1 -0
- package/dist/agents/structured-output.d.ts +90 -0
- package/dist/agents/structured-output.d.ts.map +1 -0
- package/dist/agents/structured-output.js +101 -0
- package/dist/agents/structured-output.js.map +1 -0
- package/dist/agents/test-utils/conformance.d.ts +59 -0
- package/dist/agents/test-utils/conformance.d.ts.map +1 -0
- package/dist/agents/test-utils/conformance.js +207 -0
- package/dist/agents/test-utils/conformance.js.map +1 -0
- package/dist/agents/test-utils/index.d.ts +12 -0
- package/dist/agents/test-utils/index.d.ts.map +1 -0
- package/dist/agents/test-utils/index.js +12 -0
- package/dist/agents/test-utils/index.js.map +1 -0
- package/dist/agents/test-utils/mock-agent.d.ts +66 -0
- package/dist/agents/test-utils/mock-agent.d.ts.map +1 -0
- package/dist/agents/test-utils/mock-agent.js +244 -0
- package/dist/agents/test-utils/mock-agent.js.map +1 -0
- package/dist/agents/thread.d.ts +57 -0
- package/dist/agents/thread.d.ts.map +1 -0
- package/dist/agents/thread.js +128 -0
- package/dist/agents/thread.js.map +1 -0
- package/dist/agents/turn-accumulator.d.ts +94 -0
- package/dist/agents/turn-accumulator.d.ts.map +1 -0
- package/dist/agents/turn-accumulator.js +150 -0
- package/dist/agents/turn-accumulator.js.map +1 -0
- package/dist/agents/with-memory.d.ts +55 -0
- package/dist/agents/with-memory.d.ts.map +1 -0
- package/dist/agents/with-memory.js +155 -0
- package/dist/agents/with-memory.js.map +1 -0
- package/dist/agents/with-thread.d.ts +45 -0
- package/dist/agents/with-thread.d.ts.map +1 -0
- package/dist/agents/with-thread.js +70 -0
- package/dist/agents/with-thread.js.map +1 -0
- package/dist/cache.d.ts +47 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +81 -0
- package/dist/cache.js.map +1 -0
- package/dist/context/compressor.d.ts +36 -0
- package/dist/context/compressor.d.ts.map +1 -0
- package/dist/context/compressor.js +158 -0
- package/dist/context/compressor.js.map +1 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +4 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/result-capper.d.ts +32 -0
- package/dist/context/result-capper.d.ts.map +1 -0
- package/dist/context/result-capper.js +50 -0
- package/dist/context/result-capper.js.map +1 -0
- package/dist/context/token-budget.d.ts +81 -0
- package/dist/context/token-budget.d.ts.map +1 -0
- package/dist/context/token-budget.js +99 -0
- package/dist/context/token-budget.js.map +1 -0
- package/dist/helpers/caller.d.ts +18 -0
- package/dist/helpers/caller.d.ts.map +1 -0
- package/dist/helpers/caller.js +40 -0
- package/dist/helpers/caller.js.map +1 -0
- package/dist/helpers/hook.d.ts +73 -0
- package/dist/helpers/hook.d.ts.map +1 -0
- package/dist/helpers/hook.js +244 -0
- package/dist/helpers/hook.js.map +1 -0
- package/dist/helpers/index.d.ts +12 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +11 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/one-shot.d.ts +27 -0
- package/dist/helpers/one-shot.d.ts.map +1 -0
- package/dist/helpers/one-shot.js +43 -0
- package/dist/helpers/one-shot.js.map +1 -0
- package/dist/helpers/schedule.d.ts +59 -0
- package/dist/helpers/schedule.d.ts.map +1 -0
- package/dist/helpers/schedule.js +118 -0
- package/dist/helpers/schedule.js.map +1 -0
- package/dist/helpers/types.d.ts +34 -0
- package/dist/helpers/types.d.ts.map +1 -0
- package/dist/helpers/types.js +11 -0
- package/dist/helpers/types.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/inference.d.ts +27 -0
- package/dist/inference.d.ts.map +1 -0
- package/dist/inference.js +34 -0
- package/dist/inference.js.map +1 -0
- package/dist/messages.d.ts +64 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +17 -0
- package/dist/messages.js.map +1 -0
- package/dist/orchestrator.d.ts +56 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +62 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/patterns/bounded-fanout-of.d.ts +61 -0
- package/dist/patterns/bounded-fanout-of.d.ts.map +1 -0
- package/dist/patterns/bounded-fanout-of.js +142 -0
- package/dist/patterns/bounded-fanout-of.js.map +1 -0
- package/dist/patterns/bounded-fanout.d.ts +111 -0
- package/dist/patterns/bounded-fanout.d.ts.map +1 -0
- package/dist/patterns/bounded-fanout.js +151 -0
- package/dist/patterns/bounded-fanout.js.map +1 -0
- package/dist/patterns/index.d.ts +14 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +13 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/intent-gate.d.ts +27 -0
- package/dist/patterns/intent-gate.d.ts.map +1 -0
- package/dist/patterns/intent-gate.js +32 -0
- package/dist/patterns/intent-gate.js.map +1 -0
- package/dist/patterns/match.d.ts +30 -0
- package/dist/patterns/match.d.ts.map +1 -0
- package/dist/patterns/match.js +58 -0
- package/dist/patterns/match.js.map +1 -0
- package/dist/patterns/parallel-fanout.d.ts +28 -0
- package/dist/patterns/parallel-fanout.d.ts.map +1 -0
- package/dist/patterns/parallel-fanout.js +24 -0
- package/dist/patterns/parallel-fanout.js.map +1 -0
- package/dist/patterns/parallel-phases.d.ts +27 -0
- package/dist/patterns/parallel-phases.d.ts.map +1 -0
- package/dist/patterns/parallel-phases.js +77 -0
- package/dist/patterns/parallel-phases.js.map +1 -0
- package/dist/patterns/preflight-confidence.d.ts +20 -0
- package/dist/patterns/preflight-confidence.d.ts.map +1 -0
- package/dist/patterns/preflight-confidence.js +38 -0
- package/dist/patterns/preflight-confidence.js.map +1 -0
- package/dist/patterns/spot-check.d.ts +19 -0
- package/dist/patterns/spot-check.d.ts.map +1 -0
- package/dist/patterns/spot-check.js +33 -0
- package/dist/patterns/spot-check.js.map +1 -0
- package/dist/patterns/sub-pipeline.d.ts +84 -0
- package/dist/patterns/sub-pipeline.d.ts.map +1 -0
- package/dist/patterns/sub-pipeline.js +90 -0
- package/dist/patterns/sub-pipeline.js.map +1 -0
- package/dist/patterns/synthesize-with-followup.d.ts +35 -0
- package/dist/patterns/synthesize-with-followup.d.ts.map +1 -0
- package/dist/patterns/synthesize-with-followup.js +45 -0
- package/dist/patterns/synthesize-with-followup.js.map +1 -0
- package/dist/patterns/while-condition.d.ts +31 -0
- package/dist/patterns/while-condition.d.ts.map +1 -0
- package/dist/patterns/while-condition.js +59 -0
- package/dist/patterns/while-condition.js.map +1 -0
- package/dist/patterns/with-retry.d.ts +37 -0
- package/dist/patterns/with-retry.d.ts.map +1 -0
- package/dist/patterns/with-retry.js +73 -0
- package/dist/patterns/with-retry.js.map +1 -0
- package/dist/phase.d.ts +78 -0
- package/dist/phase.d.ts.map +1 -0
- package/dist/phase.js +36 -0
- package/dist/phase.js.map +1 -0
- package/dist/session/index.d.ts +5 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +4 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/job-runner.d.ts +67 -0
- package/dist/session/job-runner.d.ts.map +1 -0
- package/dist/session/job-runner.js +131 -0
- package/dist/session/job-runner.js.map +1 -0
- package/dist/session/job-store.d.ts +98 -0
- package/dist/session/job-store.d.ts.map +1 -0
- package/dist/session/job-store.js +37 -0
- package/dist/session/job-store.js.map +1 -0
- package/dist/session/sqlite-job-store.d.ts +40 -0
- package/dist/session/sqlite-job-store.d.ts.map +1 -0
- package/dist/session/sqlite-job-store.js +200 -0
- package/dist/session/sqlite-job-store.js.map +1 -0
- package/dist/session/sse.d.ts +60 -0
- package/dist/session/sse.d.ts.map +1 -0
- package/dist/session/sse.js +97 -0
- package/dist/session/sse.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +44 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +74 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/triggers/index.d.ts +15 -0
- package/dist/triggers/index.d.ts.map +1 -0
- package/dist/triggers/index.js +14 -0
- package/dist/triggers/index.js.map +1 -0
- package/dist/triggers/run-trigger.d.ts +86 -0
- package/dist/triggers/run-trigger.d.ts.map +1 -0
- package/dist/triggers/run-trigger.js +146 -0
- package/dist/triggers/run-trigger.js.map +1 -0
- package/dist/triggers/timer-trigger.d.ts +46 -0
- package/dist/triggers/timer-trigger.d.ts.map +1 -0
- package/dist/triggers/timer-trigger.js +74 -0
- package/dist/triggers/timer-trigger.js.map +1 -0
- package/dist/triggers/types.d.ts +61 -0
- package/dist/triggers/types.d.ts.map +1 -0
- package/dist/triggers/types.js +23 -0
- package/dist/triggers/types.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message compressor — Layer 2 of token defense.
|
|
3
|
+
*
|
|
4
|
+
* When the total message history exceeds the compression threshold, older
|
|
5
|
+
* tool-result messages get their content replaced with one-line summaries.
|
|
6
|
+
* Tool call/result pairing is preserved (orphaned calls or results would
|
|
7
|
+
* cause API errors at the next request).
|
|
8
|
+
*
|
|
9
|
+
* Operates on the framework's internal Message shape (see ../messages.ts).
|
|
10
|
+
* Tool results are their own role:'tool' messages with toolCallId pointing
|
|
11
|
+
* back to an assistant message's toolCalls[i].id.
|
|
12
|
+
*/
|
|
13
|
+
import type { Message } from '../messages.js';
|
|
14
|
+
export interface CompressorStrategy {
|
|
15
|
+
compress(messages: Message[], options: CompressionOptions): Message[];
|
|
16
|
+
}
|
|
17
|
+
export interface CompressionOptions {
|
|
18
|
+
/** Number of leading messages to keep verbatim (system + initial user). */
|
|
19
|
+
protectFirst: number;
|
|
20
|
+
/** Number of trailing messages to keep verbatim (recent context). */
|
|
21
|
+
protectLast: number;
|
|
22
|
+
/** Activity-log entries used to enrich compressed summaries. Optional. */
|
|
23
|
+
activityLog: Array<{
|
|
24
|
+
agent: string;
|
|
25
|
+
action: string;
|
|
26
|
+
detail?: string;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export declare class DeterministicCompressor implements CompressorStrategy {
|
|
30
|
+
compress(messages: Message[], options: CompressionOptions): Message[];
|
|
31
|
+
}
|
|
32
|
+
export declare class AggressiveCompressor implements CompressorStrategy {
|
|
33
|
+
compress(messages: Message[], options: CompressionOptions): Message[];
|
|
34
|
+
}
|
|
35
|
+
export declare function sanitizeToolPairs(messages: Message[]): Message[];
|
|
36
|
+
//# sourceMappingURL=compressor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compressor.d.ts","sourceRoot":"","sources":["../../src/context/compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAuC,MAAM,gBAAgB,CAAC;AAMnF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,EAAE,CAAC;CACvE;AAED,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,WAAW,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxE;AAMD,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,EAAE;CA2BtE;AAOD,qBAAa,oBAAqB,YAAW,kBAAkB;IAC7D,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,EAAE;CAqCtE;AAiBD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAmEhE"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message compressor — Layer 2 of token defense.
|
|
3
|
+
*
|
|
4
|
+
* When the total message history exceeds the compression threshold, older
|
|
5
|
+
* tool-result messages get their content replaced with one-line summaries.
|
|
6
|
+
* Tool call/result pairing is preserved (orphaned calls or results would
|
|
7
|
+
* cause API errors at the next request).
|
|
8
|
+
*
|
|
9
|
+
* Operates on the framework's internal Message shape (see ../messages.ts).
|
|
10
|
+
* Tool results are their own role:'tool' messages with toolCallId pointing
|
|
11
|
+
* back to an assistant message's toolCalls[i].id.
|
|
12
|
+
*/
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Default: deterministic compression of old tool-result messages.
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export class DeterministicCompressor {
|
|
17
|
+
compress(messages, options) {
|
|
18
|
+
const { protectFirst, protectLast } = options;
|
|
19
|
+
if (messages.length <= protectFirst + protectLast) {
|
|
20
|
+
return messages;
|
|
21
|
+
}
|
|
22
|
+
const head = messages.slice(0, protectFirst);
|
|
23
|
+
const tail = messages.slice(-protectLast);
|
|
24
|
+
const middle = messages.slice(protectFirst, messages.length - protectLast);
|
|
25
|
+
const compressed = [...head];
|
|
26
|
+
for (const msg of middle) {
|
|
27
|
+
if (msg.role === 'tool') {
|
|
28
|
+
const compressedMsg = {
|
|
29
|
+
role: 'tool',
|
|
30
|
+
toolCallId: msg.toolCallId,
|
|
31
|
+
content: `[Previous tool result: ${msg.content.length} chars — compressed to save context]`,
|
|
32
|
+
};
|
|
33
|
+
compressed.push(compressedMsg);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
compressed.push(msg);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
compressed.push(...tail);
|
|
40
|
+
return compressed;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Aggressive: also compress tool-call arguments in old assistant messages.
|
|
45
|
+
// Used when deterministic compression isn't enough (HARD_STOP path).
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
export class AggressiveCompressor {
|
|
48
|
+
compress(messages, options) {
|
|
49
|
+
const { protectFirst, protectLast } = options;
|
|
50
|
+
if (messages.length <= protectFirst + protectLast) {
|
|
51
|
+
return messages;
|
|
52
|
+
}
|
|
53
|
+
const head = messages.slice(0, protectFirst);
|
|
54
|
+
const tail = messages.slice(-protectLast);
|
|
55
|
+
const middle = messages.slice(protectFirst, messages.length - protectLast);
|
|
56
|
+
const compressed = [...head];
|
|
57
|
+
for (const msg of middle) {
|
|
58
|
+
if (msg.role === 'tool') {
|
|
59
|
+
compressed.push({
|
|
60
|
+
role: 'tool',
|
|
61
|
+
toolCallId: msg.toolCallId,
|
|
62
|
+
content: '[Compressed — old tool result removed]',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else if (msg.role === 'assistant' && msg.toolCalls.length > 0) {
|
|
66
|
+
const stubbed = {
|
|
67
|
+
role: 'assistant',
|
|
68
|
+
content: msg.content,
|
|
69
|
+
toolCalls: msg.toolCalls.map((tc) => ({
|
|
70
|
+
id: tc.id,
|
|
71
|
+
name: tc.name,
|
|
72
|
+
input: { _compressed: true, summary: `Called ${tc.name}` },
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
compressed.push(stubbed);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
compressed.push(msg);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
compressed.push(...tail);
|
|
82
|
+
return compressed;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Tool-pair sanitization.
|
|
87
|
+
//
|
|
88
|
+
// Ensures every assistant ToolCall has a matching tool-result message and
|
|
89
|
+
// vice versa. Orphaned results get dropped; orphaned calls get stub results
|
|
90
|
+
// inserted. Run AFTER compression and before the next API call.
|
|
91
|
+
//
|
|
92
|
+
// Partial-orphan handling: when an assistant emits multiple tool calls and
|
|
93
|
+
// only some have matching results, stubs for the orphaned calls are
|
|
94
|
+
// appended *after* the run of real tool-result messages (i.e. immediately
|
|
95
|
+
// before whatever non-tool message comes next, or end-of-array). This
|
|
96
|
+
// preserves the OpenAI invariant "every tool_call.id must have a tool
|
|
97
|
+
// message" without re-ordering existing real results.
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
export function sanitizeToolPairs(messages) {
|
|
100
|
+
const callIds = new Set();
|
|
101
|
+
const resultIds = new Set();
|
|
102
|
+
for (const msg of messages) {
|
|
103
|
+
if (msg.role === 'assistant') {
|
|
104
|
+
for (const tc of msg.toolCalls)
|
|
105
|
+
callIds.add(tc.id);
|
|
106
|
+
}
|
|
107
|
+
else if (msg.role === 'tool') {
|
|
108
|
+
resultIds.add(msg.toolCallId);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const orphanedCalls = new Set([...callIds].filter((id) => !resultIds.has(id)));
|
|
112
|
+
const orphanedResults = new Set([...resultIds].filter((id) => !callIds.has(id)));
|
|
113
|
+
if (orphanedCalls.size === 0 && orphanedResults.size === 0) {
|
|
114
|
+
return messages;
|
|
115
|
+
}
|
|
116
|
+
const result = [];
|
|
117
|
+
for (let i = 0; i < messages.length; i++) {
|
|
118
|
+
const msg = messages[i];
|
|
119
|
+
// Drop orphaned tool-result messages outright.
|
|
120
|
+
if (msg.role === 'tool' && orphanedResults.has(msg.toolCallId)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
result.push(msg);
|
|
124
|
+
// Insert stub results for orphaned calls right after the assistant +
|
|
125
|
+
// any contiguous run of (kept) real tool-result messages that follow
|
|
126
|
+
// it. This handles the partial-orphan case where some calls have real
|
|
127
|
+
// results and some don't — stubs go AFTER the real ones rather than
|
|
128
|
+
// being skipped entirely (which would leave the orphans unpaired).
|
|
129
|
+
if (msg.role === 'assistant') {
|
|
130
|
+
const orphansHere = msg.toolCalls.map((tc) => tc.id).filter((id) => orphanedCalls.has(id));
|
|
131
|
+
if (orphansHere.length === 0)
|
|
132
|
+
continue;
|
|
133
|
+
// Walk forward over any tool messages immediately following the
|
|
134
|
+
// assistant — append the kept ones to result first, then stub the
|
|
135
|
+
// orphans, so the final order is: assistant → real results → stubs.
|
|
136
|
+
let j = i + 1;
|
|
137
|
+
while (j < messages.length && messages[j].role === 'tool') {
|
|
138
|
+
const next = messages[j];
|
|
139
|
+
if (!orphanedResults.has(next.toolCallId)) {
|
|
140
|
+
result.push(next);
|
|
141
|
+
}
|
|
142
|
+
j++;
|
|
143
|
+
}
|
|
144
|
+
for (const id of orphansHere) {
|
|
145
|
+
result.push({
|
|
146
|
+
role: 'tool',
|
|
147
|
+
toolCallId: id,
|
|
148
|
+
content: '[Result removed during context compression]',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Skip past the tool messages we just consumed; the outer loop's
|
|
152
|
+
// `i++` lands on `j`.
|
|
153
|
+
i = j - 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=compressor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compressor.js","sourceRoot":"","sources":["../../src/context/compressor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { TokenBudgetTracker, BudgetStatus, RoughTokenEstimator, type TokenEstimator, type TokenBudgetConfig, type BudgetCheck, } from './token-budget.js';
|
|
2
|
+
export { TruncateAndCacheResultCapper, NoOpResultCapper, type ResultCapper, } from './result-capper.js';
|
|
3
|
+
export { DeterministicCompressor, AggressiveCompressor, sanitizeToolPairs, type CompressorStrategy, type CompressionOptions, } from './compressor.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,4BAA4B,EAC5B,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { TokenBudgetTracker, BudgetStatus, RoughTokenEstimator, } from './token-budget.js';
|
|
2
|
+
export { TruncateAndCacheResultCapper, NoOpResultCapper, } from './result-capper.js';
|
|
3
|
+
export { DeterministicCompressor, AggressiveCompressor, sanitizeToolPairs, } from './compressor.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,GAIpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,4BAA4B,EAC5B,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-result capping — Layer 1 of token defense.
|
|
3
|
+
*
|
|
4
|
+
* Caps individual tool results before they enter the message history.
|
|
5
|
+
* Oversize results get a preview returned and (optionally) the full content
|
|
6
|
+
* stored in the pipeline cache for later retrieval.
|
|
7
|
+
*/
|
|
8
|
+
import type { PipelineCache } from '../cache.js';
|
|
9
|
+
export interface ResultCapper {
|
|
10
|
+
/**
|
|
11
|
+
* Cap a tool result. If under limit, returns as-is.
|
|
12
|
+
* If over, returns a preview and (if cache is provided) stows the full
|
|
13
|
+
* content under `full_result:{toolCallId}` for later retrieval.
|
|
14
|
+
*/
|
|
15
|
+
cap(content: string, toolName: string, toolCallId: string, cache?: PipelineCache | null): string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Default capper: truncate at last newline within the preview budget,
|
|
19
|
+
* cache full content if a cache is provided.
|
|
20
|
+
*/
|
|
21
|
+
export declare class TruncateAndCacheResultCapper implements ResultCapper {
|
|
22
|
+
private maxChars;
|
|
23
|
+
private previewChars;
|
|
24
|
+
constructor(maxChars?: number, previewChars?: number);
|
|
25
|
+
cap(content: string, _toolName: string, toolCallId: string, cache?: PipelineCache | null): string;
|
|
26
|
+
private generatePreview;
|
|
27
|
+
}
|
|
28
|
+
/** No-op capper for tests or when capping is disabled. */
|
|
29
|
+
export declare class NoOpResultCapper implements ResultCapper {
|
|
30
|
+
cap(content: string): string;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=result-capper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-capper.d.ts","sourceRoot":"","sources":["../../src/context/result-capper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAAC;CAClG;AAED;;;GAGG;AACH,qBAAa,4BAA6B,YAAW,YAAY;IAC/D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,QAAQ,GAAE,MAAe,EAAE,YAAY,GAAE,MAAc;IAKnE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM;IAmBjG,OAAO,CAAC,eAAe;CAaxB;AAED,0DAA0D;AAC1D,qBAAa,gBAAiB,YAAW,YAAY;IACnD,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAG7B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-result capping — Layer 1 of token defense.
|
|
3
|
+
*
|
|
4
|
+
* Caps individual tool results before they enter the message history.
|
|
5
|
+
* Oversize results get a preview returned and (optionally) the full content
|
|
6
|
+
* stored in the pipeline cache for later retrieval.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Default capper: truncate at last newline within the preview budget,
|
|
10
|
+
* cache full content if a cache is provided.
|
|
11
|
+
*/
|
|
12
|
+
export class TruncateAndCacheResultCapper {
|
|
13
|
+
maxChars;
|
|
14
|
+
previewChars;
|
|
15
|
+
constructor(maxChars = 12_000, previewChars = 1_500) {
|
|
16
|
+
this.maxChars = maxChars;
|
|
17
|
+
this.previewChars = previewChars;
|
|
18
|
+
}
|
|
19
|
+
cap(content, _toolName, toolCallId, cache) {
|
|
20
|
+
if (content.length <= this.maxChars) {
|
|
21
|
+
return content;
|
|
22
|
+
}
|
|
23
|
+
const preview = this.generatePreview(content);
|
|
24
|
+
const fullLength = content.length;
|
|
25
|
+
if (cache) {
|
|
26
|
+
cache.set(`full_result:${toolCallId}`, content);
|
|
27
|
+
}
|
|
28
|
+
return (`[Result capped: ${fullLength} chars total, showing first ${preview.length} chars. ` +
|
|
29
|
+
`Full result available in pipeline cache (key: full_result:${toolCallId}).]\n\n` +
|
|
30
|
+
preview);
|
|
31
|
+
}
|
|
32
|
+
generatePreview(content) {
|
|
33
|
+
if (content.length <= this.previewChars) {
|
|
34
|
+
return content;
|
|
35
|
+
}
|
|
36
|
+
const truncated = content.slice(0, this.previewChars);
|
|
37
|
+
const lastNewline = truncated.lastIndexOf('\n');
|
|
38
|
+
if (lastNewline > this.previewChars / 2) {
|
|
39
|
+
return truncated.slice(0, lastNewline + 1) + '\n...[truncated]';
|
|
40
|
+
}
|
|
41
|
+
return truncated + '\n...[truncated]';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** No-op capper for tests or when capping is disabled. */
|
|
45
|
+
export class NoOpResultCapper {
|
|
46
|
+
cap(content) {
|
|
47
|
+
return content;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=result-capper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-capper.js","sourceRoot":"","sources":["../../src/context/result-capper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;GAGG;AACH,MAAM,OAAO,4BAA4B;IAC/B,QAAQ,CAAS;IACjB,YAAY,CAAS;IAE7B,YAAY,WAAmB,MAAM,EAAE,eAAuB,KAAK;QACjE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,OAAe,EAAE,SAAiB,EAAE,UAAkB,EAAE,KAA4B;QACtF,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;QAElC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,GAAG,CAAC,eAAe,UAAU,EAAE,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,CACL,mBAAmB,UAAU,+BAA+B,OAAO,CAAC,MAAM,UAAU;YACpF,6DAA6D,UAAU,SAAS;YAChF,OAAO,CACR,CAAC;IACJ,CAAC;IAEO,eAAe,CAAC,OAAe;QACrC,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACxC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,WAAW,GAAG,IAAI,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YACxC,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC;QAClE,CAAC;QAED,OAAO,SAAS,GAAG,kBAAkB,CAAC;IACxC,CAAC;CACF;AAED,0DAA0D;AAC1D,MAAM,OAAO,gBAAgB;IAC3B,GAAG,CAAC,OAAe;QACjB,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token budget tracking and enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Estimates token usage for the next API request (system + messages + tools)
|
|
5
|
+
* and reports a status code that the agent runner uses to trigger compression
|
|
6
|
+
* or force a final response.
|
|
7
|
+
*
|
|
8
|
+
* Estimator strategy is swappable. Default is a chars-per-token heuristic;
|
|
9
|
+
* downstream can plug in a tiktoken-based estimator for exact counts.
|
|
10
|
+
*/
|
|
11
|
+
import type { Message, ToolDefinition } from '../messages.js';
|
|
12
|
+
export interface TokenEstimator {
|
|
13
|
+
estimate(text: string): number;
|
|
14
|
+
estimateRequest(systemPrompt: string, messages: Message[], tools: ToolDefinition[]): number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Default: ~4 chars per token. Accurate enough for budget enforcement.
|
|
18
|
+
* Replace with a tiktoken-based estimator for exact counts.
|
|
19
|
+
*/
|
|
20
|
+
export declare class RoughTokenEstimator implements TokenEstimator {
|
|
21
|
+
private readonly charsPerToken;
|
|
22
|
+
constructor(charsPerToken?: number);
|
|
23
|
+
estimate(text: string): number;
|
|
24
|
+
estimateRequest(systemPrompt: string, messages: Message[], tools: ToolDefinition[]): number;
|
|
25
|
+
}
|
|
26
|
+
export interface TokenBudgetConfig {
|
|
27
|
+
contextWindow: number;
|
|
28
|
+
/** Trigger compression at this fraction of available budget (default 0.50). */
|
|
29
|
+
compressionThreshold: number;
|
|
30
|
+
/** Force output at this fraction of available budget (default 0.80). */
|
|
31
|
+
hardStopThreshold: number;
|
|
32
|
+
/** Tokens reserved for the model's response. */
|
|
33
|
+
responseReserve: number;
|
|
34
|
+
/** Per-result cap (chars) before the result-capper truncates. */
|
|
35
|
+
perResultCapChars: number;
|
|
36
|
+
/** Preview size (chars) when a result is capped. */
|
|
37
|
+
previewSizeChars: number;
|
|
38
|
+
/**
|
|
39
|
+
* Leading messages preserved verbatim during compression — typically the
|
|
40
|
+
* system prompt and the seed user message. Default 1.
|
|
41
|
+
*/
|
|
42
|
+
protectFirst: number;
|
|
43
|
+
/**
|
|
44
|
+
* Trailing messages preserved verbatim during compression — recent context
|
|
45
|
+
* the model needs to make progress. Default 6 (deterministic) / 4 (aggressive).
|
|
46
|
+
* Set to a per-pipeline value when the workflow needs more or less recent history.
|
|
47
|
+
*/
|
|
48
|
+
protectLast: number;
|
|
49
|
+
/** Trailing messages preserved during the aggressive (HARD_STOP) compressor. Default 4. */
|
|
50
|
+
protectLastAggressive: number;
|
|
51
|
+
}
|
|
52
|
+
export declare enum BudgetStatus {
|
|
53
|
+
OK = "ok",
|
|
54
|
+
COMPRESS = "compress",
|
|
55
|
+
HARD_STOP = "hard_stop"
|
|
56
|
+
}
|
|
57
|
+
export interface BudgetCheck {
|
|
58
|
+
status: BudgetStatus;
|
|
59
|
+
estimatedTokens: number;
|
|
60
|
+
/** 0.0 to 1.0+. Fraction of (contextWindow - responseReserve). */
|
|
61
|
+
budgetUsed: number;
|
|
62
|
+
compressionThreshold: number;
|
|
63
|
+
hardStopThreshold: number;
|
|
64
|
+
}
|
|
65
|
+
export declare class TokenBudgetTracker {
|
|
66
|
+
private config;
|
|
67
|
+
private estimator;
|
|
68
|
+
constructor(config?: Partial<TokenBudgetConfig>, estimator?: TokenEstimator);
|
|
69
|
+
check(systemPrompt: string, messages: Message[], tools: ToolDefinition[]): BudgetCheck;
|
|
70
|
+
getResultCap(): {
|
|
71
|
+
maxChars: number;
|
|
72
|
+
previewChars: number;
|
|
73
|
+
};
|
|
74
|
+
getProtectCounts(): {
|
|
75
|
+
protectFirst: number;
|
|
76
|
+
protectLast: number;
|
|
77
|
+
protectLastAggressive: number;
|
|
78
|
+
};
|
|
79
|
+
getEstimator(): TokenEstimator;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=token-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-budget.d.ts","sourceRoot":"","sources":["../../src/context/token-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAM9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC;CAC7F;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,aAAa,GAAE,MAAU;IAIrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAK9B,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM;CAO5F;AAMD,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAkBD,oBAAY,YAAY;IACtB,EAAE,OAAO;IACT,QAAQ,aAAa;IACrB,SAAS,cAAc;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,SAAS,CAAiB;gBAEtB,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM,EAAE,SAAS,CAAC,EAAE,cAAc;IAK/E,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,WAAW;IAqBtF,YAAY,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAO1D,gBAAgB,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,qBAAqB,EAAE,MAAM,CAAC;KAC/B;IAQD,YAAY,IAAI,cAAc;CAG/B"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token budget tracking and enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Estimates token usage for the next API request (system + messages + tools)
|
|
5
|
+
* and reports a status code that the agent runner uses to trigger compression
|
|
6
|
+
* or force a final response.
|
|
7
|
+
*
|
|
8
|
+
* Estimator strategy is swappable. Default is a chars-per-token heuristic;
|
|
9
|
+
* downstream can plug in a tiktoken-based estimator for exact counts.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Default: ~4 chars per token. Accurate enough for budget enforcement.
|
|
13
|
+
* Replace with a tiktoken-based estimator for exact counts.
|
|
14
|
+
*/
|
|
15
|
+
export class RoughTokenEstimator {
|
|
16
|
+
charsPerToken;
|
|
17
|
+
constructor(charsPerToken = 4) {
|
|
18
|
+
this.charsPerToken = charsPerToken;
|
|
19
|
+
}
|
|
20
|
+
estimate(text) {
|
|
21
|
+
if (!text)
|
|
22
|
+
return 0;
|
|
23
|
+
return Math.ceil(text.length / this.charsPerToken);
|
|
24
|
+
}
|
|
25
|
+
estimateRequest(systemPrompt, messages, tools) {
|
|
26
|
+
let totalChars = 0;
|
|
27
|
+
if (systemPrompt)
|
|
28
|
+
totalChars += systemPrompt.length;
|
|
29
|
+
if (messages.length > 0)
|
|
30
|
+
totalChars += JSON.stringify(messages).length;
|
|
31
|
+
if (tools.length > 0)
|
|
32
|
+
totalChars += JSON.stringify(tools).length;
|
|
33
|
+
return Math.ceil(totalChars / this.charsPerToken);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const DEFAULT_CONFIG = {
|
|
37
|
+
contextWindow: 131_072,
|
|
38
|
+
compressionThreshold: 0.5,
|
|
39
|
+
hardStopThreshold: 0.8,
|
|
40
|
+
responseReserve: 4_096,
|
|
41
|
+
perResultCapChars: 12_000,
|
|
42
|
+
previewSizeChars: 1_500,
|
|
43
|
+
protectFirst: 1,
|
|
44
|
+
protectLast: 6,
|
|
45
|
+
protectLastAggressive: 4,
|
|
46
|
+
};
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Budget tracker
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
export var BudgetStatus;
|
|
51
|
+
(function (BudgetStatus) {
|
|
52
|
+
BudgetStatus["OK"] = "ok";
|
|
53
|
+
BudgetStatus["COMPRESS"] = "compress";
|
|
54
|
+
BudgetStatus["HARD_STOP"] = "hard_stop";
|
|
55
|
+
})(BudgetStatus || (BudgetStatus = {}));
|
|
56
|
+
export class TokenBudgetTracker {
|
|
57
|
+
config;
|
|
58
|
+
estimator;
|
|
59
|
+
constructor(config = {}, estimator) {
|
|
60
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
61
|
+
this.estimator = estimator ?? new RoughTokenEstimator();
|
|
62
|
+
}
|
|
63
|
+
check(systemPrompt, messages, tools) {
|
|
64
|
+
const estimatedTokens = this.estimator.estimateRequest(systemPrompt, messages, tools);
|
|
65
|
+
const available = this.config.contextWindow - this.config.responseReserve;
|
|
66
|
+
const budgetUsed = estimatedTokens / available;
|
|
67
|
+
let status = BudgetStatus.OK;
|
|
68
|
+
if (budgetUsed >= this.config.hardStopThreshold) {
|
|
69
|
+
status = BudgetStatus.HARD_STOP;
|
|
70
|
+
}
|
|
71
|
+
else if (budgetUsed >= this.config.compressionThreshold) {
|
|
72
|
+
status = BudgetStatus.COMPRESS;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
status,
|
|
76
|
+
estimatedTokens,
|
|
77
|
+
budgetUsed,
|
|
78
|
+
compressionThreshold: this.config.compressionThreshold,
|
|
79
|
+
hardStopThreshold: this.config.hardStopThreshold,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
getResultCap() {
|
|
83
|
+
return {
|
|
84
|
+
maxChars: this.config.perResultCapChars,
|
|
85
|
+
previewChars: this.config.previewSizeChars,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
getProtectCounts() {
|
|
89
|
+
return {
|
|
90
|
+
protectFirst: this.config.protectFirst,
|
|
91
|
+
protectLast: this.config.protectLast,
|
|
92
|
+
protectLastAggressive: this.config.protectLastAggressive,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
getEstimator() {
|
|
96
|
+
return this.estimator;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=token-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-budget.js","sourceRoot":"","sources":["../../src/context/token-budget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort name derivation from the calling file's path.
|
|
3
|
+
*
|
|
4
|
+
* Walks the captured stack and returns the basename of the first frame
|
|
5
|
+
* outside this `helpers/` directory. Used by `oneShot` / `schedule` /
|
|
6
|
+
* `hook` to auto-name registrations when the caller didn't pass one.
|
|
7
|
+
*
|
|
8
|
+
* Pure heuristic — for production registrations the caller should pass
|
|
9
|
+
* an explicit `name`. The stack-walk is fast and synchronous; the
|
|
10
|
+
* helper file paths the matcher looks for are stable.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Returns a slug-y name derived from the calling file (the first stack
|
|
14
|
+
* frame that isn't inside `helpers/`). Falls back to `fallback` if no
|
|
15
|
+
* usable frame is found.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deriveNameFromCaller(fallback: string): string;
|
|
18
|
+
//# sourceMappingURL=caller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caller.d.ts","sourceRoot":"","sources":["../../src/helpers/caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAYH;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa7D"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort name derivation from the calling file's path.
|
|
3
|
+
*
|
|
4
|
+
* Walks the captured stack and returns the basename of the first frame
|
|
5
|
+
* outside this `helpers/` directory. Used by `oneShot` / `schedule` /
|
|
6
|
+
* `hook` to auto-name registrations when the caller didn't pass one.
|
|
7
|
+
*
|
|
8
|
+
* Pure heuristic — for production registrations the caller should pass
|
|
9
|
+
* an explicit `name`. The stack-walk is fast and synchronous; the
|
|
10
|
+
* helper file paths the matcher looks for are stable.
|
|
11
|
+
*/
|
|
12
|
+
import { basename } from 'node:path';
|
|
13
|
+
const HELPER_FILE_MATCHERS = [
|
|
14
|
+
'helpers/one-shot',
|
|
15
|
+
'helpers/schedule',
|
|
16
|
+
'helpers/hook',
|
|
17
|
+
'helpers/caller',
|
|
18
|
+
'helpers/index',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Returns a slug-y name derived from the calling file (the first stack
|
|
22
|
+
* frame that isn't inside `helpers/`). Falls back to `fallback` if no
|
|
23
|
+
* usable frame is found.
|
|
24
|
+
*/
|
|
25
|
+
export function deriveNameFromCaller(fallback) {
|
|
26
|
+
const stack = new Error().stack ?? '';
|
|
27
|
+
const lines = stack.split('\n').slice(1);
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
const match = line.match(/\(?(\/[^):\s]+\.(?:ts|js|mts|mjs|cjs))(?::\d+)?(?::\d+)?\)?/);
|
|
30
|
+
if (!match)
|
|
31
|
+
continue;
|
|
32
|
+
const path = match[1];
|
|
33
|
+
if (HELPER_FILE_MATCHERS.some((m) => path.includes(m)))
|
|
34
|
+
continue;
|
|
35
|
+
const base = basename(path).replace(/\.(ts|js|mts|mjs|cjs)$/, '');
|
|
36
|
+
return base;
|
|
37
|
+
}
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=caller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caller.js","sourceRoot":"","sources":["../../src/helpers/caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,oBAAoB,GAAG;IAC3B,kBAAkB;IAClB,kBAAkB;IAClB,cAAc;IACd,gBAAgB;IAChB,eAAe;CAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACxF,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACvB,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAAE,SAAS;QACjE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hook` — register a pipeline driven by an inbound HTTP webhook.
|
|
3
|
+
*
|
|
4
|
+
* export default hook({ path: '/webhook/digest' }, async (payload, ctx) => {
|
|
5
|
+
* return { ok: true, processed: payload };
|
|
6
|
+
* });
|
|
7
|
+
*
|
|
8
|
+
* The library maintains one `node:http` server per project — every `hook`
|
|
9
|
+
* registration adds a new route to the same server. The server starts
|
|
10
|
+
* lazily on the first `Trigger.start()`. Routes resolve by exact path
|
|
11
|
+
* match; only POST with a JSON body is supported in v3.0.0.
|
|
12
|
+
*
|
|
13
|
+
* The handler is called with the parsed JSON body and ctx. Its return
|
|
14
|
+
* value is JSON-serialized and sent back as the HTTP response body
|
|
15
|
+
* (status 200). Throws produce status 500 with `{ error: message }`.
|
|
16
|
+
*
|
|
17
|
+
* Server port: `process.env.THREAD_PHASE_HTTP_PORT` if set, else 7777.
|
|
18
|
+
*/
|
|
19
|
+
import { type Server } from 'node:http';
|
|
20
|
+
import type { Trigger, TriggerEvent } from '../triggers/types.js';
|
|
21
|
+
import type { ExtensionRegisterFn, HelperHandler } from './types.js';
|
|
22
|
+
export interface HookSpec {
|
|
23
|
+
/** URL path the webhook listens on. Exact match. */
|
|
24
|
+
path: string;
|
|
25
|
+
/** HTTP method. Only 'POST' is supported in v3.0.0. */
|
|
26
|
+
method?: 'POST';
|
|
27
|
+
}
|
|
28
|
+
export interface HookOptions {
|
|
29
|
+
/** Pipeline + trigger base name. Defaults to the calling file's basename. */
|
|
30
|
+
name?: string;
|
|
31
|
+
/** Free-form description for `thread-phase list`. */
|
|
32
|
+
description?: string;
|
|
33
|
+
}
|
|
34
|
+
/** Test-only: tear down the shared server so each test starts clean. */
|
|
35
|
+
export declare function _resetHttpServerForTests(): void;
|
|
36
|
+
export declare class HttpTrigger implements Trigger<unknown> {
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly path: string;
|
|
39
|
+
readonly method: 'POST';
|
|
40
|
+
/** @internal — exposed for tests asserting the shared-server invariant. */
|
|
41
|
+
readonly _server: Server;
|
|
42
|
+
private seq;
|
|
43
|
+
private stopped;
|
|
44
|
+
private queued;
|
|
45
|
+
private waiter;
|
|
46
|
+
private readonly shared;
|
|
47
|
+
/** Map event.id → pending response so the handler's return value can be sent back. */
|
|
48
|
+
private readonly pendingById;
|
|
49
|
+
/** Map event.id → user handler's return value (so dispatch can resolve sync). */
|
|
50
|
+
private readonly resultsById;
|
|
51
|
+
constructor(opts: {
|
|
52
|
+
name: string;
|
|
53
|
+
path: string;
|
|
54
|
+
method: 'POST';
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* Called by the shared HTTP handler. Enqueues a new event and returns
|
|
58
|
+
* a promise that resolves with the user handler's return value.
|
|
59
|
+
*/
|
|
60
|
+
dispatch(body: unknown): Promise<unknown>;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the pending HTTP response for a given event id. Called by the
|
|
63
|
+
* helper-generated Phase once the user handler has returned.
|
|
64
|
+
*/
|
|
65
|
+
resolveResponse(eventId: number, value: unknown): void;
|
|
66
|
+
/** Reject the pending HTTP response for a given event id. */
|
|
67
|
+
rejectResponse(eventId: number, err: Error): void;
|
|
68
|
+
start(): AsyncGenerator<TriggerEvent<unknown>, void>;
|
|
69
|
+
stop(): Promise<void>;
|
|
70
|
+
private ensureServerStarted;
|
|
71
|
+
}
|
|
72
|
+
export declare function hook<TBody = unknown, TResult = unknown>(spec: HookSpec, handler: HelperHandler<TBody, TResult>, options?: HookOptions): ExtensionRegisterFn;
|
|
73
|
+
//# sourceMappingURL=hook.d.ts.map
|