@geminixiang/mikan 0.5.2 → 1.0.0-beta.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/CHANGELOG.md +38 -0
- package/README.md +3 -3
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/shared.js +2 -2
- package/dist/adapters/shared.js.map +1 -1
- package/dist/agent.d.ts +11 -2
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +211 -119
- package/dist/agent.js.map +1 -1
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +1 -5
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/auto-reply.d.ts +3 -0
- package/dist/commands/auto-reply.d.ts.map +1 -1
- package/dist/commands/auto-reply.js +3 -0
- package/dist/commands/auto-reply.js.map +1 -1
- package/dist/commands/model.d.ts +2 -3
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +1 -4
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +1 -5
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/registry.js +2 -5
- package/dist/commands/registry.js.map +1 -1
- package/dist/commands/types.d.ts +6 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/config.d.ts +42 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +93 -7
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts +0 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +1 -6
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +9 -6
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/auth.d.ts +16 -0
- package/dist/harness/auth.d.ts.map +1 -0
- package/dist/harness/auth.js +86 -0
- package/dist/harness/auth.js.map +1 -0
- package/dist/harness/extensions/loader.d.ts +43 -0
- package/dist/harness/extensions/loader.d.ts.map +1 -0
- package/dist/harness/extensions/loader.js +325 -0
- package/dist/harness/extensions/loader.js.map +1 -0
- package/dist/harness/extensions/registry.d.ts +23 -0
- package/dist/harness/extensions/registry.d.ts.map +1 -0
- package/dist/harness/extensions/registry.js +45 -0
- package/dist/harness/extensions/registry.js.map +1 -0
- package/dist/harness/extensions/types.d.ts +225 -0
- package/dist/harness/extensions/types.d.ts.map +1 -0
- package/dist/harness/extensions/types.js +2 -0
- package/dist/harness/extensions/types.js.map +1 -0
- package/dist/harness/http.d.ts +16 -0
- package/dist/harness/http.d.ts.map +1 -0
- package/dist/harness/http.js +71 -0
- package/dist/harness/http.js.map +1 -0
- package/dist/harness/index.d.ts +19 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +18 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/models.d.ts +34 -0
- package/dist/harness/models.d.ts.map +1 -0
- package/dist/harness/models.js +248 -0
- package/dist/harness/models.js.map +1 -0
- package/dist/harness/runner.d.ts +124 -0
- package/dist/harness/runner.d.ts.map +1 -0
- package/dist/harness/runner.js +436 -0
- package/dist/harness/runner.js.map +1 -0
- package/dist/harness/session-store.d.ts +77 -0
- package/dist/harness/session-store.d.ts.map +1 -0
- package/dist/harness/session-store.js +248 -0
- package/dist/harness/session-store.js.map +1 -0
- package/dist/harness/settings.d.ts +47 -0
- package/dist/harness/settings.d.ts.map +1 -0
- package/dist/harness/settings.js +29 -0
- package/dist/harness/settings.js.map +1 -0
- package/dist/harness/skills.d.ts +54 -0
- package/dist/harness/skills.d.ts.map +1 -0
- package/dist/harness/skills.js +203 -0
- package/dist/harness/skills.js.map +1 -0
- package/dist/harness/types.d.ts +32 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +11 -14
- package/dist/log.js.map +1 -1
- package/dist/main.js +67 -8
- package/dist/main.js.map +1 -1
- package/dist/model-registry.d.ts +2 -2
- package/dist/model-registry.d.ts.map +1 -1
- package/dist/model-registry.js.map +1 -1
- package/dist/provisioner.d.ts +1 -4
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +3 -8
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/agent-run-controller.d.ts.map +1 -1
- package/dist/runtime/agent-run-controller.js +6 -3
- package/dist/runtime/agent-run-controller.js.map +1 -1
- package/dist/runtime/conversation-runtime.js +1 -1
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +4 -3
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/container.d.ts +1 -0
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +2 -2
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +0 -4
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/image.d.ts.map +1 -1
- package/dist/sandbox/image.js +0 -3
- package/dist/sandbox/image.js.map +1 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +4 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/types.d.ts +2 -2
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sessions/agent-memory-file-manager.d.ts.map +1 -1
- package/dist/sessions/agent-memory-file-manager.js +8 -8
- package/dist/sessions/agent-memory-file-manager.js.map +1 -1
- package/dist/sessions/metadata.js +2 -2
- package/dist/sessions/metadata.js.map +1 -1
- package/dist/sessions/store.d.ts +3 -3
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +17 -43
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +1 -1
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/store.d.ts +1 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +18 -11
- package/dist/store.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +1 -3
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +7 -74
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +1 -3
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +3 -7
- package/dist/tools/write.js.map +1 -1
- package/dist/trigger.d.ts +2 -0
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js +7 -13
- package/dist/trigger.js.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +7 -1
- package/dist/vault/index.js.map +1 -1
- package/dist/web/admin/portal.d.ts +1 -1
- package/dist/web/admin/portal.d.ts.map +1 -1
- package/dist/web/admin/portal.js +394 -73
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/admin/provider-models.d.ts +2 -2
- package/dist/web/admin/provider-models.d.ts.map +1 -1
- package/dist/web/admin/provider-models.js +4 -2
- package/dist/web/admin/provider-models.js.map +1 -1
- package/dist/web/admin/store.d.ts.map +1 -1
- package/dist/web/admin/store.js +3 -12
- package/dist/web/admin/store.js.map +1 -1
- package/dist/web/admin/types.d.ts +7 -5
- package/dist/web/admin/types.d.ts.map +1 -1
- package/dist/web/admin/types.js.map +1 -1
- package/dist/web/login/oauth.d.ts.map +1 -1
- package/dist/web/login/oauth.js +41 -23
- package/dist/web/login/oauth.js.map +1 -1
- package/dist/web/login/store.d.ts +0 -4
- package/dist/web/login/store.d.ts.map +1 -1
- package/dist/web/login/store.js +3 -16
- package/dist/web/login/store.js.map +1 -1
- package/dist/web/server.js +2 -2
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-view/portal.d.ts.map +1 -1
- package/dist/web/session-view/portal.js +5 -16
- package/dist/web/session-view/portal.js.map +1 -1
- package/dist/web/session-view/service.js +7 -13
- package/dist/web/session-view/service.js.map +1 -1
- package/dist/web/session-view/store.d.ts.map +1 -1
- package/dist/web/session-view/store.js +2 -7
- package/dist/web/session-view/store.js.map +1 -1
- package/dist/web/token-store.d.ts +2 -8
- package/dist/web/token-store.d.ts.map +1 -1
- package/dist/web/token-store.js +11 -12
- package/dist/web/token-store.js.map +1 -1
- package/package.json +6 -3
- package/dist/context.d.ts +0 -2
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -2
- package/dist/context.js.map +0 -1
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MikanAgentSession — mikan's agent run loop.
|
|
3
|
+
*
|
|
4
|
+
* Owns a pi-agent-core `Agent` and layers on the behaviors mikan needs for
|
|
5
|
+
* long-lived chat conversations:
|
|
6
|
+
*
|
|
7
|
+
* - message persistence into the conversation's {@link SessionStore}
|
|
8
|
+
* - automatic context compaction (threshold and overflow recovery), using
|
|
9
|
+
* pi-agent-core's compaction pipeline over the session tree
|
|
10
|
+
* - automatic retry with exponential backoff on transient provider errors
|
|
11
|
+
* - extension hook dispatch ({@link ExtensionRegistry})
|
|
12
|
+
*
|
|
13
|
+
* Events mirror the agent's lifecycle events plus `compaction_start/_end`
|
|
14
|
+
* and `auto_retry_start/_end`, preserving the event surface mikan's
|
|
15
|
+
* platform adapters already render.
|
|
16
|
+
*/
|
|
17
|
+
import { Agent, calculateContextTokens, compact, convertToLlm, estimateContextTokens, getOrThrow, prepareCompaction, shouldCompact, } from "@earendil-works/pi-agent-core";
|
|
18
|
+
import { isContextOverflow, } from "@earendil-works/pi-ai";
|
|
19
|
+
import * as log from "../log.js";
|
|
20
|
+
import { resolveHarnessSettings } from "./settings.js";
|
|
21
|
+
// Transient provider failures worth retrying: overload/rate-limit responses,
|
|
22
|
+
// 5xx statuses, and network/stream interruptions.
|
|
23
|
+
const RETRYABLE_ERROR_PATTERN = /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i;
|
|
24
|
+
function sleep(ms, signal) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
if (signal.aborted) {
|
|
27
|
+
reject(new Error("aborted"));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
signal.removeEventListener("abort", onAbort);
|
|
32
|
+
resolve();
|
|
33
|
+
}, ms);
|
|
34
|
+
const onAbort = () => {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
reject(new Error("aborted"));
|
|
37
|
+
};
|
|
38
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export class MikanAgentSession {
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.listeners = new Set();
|
|
44
|
+
this.retryAttempt = 0;
|
|
45
|
+
this.overflowRecoveryAttempted = false;
|
|
46
|
+
this.tally = { tokens: 0, costUsd: 0, llmCalls: 0, startedAt: 0 };
|
|
47
|
+
this.runBudget = {};
|
|
48
|
+
this.models = options.models;
|
|
49
|
+
this.sessionStore = options.sessionStore;
|
|
50
|
+
this.settings = resolveHarnessSettings(options.settings);
|
|
51
|
+
this.extensions = options.extensions;
|
|
52
|
+
const tools = [...options.tools, ...(options.extensions?.getContributedTools() ?? [])];
|
|
53
|
+
this.agent = new Agent({
|
|
54
|
+
initialState: {
|
|
55
|
+
systemPrompt: options.systemPrompt,
|
|
56
|
+
model: options.model,
|
|
57
|
+
thinkingLevel: options.thinkingLevel,
|
|
58
|
+
tools,
|
|
59
|
+
},
|
|
60
|
+
convertToLlm,
|
|
61
|
+
streamFn: (model, context, streamOptions) => this.models.models.streamSimple(model, context, streamOptions),
|
|
62
|
+
beforeToolCall: async ({ toolCall, args }) => {
|
|
63
|
+
if (!this.extensions?.hasHandlers("tool_call"))
|
|
64
|
+
return undefined;
|
|
65
|
+
const result = await this.extensions.emit("tool_call", {
|
|
66
|
+
toolCallId: toolCall.id,
|
|
67
|
+
toolName: toolCall.name,
|
|
68
|
+
args,
|
|
69
|
+
});
|
|
70
|
+
return result ?? undefined;
|
|
71
|
+
},
|
|
72
|
+
afterToolCall: async ({ toolCall, args, result, isError }) => {
|
|
73
|
+
if (this.extensions?.hasHandlers("tool_result")) {
|
|
74
|
+
await this.extensions.emit("tool_result", {
|
|
75
|
+
toolCallId: toolCall.id,
|
|
76
|
+
toolName: toolCall.name,
|
|
77
|
+
args,
|
|
78
|
+
content: result.content,
|
|
79
|
+
isError,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
this.agent.subscribe(async (event) => {
|
|
86
|
+
await this.handleAgentEvent(event);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/** Conversation transcript currently held by the agent. */
|
|
90
|
+
get messages() {
|
|
91
|
+
return this.agent.state.messages;
|
|
92
|
+
}
|
|
93
|
+
get model() {
|
|
94
|
+
return this.agent.state.model;
|
|
95
|
+
}
|
|
96
|
+
subscribe(listener) {
|
|
97
|
+
this.listeners.add(listener);
|
|
98
|
+
return () => this.listeners.delete(listener);
|
|
99
|
+
}
|
|
100
|
+
/** Replace the agent transcript from the persisted session tree. */
|
|
101
|
+
reloadFromSession() {
|
|
102
|
+
const context = this.sessionStore.buildSessionContext();
|
|
103
|
+
if (context.messages.length > 0) {
|
|
104
|
+
this.agent.state.messages = context.messages;
|
|
105
|
+
}
|
|
106
|
+
return context.messages.length;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Send a user prompt and run the agent loop to completion, including
|
|
110
|
+
* automatic retries and compaction. Resolves when the turn (and any
|
|
111
|
+
* recovery continuations) settles.
|
|
112
|
+
*
|
|
113
|
+
* @param options.budget Per-run resource ceilings that override the session
|
|
114
|
+
* defaults. Autonomous (event / trigger) runs should pass a budget so a
|
|
115
|
+
* runaway loop is stopped even with no human watching.
|
|
116
|
+
*/
|
|
117
|
+
async prompt(text, options) {
|
|
118
|
+
if (this.agent.state.isStreaming) {
|
|
119
|
+
throw new Error("Agent is already processing a prompt");
|
|
120
|
+
}
|
|
121
|
+
const model = this.agent.state.model;
|
|
122
|
+
await this.ensureAuthConfigured(model);
|
|
123
|
+
this.retryAttempt = 0;
|
|
124
|
+
this.overflowRecoveryAttempted = false;
|
|
125
|
+
this.runBudget = { ...this.settings.budget, ...options?.budget };
|
|
126
|
+
this.budgetExceededReason = undefined;
|
|
127
|
+
this.tally = { tokens: 0, costUsd: 0, llmCalls: 0, startedAt: Date.now() };
|
|
128
|
+
// A previous turn may have ended over the threshold (for example after an
|
|
129
|
+
// abort); compact before adding new context on top.
|
|
130
|
+
const lastAssistant = this.findLastAssistantMessage();
|
|
131
|
+
if (lastAssistant && lastAssistant.stopReason !== "error") {
|
|
132
|
+
await this.checkThresholdCompaction(lastAssistant);
|
|
133
|
+
}
|
|
134
|
+
let systemPrompt = this.agent.state.systemPrompt;
|
|
135
|
+
if (this.extensions?.hasHandlers("before_agent_start")) {
|
|
136
|
+
const result = await this.extensions.emit("before_agent_start", {
|
|
137
|
+
prompt: text,
|
|
138
|
+
images: options?.images,
|
|
139
|
+
systemPrompt,
|
|
140
|
+
});
|
|
141
|
+
if (result?.systemPrompt) {
|
|
142
|
+
systemPrompt = result.systemPrompt;
|
|
143
|
+
this.agent.state.systemPrompt = systemPrompt;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const userMessage = {
|
|
147
|
+
role: "user",
|
|
148
|
+
content: [{ type: "text", text }, ...(options?.images ?? [])],
|
|
149
|
+
timestamp: Date.now(),
|
|
150
|
+
};
|
|
151
|
+
await this.agent.prompt([userMessage]);
|
|
152
|
+
while (await this.handlePostRun()) {
|
|
153
|
+
await this.agent.continue();
|
|
154
|
+
}
|
|
155
|
+
if (this.extensions?.hasHandlers("turn_end")) {
|
|
156
|
+
await this.extensions.emit("turn_end", { messages: this.agent.state.messages });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Abort the active run, pending retry backoff, and in-flight compaction. */
|
|
160
|
+
abort() {
|
|
161
|
+
this.retryAbortController?.abort();
|
|
162
|
+
this.compactionAbortController?.abort();
|
|
163
|
+
this.agent.abort();
|
|
164
|
+
}
|
|
165
|
+
async ensureAuthConfigured(model) {
|
|
166
|
+
const auth = await this.models.getAuth(model);
|
|
167
|
+
if (!auth) {
|
|
168
|
+
throw new Error(`No credentials for provider "${model.provider}". Set the provider API key environment variable or add it to auth.json.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async emit(event) {
|
|
172
|
+
for (const listener of this.listeners) {
|
|
173
|
+
try {
|
|
174
|
+
await listener(event);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
log.logWarning("Harness event listener failed", err instanceof Error ? err.message : String(err));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async handleAgentEvent(event) {
|
|
182
|
+
await this.emit(event);
|
|
183
|
+
if (event.type !== "message_end")
|
|
184
|
+
return;
|
|
185
|
+
const message = event.message;
|
|
186
|
+
if (message.role === "user" || message.role === "assistant" || message.role === "toolResult") {
|
|
187
|
+
this.sessionStore.appendMessage(message);
|
|
188
|
+
}
|
|
189
|
+
else if (message.role === "custom") {
|
|
190
|
+
const custom = message;
|
|
191
|
+
this.sessionStore.appendCustomMessageEntry(custom.customType, custom.content, custom.display, custom.details);
|
|
192
|
+
}
|
|
193
|
+
if (this.extensions?.hasHandlers("message_end")) {
|
|
194
|
+
await this.extensions.emit("message_end", { message });
|
|
195
|
+
}
|
|
196
|
+
if (message.role === "assistant") {
|
|
197
|
+
this.recordUsage(message);
|
|
198
|
+
await this.enforceBudget();
|
|
199
|
+
if (message.stopReason !== "error") {
|
|
200
|
+
this.overflowRecoveryAttempted = false;
|
|
201
|
+
if (this.retryAttempt > 0) {
|
|
202
|
+
await this.emit({ type: "auto_retry_end", success: true, attempt: this.retryAttempt });
|
|
203
|
+
this.retryAttempt = 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** Fold one assistant turn's usage into the running per-run tally. */
|
|
209
|
+
recordUsage(message) {
|
|
210
|
+
this.tally.llmCalls += 1;
|
|
211
|
+
const usage = message.usage;
|
|
212
|
+
if (!usage)
|
|
213
|
+
return;
|
|
214
|
+
this.tally.tokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
215
|
+
this.tally.costUsd += usage.cost?.total ?? 0;
|
|
216
|
+
}
|
|
217
|
+
/** Compare the tally against the run budget; abort the run when a cap is exceeded. */
|
|
218
|
+
async enforceBudget() {
|
|
219
|
+
if (this.budgetExceededReason)
|
|
220
|
+
return;
|
|
221
|
+
const reason = this.overBudgetReason();
|
|
222
|
+
if (!reason)
|
|
223
|
+
return;
|
|
224
|
+
this.budgetExceededReason = reason;
|
|
225
|
+
await this.emit({
|
|
226
|
+
type: "budget_exceeded",
|
|
227
|
+
reason,
|
|
228
|
+
tokens: this.tally.tokens,
|
|
229
|
+
costUsd: this.tally.costUsd,
|
|
230
|
+
llmCalls: this.tally.llmCalls,
|
|
231
|
+
durationMs: Date.now() - this.tally.startedAt,
|
|
232
|
+
});
|
|
233
|
+
log.logWarning("Run budget exceeded — aborting", reason);
|
|
234
|
+
this.agent.abort();
|
|
235
|
+
}
|
|
236
|
+
overBudgetReason() {
|
|
237
|
+
const { maxTokens, maxCostUsd, maxDurationMs, maxLlmCalls } = this.runBudget;
|
|
238
|
+
if (maxLlmCalls !== undefined && this.tally.llmCalls >= maxLlmCalls) {
|
|
239
|
+
return `${this.tally.llmCalls} LLM calls >= ${maxLlmCalls} limit`;
|
|
240
|
+
}
|
|
241
|
+
if (maxTokens !== undefined && this.tally.tokens >= maxTokens) {
|
|
242
|
+
return `${this.tally.tokens} tokens >= ${maxTokens} limit`;
|
|
243
|
+
}
|
|
244
|
+
if (maxCostUsd !== undefined && this.tally.costUsd >= maxCostUsd) {
|
|
245
|
+
return `cost ${this.tally.costUsd.toFixed(2)} USD >= ${maxCostUsd} USD limit`;
|
|
246
|
+
}
|
|
247
|
+
if (maxDurationMs !== undefined && Date.now() - this.tally.startedAt >= maxDurationMs) {
|
|
248
|
+
return `${Date.now() - this.tally.startedAt}ms >= ${maxDurationMs}ms limit`;
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
findLastAssistantMessage() {
|
|
253
|
+
const messages = this.agent.state.messages;
|
|
254
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
255
|
+
const message = messages[i];
|
|
256
|
+
if (message.role === "assistant")
|
|
257
|
+
return message;
|
|
258
|
+
}
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
/** Decide what to do after a run settles. Returns true when the agent should continue. */
|
|
262
|
+
async handlePostRun() {
|
|
263
|
+
// A run stopped for going over budget must not be retried or compacted back
|
|
264
|
+
// into another continuation — that would defeat the circuit breaker.
|
|
265
|
+
if (this.budgetExceededReason)
|
|
266
|
+
return false;
|
|
267
|
+
const lastAssistant = this.findLastAssistantMessage();
|
|
268
|
+
if (!lastAssistant)
|
|
269
|
+
return false;
|
|
270
|
+
if (lastAssistant.stopReason === "error") {
|
|
271
|
+
const contextWindow = this.agent.state.model.contextWindow || 0;
|
|
272
|
+
if (isContextOverflow(lastAssistant, contextWindow)) {
|
|
273
|
+
return this.handleOverflow();
|
|
274
|
+
}
|
|
275
|
+
if (this.isRetryableError(lastAssistant)) {
|
|
276
|
+
return this.prepareRetry(lastAssistant);
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
return this.checkThresholdCompaction(lastAssistant);
|
|
281
|
+
}
|
|
282
|
+
async handleOverflow() {
|
|
283
|
+
if (this.overflowRecoveryAttempted) {
|
|
284
|
+
await this.emit({
|
|
285
|
+
type: "compaction_end",
|
|
286
|
+
reason: "overflow",
|
|
287
|
+
aborted: false,
|
|
288
|
+
errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try /new or a larger-context model.",
|
|
289
|
+
});
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
this.overflowRecoveryAttempted = true;
|
|
293
|
+
this.dropTrailingErrorMessage();
|
|
294
|
+
return this.runCompaction("overflow", true);
|
|
295
|
+
}
|
|
296
|
+
async checkThresholdCompaction(lastAssistant) {
|
|
297
|
+
const settings = this.settings.compaction;
|
|
298
|
+
if (!settings.enabled)
|
|
299
|
+
return false;
|
|
300
|
+
const contextWindow = this.agent.state.model.contextWindow || 0;
|
|
301
|
+
if (contextWindow <= 0)
|
|
302
|
+
return false;
|
|
303
|
+
let contextTokens = lastAssistant.usage ? calculateContextTokens(lastAssistant.usage) : 0;
|
|
304
|
+
if (contextTokens === 0) {
|
|
305
|
+
const estimate = estimateContextTokens(this.agent.state.messages);
|
|
306
|
+
if (estimate.lastUsageIndex === null)
|
|
307
|
+
return false;
|
|
308
|
+
contextTokens = estimate.tokens;
|
|
309
|
+
}
|
|
310
|
+
if (!shouldCompact(contextTokens, contextWindow, settings))
|
|
311
|
+
return false;
|
|
312
|
+
return this.runCompaction("threshold", false);
|
|
313
|
+
}
|
|
314
|
+
async runCompaction(reason, willRetry) {
|
|
315
|
+
let started = false;
|
|
316
|
+
try {
|
|
317
|
+
const pathEntries = this.sessionStore.getBranch();
|
|
318
|
+
const preparation = getOrThrow(prepareCompaction(pathEntries, this.settings.compaction));
|
|
319
|
+
if (!preparation)
|
|
320
|
+
return false;
|
|
321
|
+
await this.emit({ type: "compaction_start", reason });
|
|
322
|
+
started = true;
|
|
323
|
+
this.compactionAbortController = new AbortController();
|
|
324
|
+
const signal = this.compactionAbortController.signal;
|
|
325
|
+
const result = getOrThrow(await compact(preparation, this.models.models, this.agent.state.model, undefined, signal, this.agent.state.thinkingLevel));
|
|
326
|
+
if (signal.aborted) {
|
|
327
|
+
await this.emit({ type: "compaction_end", reason, aborted: true });
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
const entryId = this.sessionStore.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details);
|
|
331
|
+
const context = this.sessionStore.buildSessionContext();
|
|
332
|
+
this.agent.state.messages = context.messages;
|
|
333
|
+
if (this.extensions?.hasHandlers("session_compact")) {
|
|
334
|
+
const entry = this.sessionStore.getEntry(entryId);
|
|
335
|
+
if (entry?.type === "compaction") {
|
|
336
|
+
await this.extensions.emit("session_compact", {
|
|
337
|
+
entry: entry,
|
|
338
|
+
reason,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
await this.emit({
|
|
343
|
+
type: "compaction_end",
|
|
344
|
+
reason,
|
|
345
|
+
result: {
|
|
346
|
+
summary: result.summary,
|
|
347
|
+
firstKeptEntryId: result.firstKeptEntryId,
|
|
348
|
+
tokensBefore: result.tokensBefore,
|
|
349
|
+
},
|
|
350
|
+
aborted: false,
|
|
351
|
+
});
|
|
352
|
+
if (willRetry) {
|
|
353
|
+
this.dropTrailingErrorMessage();
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
return this.agent.hasQueuedMessages();
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
360
|
+
if (started) {
|
|
361
|
+
await this.emit({
|
|
362
|
+
type: "compaction_end",
|
|
363
|
+
reason,
|
|
364
|
+
aborted: false,
|
|
365
|
+
errorMessage: reason === "overflow"
|
|
366
|
+
? `Context overflow recovery failed: ${errorMessage}`
|
|
367
|
+
: `Auto-compaction failed: ${errorMessage}`,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
log.logWarning("Compaction preparation failed", errorMessage);
|
|
372
|
+
}
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
finally {
|
|
376
|
+
this.compactionAbortController = undefined;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
isRetryableError(message) {
|
|
380
|
+
if (message.stopReason !== "error" || !message.errorMessage)
|
|
381
|
+
return false;
|
|
382
|
+
return RETRYABLE_ERROR_PATTERN.test(message.errorMessage);
|
|
383
|
+
}
|
|
384
|
+
async prepareRetry(message) {
|
|
385
|
+
const settings = this.settings.retry;
|
|
386
|
+
if (!settings.enabled)
|
|
387
|
+
return false;
|
|
388
|
+
this.retryAttempt++;
|
|
389
|
+
if (this.retryAttempt > settings.maxRetries) {
|
|
390
|
+
this.retryAttempt--;
|
|
391
|
+
await this.emit({
|
|
392
|
+
type: "auto_retry_end",
|
|
393
|
+
success: false,
|
|
394
|
+
attempt: this.retryAttempt,
|
|
395
|
+
finalError: message.errorMessage,
|
|
396
|
+
});
|
|
397
|
+
this.retryAttempt = 0;
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
const delayMs = settings.baseDelayMs * 2 ** (this.retryAttempt - 1);
|
|
401
|
+
await this.emit({
|
|
402
|
+
type: "auto_retry_start",
|
|
403
|
+
attempt: this.retryAttempt,
|
|
404
|
+
maxAttempts: settings.maxRetries,
|
|
405
|
+
delayMs,
|
|
406
|
+
errorMessage: message.errorMessage || "Unknown error",
|
|
407
|
+
});
|
|
408
|
+
this.dropTrailingErrorMessage();
|
|
409
|
+
this.retryAbortController = new AbortController();
|
|
410
|
+
try {
|
|
411
|
+
await sleep(delayMs, this.retryAbortController.signal);
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
const attempt = this.retryAttempt;
|
|
415
|
+
this.retryAttempt = 0;
|
|
416
|
+
await this.emit({
|
|
417
|
+
type: "auto_retry_end",
|
|
418
|
+
success: false,
|
|
419
|
+
attempt,
|
|
420
|
+
finalError: "Retry cancelled",
|
|
421
|
+
});
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
finally {
|
|
425
|
+
this.retryAbortController = undefined;
|
|
426
|
+
}
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
dropTrailingErrorMessage() {
|
|
430
|
+
const messages = this.agent.state.messages;
|
|
431
|
+
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
432
|
+
this.agent.state.messages = messages.slice(0, -1);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/harness/runner.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MikanAgentSession — mikan's agent run loop.\n *\n * Owns a pi-agent-core `Agent` and layers on the behaviors mikan needs for\n * long-lived chat conversations:\n *\n * - message persistence into the conversation's {@link SessionStore}\n * - automatic context compaction (threshold and overflow recovery), using\n * pi-agent-core's compaction pipeline over the session tree\n * - automatic retry with exponential backoff on transient provider errors\n * - extension hook dispatch ({@link ExtensionRegistry})\n *\n * Events mirror the agent's lifecycle events plus `compaction_start/_end`\n * and `auto_retry_start/_end`, preserving the event surface mikan's\n * platform adapters already render.\n */\nimport {\n Agent,\n calculateContextTokens,\n compact,\n convertToLlm,\n estimateContextTokens,\n getOrThrow,\n prepareCompaction,\n shouldCompact,\n type AgentEvent,\n type AgentMessage,\n type AgentTool,\n type CustomMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n isContextOverflow,\n type Api,\n type AssistantMessage,\n type ImageContent,\n type Model,\n} from \"@earendil-works/pi-ai\";\nimport * as log from \"../log.js\";\nimport type { ExtensionRegistry } from \"./extensions/registry.js\";\nimport type { MikanModels } from \"./models.js\";\nimport { resolveHarnessSettings, type BudgetSettings, type HarnessSettings } from \"./settings.js\";\nimport type { SessionStore } from \"./session-store.js\";\nimport type { CompactionEntry } from \"./types.js\";\n\nexport type CompactionReason = \"threshold\" | \"overflow\" | \"manual\";\n\n/** Running resource tally for the current `prompt()` call, matched against the budget. */\ninterface RunTally {\n tokens: number;\n costUsd: number;\n llmCalls: number;\n startedAt: number;\n}\n\ninterface CompactionResultSummary {\n summary: string;\n firstKeptEntryId: string;\n tokensBefore: number;\n}\n\nexport type HarnessEvent =\n | AgentEvent\n | { type: \"compaction_start\"; reason: CompactionReason }\n | {\n type: \"compaction_end\";\n reason: CompactionReason;\n result?: CompactionResultSummary;\n aborted: boolean;\n errorMessage?: string;\n }\n | {\n type: \"auto_retry_start\";\n attempt: number;\n maxAttempts: number;\n delayMs: number;\n errorMessage: string;\n }\n | { type: \"auto_retry_end\"; success: boolean; attempt: number; finalError?: string }\n | {\n type: \"budget_exceeded\";\n /** Which cap was hit, e.g. \"cost 2.01 USD > 2 USD limit\". */\n reason: string;\n tokens: number;\n costUsd: number;\n llmCalls: number;\n durationMs: number;\n };\n\nexport type HarnessEventListener = (event: HarnessEvent) => void | Promise<void>;\n\n// Transient provider failures worth retrying: overload/rate-limit responses,\n// 5xx statuses, and network/stream interruptions.\nconst RETRYABLE_ERROR_PATTERN =\n /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i;\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error(\"aborted\"));\n return;\n }\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error(\"aborted\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nexport interface MikanAgentSessionOptions {\n systemPrompt: string;\n model: Model<Api>;\n thinkingLevel: ThinkingLevel;\n tools: AgentTool[];\n models: MikanModels;\n sessionStore: SessionStore;\n settings?: Parameters<typeof resolveHarnessSettings>[0];\n extensions?: ExtensionRegistry;\n}\n\nexport class MikanAgentSession {\n readonly agent: Agent;\n readonly sessionStore: SessionStore;\n private readonly models: MikanModels;\n private readonly settings: HarnessSettings;\n private readonly extensions: ExtensionRegistry | undefined;\n private listeners = new Set<HarnessEventListener>();\n private retryAttempt = 0;\n private overflowRecoveryAttempted = false;\n private retryAbortController: AbortController | undefined;\n private compactionAbortController: AbortController | undefined;\n private tally: RunTally = { tokens: 0, costUsd: 0, llmCalls: 0, startedAt: 0 };\n private runBudget: BudgetSettings = {};\n private budgetExceededReason: string | undefined;\n\n constructor(options: MikanAgentSessionOptions) {\n this.models = options.models;\n this.sessionStore = options.sessionStore;\n this.settings = resolveHarnessSettings(options.settings);\n this.extensions = options.extensions;\n\n const tools = [...options.tools, ...(options.extensions?.getContributedTools() ?? [])];\n this.agent = new Agent({\n initialState: {\n systemPrompt: options.systemPrompt,\n model: options.model,\n thinkingLevel: options.thinkingLevel,\n tools,\n },\n convertToLlm,\n streamFn: (model, context, streamOptions) =>\n this.models.models.streamSimple(model, context, streamOptions),\n beforeToolCall: async ({ toolCall, args }) => {\n if (!this.extensions?.hasHandlers(\"tool_call\")) return undefined;\n const result = await this.extensions.emit(\"tool_call\", {\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n args,\n });\n return result ?? undefined;\n },\n afterToolCall: async ({ toolCall, args, result, isError }) => {\n if (this.extensions?.hasHandlers(\"tool_result\")) {\n await this.extensions.emit(\"tool_result\", {\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n args,\n content: result.content,\n isError,\n });\n }\n return undefined;\n },\n });\n\n this.agent.subscribe(async (event) => {\n await this.handleAgentEvent(event);\n });\n }\n\n /** Conversation transcript currently held by the agent. */\n get messages(): AgentMessage[] {\n return this.agent.state.messages;\n }\n\n get model(): Model<Api> {\n return this.agent.state.model;\n }\n\n subscribe(listener: HarnessEventListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** Replace the agent transcript from the persisted session tree. */\n reloadFromSession(): number {\n const context = this.sessionStore.buildSessionContext();\n if (context.messages.length > 0) {\n this.agent.state.messages = context.messages;\n }\n return context.messages.length;\n }\n\n /**\n * Send a user prompt and run the agent loop to completion, including\n * automatic retries and compaction. Resolves when the turn (and any\n * recovery continuations) settles.\n *\n * @param options.budget Per-run resource ceilings that override the session\n * defaults. Autonomous (event / trigger) runs should pass a budget so a\n * runaway loop is stopped even with no human watching.\n */\n async prompt(\n text: string,\n options?: { images?: ImageContent[]; budget?: BudgetSettings },\n ): Promise<void> {\n if (this.agent.state.isStreaming) {\n throw new Error(\"Agent is already processing a prompt\");\n }\n\n const model = this.agent.state.model;\n await this.ensureAuthConfigured(model);\n\n this.retryAttempt = 0;\n this.overflowRecoveryAttempted = false;\n this.runBudget = { ...this.settings.budget, ...options?.budget };\n this.budgetExceededReason = undefined;\n this.tally = { tokens: 0, costUsd: 0, llmCalls: 0, startedAt: Date.now() };\n\n // A previous turn may have ended over the threshold (for example after an\n // abort); compact before adding new context on top.\n const lastAssistant = this.findLastAssistantMessage();\n if (lastAssistant && lastAssistant.stopReason !== \"error\") {\n await this.checkThresholdCompaction(lastAssistant);\n }\n\n let systemPrompt = this.agent.state.systemPrompt;\n if (this.extensions?.hasHandlers(\"before_agent_start\")) {\n const result = await this.extensions.emit(\"before_agent_start\", {\n prompt: text,\n images: options?.images,\n systemPrompt,\n });\n if (result?.systemPrompt) {\n systemPrompt = result.systemPrompt;\n this.agent.state.systemPrompt = systemPrompt;\n }\n }\n\n const userMessage: AgentMessage = {\n role: \"user\",\n content: [{ type: \"text\", text }, ...(options?.images ?? [])],\n timestamp: Date.now(),\n };\n\n await this.agent.prompt([userMessage]);\n while (await this.handlePostRun()) {\n await this.agent.continue();\n }\n\n if (this.extensions?.hasHandlers(\"turn_end\")) {\n await this.extensions.emit(\"turn_end\", { messages: this.agent.state.messages });\n }\n }\n\n /** Abort the active run, pending retry backoff, and in-flight compaction. */\n abort(): void {\n this.retryAbortController?.abort();\n this.compactionAbortController?.abort();\n this.agent.abort();\n }\n\n private async ensureAuthConfigured(model: Model<Api>): Promise<void> {\n const auth = await this.models.getAuth(model);\n if (!auth) {\n throw new Error(\n `No credentials for provider \"${model.provider}\". Set the provider API key environment variable or add it to auth.json.`,\n );\n }\n }\n\n private async emit(event: HarnessEvent): Promise<void> {\n for (const listener of this.listeners) {\n try {\n await listener(event);\n } catch (err) {\n log.logWarning(\n \"Harness event listener failed\",\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n private async handleAgentEvent(event: AgentEvent): Promise<void> {\n await this.emit(event);\n\n if (event.type !== \"message_end\") return;\n const message = event.message;\n\n if (message.role === \"user\" || message.role === \"assistant\" || message.role === \"toolResult\") {\n this.sessionStore.appendMessage(message);\n } else if (message.role === \"custom\") {\n const custom = message as CustomMessage;\n this.sessionStore.appendCustomMessageEntry(\n custom.customType,\n custom.content,\n custom.display,\n custom.details,\n );\n }\n\n if (this.extensions?.hasHandlers(\"message_end\")) {\n await this.extensions.emit(\"message_end\", { message });\n }\n\n if (message.role === \"assistant\") {\n this.recordUsage(message);\n await this.enforceBudget();\n if (message.stopReason !== \"error\") {\n this.overflowRecoveryAttempted = false;\n if (this.retryAttempt > 0) {\n await this.emit({ type: \"auto_retry_end\", success: true, attempt: this.retryAttempt });\n this.retryAttempt = 0;\n }\n }\n }\n }\n\n /** Fold one assistant turn's usage into the running per-run tally. */\n private recordUsage(message: AssistantMessage): void {\n this.tally.llmCalls += 1;\n const usage = message.usage;\n if (!usage) return;\n this.tally.tokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n this.tally.costUsd += usage.cost?.total ?? 0;\n }\n\n /** Compare the tally against the run budget; abort the run when a cap is exceeded. */\n private async enforceBudget(): Promise<void> {\n if (this.budgetExceededReason) return;\n const reason = this.overBudgetReason();\n if (!reason) return;\n\n this.budgetExceededReason = reason;\n await this.emit({\n type: \"budget_exceeded\",\n reason,\n tokens: this.tally.tokens,\n costUsd: this.tally.costUsd,\n llmCalls: this.tally.llmCalls,\n durationMs: Date.now() - this.tally.startedAt,\n });\n log.logWarning(\"Run budget exceeded — aborting\", reason);\n this.agent.abort();\n }\n\n private overBudgetReason(): string | undefined {\n const { maxTokens, maxCostUsd, maxDurationMs, maxLlmCalls } = this.runBudget;\n if (maxLlmCalls !== undefined && this.tally.llmCalls >= maxLlmCalls) {\n return `${this.tally.llmCalls} LLM calls >= ${maxLlmCalls} limit`;\n }\n if (maxTokens !== undefined && this.tally.tokens >= maxTokens) {\n return `${this.tally.tokens} tokens >= ${maxTokens} limit`;\n }\n if (maxCostUsd !== undefined && this.tally.costUsd >= maxCostUsd) {\n return `cost ${this.tally.costUsd.toFixed(2)} USD >= ${maxCostUsd} USD limit`;\n }\n if (maxDurationMs !== undefined && Date.now() - this.tally.startedAt >= maxDurationMs) {\n return `${Date.now() - this.tally.startedAt}ms >= ${maxDurationMs}ms limit`;\n }\n return undefined;\n }\n\n private findLastAssistantMessage(): AssistantMessage | undefined {\n const messages = this.agent.state.messages;\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i];\n if (message.role === \"assistant\") return message;\n }\n return undefined;\n }\n\n /** Decide what to do after a run settles. Returns true when the agent should continue. */\n private async handlePostRun(): Promise<boolean> {\n // A run stopped for going over budget must not be retried or compacted back\n // into another continuation — that would defeat the circuit breaker.\n if (this.budgetExceededReason) return false;\n\n const lastAssistant = this.findLastAssistantMessage();\n if (!lastAssistant) return false;\n\n if (lastAssistant.stopReason === \"error\") {\n const contextWindow = this.agent.state.model.contextWindow || 0;\n if (isContextOverflow(lastAssistant, contextWindow)) {\n return this.handleOverflow();\n }\n if (this.isRetryableError(lastAssistant)) {\n return this.prepareRetry(lastAssistant);\n }\n return false;\n }\n\n return this.checkThresholdCompaction(lastAssistant);\n }\n\n private async handleOverflow(): Promise<boolean> {\n if (this.overflowRecoveryAttempted) {\n await this.emit({\n type: \"compaction_end\",\n reason: \"overflow\",\n aborted: false,\n errorMessage:\n \"Context overflow recovery failed after one compact-and-retry attempt. Try /new or a larger-context model.\",\n });\n return false;\n }\n this.overflowRecoveryAttempted = true;\n this.dropTrailingErrorMessage();\n return this.runCompaction(\"overflow\", true);\n }\n\n private async checkThresholdCompaction(lastAssistant: AssistantMessage): Promise<boolean> {\n const settings = this.settings.compaction;\n if (!settings.enabled) return false;\n const contextWindow = this.agent.state.model.contextWindow || 0;\n if (contextWindow <= 0) return false;\n\n let contextTokens = lastAssistant.usage ? calculateContextTokens(lastAssistant.usage) : 0;\n if (contextTokens === 0) {\n const estimate = estimateContextTokens(this.agent.state.messages);\n if (estimate.lastUsageIndex === null) return false;\n contextTokens = estimate.tokens;\n }\n\n if (!shouldCompact(contextTokens, contextWindow, settings)) return false;\n return this.runCompaction(\"threshold\", false);\n }\n\n private async runCompaction(reason: CompactionReason, willRetry: boolean): Promise<boolean> {\n let started = false;\n try {\n const pathEntries = this.sessionStore.getBranch();\n const preparation = getOrThrow(prepareCompaction(pathEntries, this.settings.compaction));\n if (!preparation) return false;\n\n await this.emit({ type: \"compaction_start\", reason });\n started = true;\n this.compactionAbortController = new AbortController();\n const signal = this.compactionAbortController.signal;\n\n const result = getOrThrow(\n await compact(\n preparation,\n this.models.models,\n this.agent.state.model,\n undefined,\n signal,\n this.agent.state.thinkingLevel,\n ),\n );\n if (signal.aborted) {\n await this.emit({ type: \"compaction_end\", reason, aborted: true });\n return false;\n }\n\n const entryId = this.sessionStore.appendCompaction(\n result.summary,\n result.firstKeptEntryId,\n result.tokensBefore,\n result.details,\n );\n const context = this.sessionStore.buildSessionContext();\n this.agent.state.messages = context.messages;\n\n if (this.extensions?.hasHandlers(\"session_compact\")) {\n const entry = this.sessionStore.getEntry(entryId);\n if (entry?.type === \"compaction\") {\n await this.extensions.emit(\"session_compact\", {\n entry: entry as CompactionEntry,\n reason,\n });\n }\n }\n\n await this.emit({\n type: \"compaction_end\",\n reason,\n result: {\n summary: result.summary,\n firstKeptEntryId: result.firstKeptEntryId,\n tokensBefore: result.tokensBefore,\n },\n aborted: false,\n });\n\n if (willRetry) {\n this.dropTrailingErrorMessage();\n return true;\n }\n return this.agent.hasQueuedMessages();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"compaction failed\";\n if (started) {\n await this.emit({\n type: \"compaction_end\",\n reason,\n aborted: false,\n errorMessage:\n reason === \"overflow\"\n ? `Context overflow recovery failed: ${errorMessage}`\n : `Auto-compaction failed: ${errorMessage}`,\n });\n } else {\n log.logWarning(\"Compaction preparation failed\", errorMessage);\n }\n return false;\n } finally {\n this.compactionAbortController = undefined;\n }\n }\n\n private isRetryableError(message: AssistantMessage): boolean {\n if (message.stopReason !== \"error\" || !message.errorMessage) return false;\n return RETRYABLE_ERROR_PATTERN.test(message.errorMessage);\n }\n\n private async prepareRetry(message: AssistantMessage): Promise<boolean> {\n const settings = this.settings.retry;\n if (!settings.enabled) return false;\n\n this.retryAttempt++;\n if (this.retryAttempt > settings.maxRetries) {\n this.retryAttempt--;\n await this.emit({\n type: \"auto_retry_end\",\n success: false,\n attempt: this.retryAttempt,\n finalError: message.errorMessage,\n });\n this.retryAttempt = 0;\n return false;\n }\n\n const delayMs = settings.baseDelayMs * 2 ** (this.retryAttempt - 1);\n await this.emit({\n type: \"auto_retry_start\",\n attempt: this.retryAttempt,\n maxAttempts: settings.maxRetries,\n delayMs,\n errorMessage: message.errorMessage || \"Unknown error\",\n });\n\n this.dropTrailingErrorMessage();\n\n this.retryAbortController = new AbortController();\n try {\n await sleep(delayMs, this.retryAbortController.signal);\n } catch {\n const attempt = this.retryAttempt;\n this.retryAttempt = 0;\n await this.emit({\n type: \"auto_retry_end\",\n success: false,\n attempt,\n finalError: \"Retry cancelled\",\n });\n return false;\n } finally {\n this.retryAbortController = undefined;\n }\n return true;\n }\n\n private dropTrailingErrorMessage(): void {\n const messages = this.agent.state.messages;\n if (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n this.agent.state.messages = messages.slice(0, -1);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
|
|
3
|
+
import { type SessionContext, type SessionEntry, type SessionFileEntry, type SessionHeader } from "./types.js";
|
|
4
|
+
/** Parse JSONL content tolerantly: malformed lines are skipped. */
|
|
5
|
+
export declare function parseSessionFileEntries(content: string): SessionFileEntry[];
|
|
6
|
+
/**
|
|
7
|
+
* Load a session file. Returns no entries when the file is missing or its
|
|
8
|
+
* first parseable line is not a valid session header (mirrors the tolerant
|
|
9
|
+
* read behavior mikan has relied on). Throws only on I/O errors.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadSessionFileEntries(filePath: string): SessionFileEntry[];
|
|
12
|
+
/**
|
|
13
|
+
* Append-oriented session store over one v3 JSONL file.
|
|
14
|
+
*
|
|
15
|
+
* Reading is tolerant (missing files and malformed lines yield an empty
|
|
16
|
+
* session); appends persist immediately. A session opened from a file
|
|
17
|
+
* without a valid header gets its header materialized on the first append.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SessionStore {
|
|
20
|
+
private readonly sessionFile;
|
|
21
|
+
private readonly cwd;
|
|
22
|
+
private readonly sessionId;
|
|
23
|
+
private header;
|
|
24
|
+
private headerOnDisk;
|
|
25
|
+
private entries;
|
|
26
|
+
private byId;
|
|
27
|
+
private leafId;
|
|
28
|
+
private constructor();
|
|
29
|
+
/**
|
|
30
|
+
* Open a session file. A missing or empty file starts an empty session
|
|
31
|
+
* whose header is written on the first append. A file that has content but
|
|
32
|
+
* no leading session header is treated as corrupted and throws, rather than
|
|
33
|
+
* being silently overwritten on the next append (which would erase the
|
|
34
|
+
* existing history). Callers on read paths already wrap this in try/catch.
|
|
35
|
+
* @param cwdOverride Working directory override; defaults to the header cwd.
|
|
36
|
+
*/
|
|
37
|
+
static open(path: string, cwdOverride?: string): SessionStore;
|
|
38
|
+
/** Create a new session file with a fresh header, replacing any existing file. */
|
|
39
|
+
static create(path: string, cwd: string, options?: {
|
|
40
|
+
id?: string;
|
|
41
|
+
}): SessionStore;
|
|
42
|
+
getSessionFile(): string;
|
|
43
|
+
getCwd(): string;
|
|
44
|
+
/** Session header as read from disk, or null when the file had none. */
|
|
45
|
+
getHeader(): SessionHeader | null;
|
|
46
|
+
/** Stable session id: the header id, or a generated id for fresh sessions. */
|
|
47
|
+
getSessionId(): string;
|
|
48
|
+
getLeafId(): string | null;
|
|
49
|
+
getEntry(id: string): SessionEntry | undefined;
|
|
50
|
+
/** All session entries (header excluded). Returns a shallow copy. */
|
|
51
|
+
getEntries(): SessionEntry[];
|
|
52
|
+
/** Latest user-assigned session name, if any. */
|
|
53
|
+
getSessionName(): string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Walk from an entry to the root, returning entries in path order
|
|
56
|
+
* (root first). Defaults to the current leaf.
|
|
57
|
+
*/
|
|
58
|
+
getBranch(fromId?: string): SessionEntry[];
|
|
59
|
+
/**
|
|
60
|
+
* Build the LLM context (messages, thinking level, model) from the current
|
|
61
|
+
* branch, resolving compaction and branch summaries along the path.
|
|
62
|
+
*/
|
|
63
|
+
buildSessionContext(): SessionContext;
|
|
64
|
+
/** Append a chat message as a child of the current leaf. Returns the entry id. */
|
|
65
|
+
appendMessage(message: AgentMessage): string;
|
|
66
|
+
/** Append an extension/application data entry (never sent to the LLM). */
|
|
67
|
+
appendCustomEntry(customType: string, data?: unknown): string;
|
|
68
|
+
/** Append a custom message entry that participates in LLM context. */
|
|
69
|
+
appendCustomMessageEntry(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details?: unknown): string;
|
|
70
|
+
/** Record a user-visible session name. */
|
|
71
|
+
appendSessionInfo(name: string): string;
|
|
72
|
+
/** Persist a compaction summary produced for this session. */
|
|
73
|
+
appendCompaction(summary: string, firstKeptEntryId: string, tokensBefore: number, details?: unknown, fromHook?: boolean): string;
|
|
74
|
+
private appendEntry;
|
|
75
|
+
private persist;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=session-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/harness/session-store.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAkBpB,mEAAmE;AACnE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAmB3E;AAMD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAM3E;AAQD;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,IAAI,CAAmC;IAC/C,OAAO,CAAC,MAAM,CAAuB;IAErC,OAAO,eAiBN;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,CAW5D;IAED,kFAAkF;IAClF,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY,CAUhF;IAED,cAAc,IAAI,MAAM,CAEvB;IAED,MAAM,IAAI,MAAM,CAEf;IAED,wEAAwE;IACxE,SAAS,IAAI,aAAa,GAAG,IAAI,CAEhC;IAED,8EAA8E;IAC9E,YAAY,IAAI,MAAM,CAErB;IAED,SAAS,IAAI,MAAM,GAAG,IAAI,CAEzB;IAED,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAE7C;IAED,qEAAqE;IACrE,UAAU,IAAI,YAAY,EAAE,CAE3B;IAED,iDAAiD;IACjD,cAAc,IAAI,MAAM,GAAG,SAAS,CAMnC;IAED;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAUzC;IAED;;;OAGG;IACH,mBAAmB,IAAI,cAAc,CAEpC;IAED,kFAAkF;IAClF,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAE3C;IAED,0EAA0E;IAC1E,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAE5D;IAED,sEAAsE;IACtE,wBAAwB,CACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,EAChD,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,CAER;IAED,0CAA0C;IAC1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtC;IAED,8DAA8D;IAC9D,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,OAAO,EACjB,QAAQ,CAAC,EAAE,OAAO,GACjB,MAAM,CASR;IAED,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,OAAO;CAiBhB"}
|