@geminixiang/mikan 1.0.0-beta.87 → 1.0.0-beta.89
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 +44 -1370
- package/README.md +2 -2
- package/dist/adapters/commands/session-view.d.ts.map +1 -1
- package/dist/adapters/commands/session-view.js +4 -4
- package/dist/adapters/commands/session-view.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +4 -1
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +6 -3
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +3 -1
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/discord/types.d.ts +1 -0
- package/dist/adapters/discord/types.d.ts.map +1 -1
- package/dist/adapters/discord/types.js.map +1 -1
- package/dist/adapters/github/activity.d.ts.map +1 -1
- package/dist/adapters/github/activity.js +7 -13
- package/dist/adapters/github/activity.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +4 -1
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +3 -3
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js +3 -1
- package/dist/adapters/github/context.js.map +1 -1
- package/dist/adapters/github/types.d.ts +0 -3
- package/dist/adapters/github/types.d.ts.map +1 -1
- package/dist/adapters/github/types.js.map +1 -1
- package/dist/adapters/intake.d.ts.map +1 -1
- package/dist/adapters/intake.js +12 -2
- package/dist/adapters/intake.js.map +1 -1
- package/dist/adapters/shared.d.ts +2 -1
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/shared.js +7 -2
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/slack/assistant.d.ts +2 -4
- package/dist/adapters/slack/assistant.d.ts.map +1 -1
- package/dist/adapters/slack/assistant.js +10 -16
- package/dist/adapters/slack/assistant.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +5 -1
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +15 -11
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +1 -1
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/slack/task-status.d.ts.map +1 -1
- package/dist/adapters/slack/task-status.js +1 -2
- package/dist/adapters/slack/task-status.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +4 -1
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +3 -3
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +3 -1
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/types.d.ts +3 -1
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/adapters/web/admin/client-assets.js +1 -1
- package/dist/adapters/web/admin/client-assets.js.map +1 -1
- package/dist/adapters/web/admin/portal.d.ts +0 -1
- package/dist/adapters/web/admin/portal.d.ts.map +1 -1
- package/dist/adapters/web/admin/portal.js +26 -42
- package/dist/adapters/web/admin/portal.js.map +1 -1
- package/dist/adapters/web/admin/types.d.ts +0 -2
- package/dist/adapters/web/admin/types.d.ts.map +1 -1
- package/dist/adapters/web/admin/types.js.map +1 -1
- package/dist/adapters/web/login/portal.d.ts.map +1 -1
- package/dist/adapters/web/login/portal.js +0 -8
- package/dist/adapters/web/login/portal.js.map +1 -1
- package/dist/adapters/web/server.d.ts.map +1 -1
- package/dist/adapters/web/server.js +0 -1
- package/dist/adapters/web/server.js.map +1 -1
- package/dist/adapters/web/session-view/portal.d.ts +1 -2
- package/dist/adapters/web/session-view/portal.d.ts.map +1 -1
- package/dist/adapters/web/session-view/portal.js +38 -328
- package/dist/adapters/web/session-view/portal.js.map +1 -1
- package/dist/adapters/web/session-view/types.d.ts +4 -6
- package/dist/adapters/web/session-view/types.d.ts.map +1 -1
- package/dist/adapters/web/session-view/types.js.map +1 -1
- package/dist/env-manifest.d.ts +0 -4
- package/dist/env-manifest.d.ts.map +1 -1
- package/dist/env-manifest.js +5 -8
- package/dist/env-manifest.js.map +1 -1
- package/dist/events/index.d.ts +2 -9
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +0 -1
- package/dist/events/index.js.map +1 -1
- package/dist/events/scheduler.d.ts +0 -1
- package/dist/events/scheduler.d.ts.map +1 -1
- package/dist/events/scheduler.js +2 -7
- package/dist/events/scheduler.js.map +1 -1
- package/dist/harness/execution-env.d.ts +1 -1
- package/dist/harness/execution-env.d.ts.map +1 -1
- package/dist/harness/execution-env.js +32 -32
- package/dist/harness/execution-env.js.map +1 -1
- package/dist/harness/index.d.ts +2 -3
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +0 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/mcp-result.d.ts +4 -3
- package/dist/harness/mcp-result.d.ts.map +1 -1
- package/dist/harness/mcp-result.js +27 -13
- package/dist/harness/mcp-result.js.map +1 -1
- package/dist/harness/mcp.js +4 -4
- package/dist/harness/mcp.js.map +1 -1
- package/dist/harness/presenter.d.ts.map +1 -1
- package/dist/harness/presenter.js +20 -25
- package/dist/harness/presenter.js.map +1 -1
- package/dist/harness/prompt.d.ts +0 -1
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +3 -32
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/run-events.d.ts.map +1 -1
- package/dist/harness/run-events.js +4 -12
- package/dist/harness/run-events.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +35 -38
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.d.ts +37 -38
- package/dist/harness/session.d.ts.map +1 -1
- package/dist/harness/session.js +387 -388
- package/dist/harness/session.js.map +1 -1
- package/dist/harness/subagent.d.ts +5 -5
- package/dist/harness/subagent.d.ts.map +1 -1
- package/dist/harness/subagent.js +40 -45
- package/dist/harness/subagent.js.map +1 -1
- package/dist/harness/tools/codemode.d.ts +3 -0
- package/dist/harness/tools/codemode.d.ts.map +1 -0
- package/dist/harness/tools/codemode.js +173 -0
- package/dist/harness/tools/codemode.js.map +1 -0
- package/dist/harness/tools/history.d.ts +20 -0
- package/dist/harness/tools/history.d.ts.map +1 -0
- package/dist/harness/tools/history.js +293 -0
- package/dist/harness/tools/history.js.map +1 -0
- package/dist/harness/tools/pi-tools.d.ts +1 -1
- package/dist/harness/tools/pi-tools.d.ts.map +1 -1
- package/dist/harness/tools/pi-tools.js +47 -21
- package/dist/harness/tools/pi-tools.js.map +1 -1
- package/dist/harness/tools/secret-redaction.d.ts.map +1 -1
- package/dist/harness/tools/secret-redaction.js +46 -2
- package/dist/harness/tools/secret-redaction.js.map +1 -1
- package/dist/harness/tools/tool-search.d.ts +5 -0
- package/dist/harness/tools/tool-search.d.ts.map +1 -0
- package/dist/harness/tools/tool-search.js +84 -0
- package/dist/harness/tools/tool-search.js.map +1 -0
- package/dist/harness/types.d.ts +75 -15
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +0 -3
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +0 -15
- package/dist/log.js.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/memory-capture/index.js +0 -1
- package/dist/memory-capture/index.js.map +1 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -0
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/session-files.d.ts +3 -2
- package/dist/migrations/session-files.d.ts.map +1 -1
- package/dist/migrations/session-files.js.map +1 -1
- package/dist/migrations/session-v4.d.ts +77 -0
- package/dist/migrations/session-v4.d.ts.map +1 -0
- package/dist/migrations/session-v4.js +177 -0
- package/dist/migrations/session-v4.js.map +1 -0
- package/dist/migrations/sessions-sqlite.d.ts +3 -0
- package/dist/migrations/sessions-sqlite.d.ts.map +1 -0
- package/dist/migrations/sessions-sqlite.js +215 -0
- package/dist/migrations/sessions-sqlite.js.map +1 -0
- package/dist/migrations/sessions-v3.d.ts +2 -2
- package/dist/migrations/sessions-v3.d.ts.map +1 -1
- package/dist/migrations/sessions-v3.js +8 -19
- package/dist/migrations/sessions-v3.js.map +1 -1
- package/dist/migrations/types.d.ts +0 -5
- package/dist/migrations/types.d.ts.map +1 -1
- package/dist/migrations/types.js.map +1 -1
- package/dist/observability/sentry.d.ts +2 -9
- package/dist/observability/sentry.d.ts.map +1 -1
- package/dist/observability/sentry.js +12 -64
- package/dist/observability/sentry.js.map +1 -1
- package/dist/observability/types.d.ts +0 -16
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/observability/types.js.map +1 -1
- package/dist/office/index.d.ts +1 -4
- package/dist/office/index.d.ts.map +1 -1
- package/dist/office/index.js +2 -14
- package/dist/office/index.js.map +1 -1
- package/dist/office/projection.d.ts.map +1 -1
- package/dist/office/projection.js +0 -1
- package/dist/office/projection.js.map +1 -1
- package/dist/office/types.d.ts +1 -2
- package/dist/office/types.d.ts.map +1 -1
- package/dist/office/types.js.map +1 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +2 -16
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/session-lifecycle.d.ts +1 -2
- package/dist/runtime/session-lifecycle.d.ts.map +1 -1
- package/dist/runtime/session-lifecycle.js +7 -16
- package/dist/runtime/session-lifecycle.js.map +1 -1
- package/dist/runtime/types.d.ts +0 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js +3 -4
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts +8 -6
- package/dist/sessions/chat-history-sync.d.ts.map +1 -1
- package/dist/sessions/chat-history-sync.js +70 -143
- package/dist/sessions/chat-history-sync.js.map +1 -1
- package/dist/sessions/compaction-summary.d.ts +4 -0
- package/dist/sessions/compaction-summary.d.ts.map +1 -0
- package/dist/sessions/compaction-summary.js +16 -0
- package/dist/sessions/compaction-summary.js.map +1 -0
- package/dist/sessions/session-key.d.ts +1 -0
- package/dist/sessions/session-key.d.ts.map +1 -1
- package/dist/sessions/session-key.js +3 -0
- package/dist/sessions/session-key.js.map +1 -1
- package/dist/sessions/session-store.d.ts +47 -29
- package/dist/sessions/session-store.d.ts.map +1 -1
- package/dist/sessions/session-store.js +578 -484
- package/dist/sessions/session-store.js.map +1 -1
- package/dist/sessions/types.d.ts +60 -46
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/settings/apply.js +2 -2
- package/dist/settings/apply.js.map +1 -1
- package/dist/types.d.ts +10 -3
- 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 +0 -3
- package/dist/vault/index.js.map +1 -1
- package/dist/vault/types.d.ts +0 -3
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/package.json +8 -5
- package/dist/sessions/store.d.ts +0 -17
- package/dist/sessions/store.d.ts.map +0 -1
- package/dist/sessions/store.js +0 -211
- package/dist/sessions/store.js.map +0 -1
package/dist/harness/session.js
CHANGED
|
@@ -1,43 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
2
|
+
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
3
|
+
import { AgentDoc, DEFAULT_COMPACTION_POLICY, CompactionTask, GenerationTask, LiveDoc, ToolTask, defineExtension, hook, section, watchEvents, } from "@earendil-works/pi-durable";
|
|
2
4
|
import * as log from "../log.js";
|
|
3
5
|
import { ToolLoopGuard } from "./loop-guard.js";
|
|
4
6
|
import { adaptAgentTool, isHarnessTool } from "./tools/pi-tools.js";
|
|
5
7
|
import { START_TASK_TOOL, TASK_STATUS_TOOL } from "./tools/task.js";
|
|
8
|
+
import { createCodemodeTool } from "./tools/codemode.js";
|
|
9
|
+
import { createToolSearchTool } from "./tools/tool-search.js";
|
|
10
|
+
import { withSecretRedaction } from "./tools/secret-redaction.js";
|
|
6
11
|
import { errorMessage } from "../unknown-values.js";
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"tool_start",
|
|
17
|
-
"tool_update",
|
|
18
|
-
"tool_end",
|
|
19
|
-
"retry_scheduled",
|
|
20
|
-
"retry_end",
|
|
21
|
-
"compaction_start",
|
|
22
|
-
"compaction_end",
|
|
23
|
-
"usage",
|
|
24
|
-
"fault",
|
|
25
|
-
];
|
|
12
|
+
const context = BACKGROUND_CONTEXT;
|
|
13
|
+
const COMPACTION_REQUEST = Symbol("compaction-request");
|
|
14
|
+
function deferred() {
|
|
15
|
+
let resolve;
|
|
16
|
+
const promise = new Promise((done) => {
|
|
17
|
+
resolve = done;
|
|
18
|
+
});
|
|
19
|
+
return { promise, resolve };
|
|
20
|
+
}
|
|
26
21
|
export class MikanAgentSession {
|
|
27
22
|
constructor(options) {
|
|
28
23
|
this.options = options;
|
|
29
24
|
this.listeners = new Set();
|
|
30
|
-
this.transcript = [];
|
|
31
25
|
this.runActive = false;
|
|
32
26
|
this.runAborted = false;
|
|
33
27
|
this.runBudget = {};
|
|
34
28
|
this.retryAttempt = 0;
|
|
35
|
-
this.latestAssistantNeedsCall = false;
|
|
36
|
-
this.latestAssistantErrored = false;
|
|
37
29
|
this.runMessages = [];
|
|
38
30
|
this.toolArgs = new Map();
|
|
39
31
|
this.loopGuard = new ToolLoopGuard();
|
|
40
32
|
this.loopNotices = new Map();
|
|
33
|
+
this.runStarted = false;
|
|
34
|
+
this.runEndSeen = false;
|
|
35
|
+
this.observedUsage = createEmptyUsage();
|
|
36
|
+
this.runEnded = deferred();
|
|
41
37
|
this.tally = {
|
|
42
38
|
usage: createEmptyUsage(),
|
|
43
39
|
llmCalls: 0,
|
|
@@ -50,8 +46,8 @@ export class MikanAgentSession {
|
|
|
50
46
|
this.systemPrompt = options.systemPrompt;
|
|
51
47
|
this.settings = resolveHarnessSettings(options.settings);
|
|
52
48
|
}
|
|
53
|
-
get
|
|
54
|
-
return this.
|
|
49
|
+
get lastRunMessages() {
|
|
50
|
+
return this.runMessages;
|
|
55
51
|
}
|
|
56
52
|
get isActiveRun() {
|
|
57
53
|
return this.runActive;
|
|
@@ -71,6 +67,7 @@ export class MikanAgentSession {
|
|
|
71
67
|
toolCallCounts: { ...this.tally.toolCallCounts },
|
|
72
68
|
durationMs: this.tally.startedAt > 0 ? (this.tally.endedAt ?? Date.now()) - this.tally.startedAt : 0,
|
|
73
69
|
budgetExceededReason: this.budgetExceededReason || undefined,
|
|
70
|
+
status: this.lastSessionRunStatus,
|
|
74
71
|
};
|
|
75
72
|
}
|
|
76
73
|
captureExternalUsageSink() {
|
|
@@ -84,86 +81,38 @@ export class MikanAgentSession {
|
|
|
84
81
|
await this.exceedBudget(reason);
|
|
85
82
|
};
|
|
86
83
|
}
|
|
87
|
-
async foldExternalUsage(usage) {
|
|
88
|
-
await this.captureExternalUsageSink()(usage);
|
|
89
|
-
}
|
|
90
84
|
subscribe(listener) {
|
|
91
85
|
this.listeners.add(listener);
|
|
92
86
|
return () => this.listeners.delete(listener);
|
|
93
87
|
}
|
|
94
|
-
async reloadFromSession() {
|
|
95
|
-
this.transcript = (await this.sessionStore.buildSessionContext()).messages;
|
|
96
|
-
return this.transcript.length;
|
|
97
|
-
}
|
|
98
88
|
async prompt(text, options) {
|
|
99
89
|
await this.run(text, options);
|
|
100
90
|
}
|
|
101
91
|
async steer(text) {
|
|
102
|
-
if (!this.runActive || this.runAborted || !this.
|
|
92
|
+
if (!this.runActive || this.runAborted || !this.attached)
|
|
103
93
|
return false;
|
|
104
|
-
|
|
94
|
+
await this.attached.conversation.submit({ type: "input", content: text, whenBusy: "steer" }, context);
|
|
105
95
|
return true;
|
|
106
96
|
}
|
|
107
|
-
async resume(options) {
|
|
108
|
-
await this.run(undefined, options);
|
|
109
|
-
}
|
|
110
|
-
compactWhenNearLimit() {
|
|
111
|
-
if (this.runActive || this.idleCompaction || !this.lane)
|
|
112
|
-
return;
|
|
113
|
-
const { enabled, reserveTokens } = this.settings.compaction;
|
|
114
|
-
if (!enabled)
|
|
115
|
-
return;
|
|
116
|
-
const threshold = (this.model.contextWindow - reserveTokens) * IDLE_COMPACTION_FRACTION_OF_PI_THRESHOLD;
|
|
117
|
-
const { tokens } = estimateContextTokens(this.transcript);
|
|
118
|
-
if (tokens < threshold)
|
|
119
|
-
return;
|
|
120
|
-
const lane = this.lane;
|
|
121
|
-
log.logInfo(`Idle compaction started ${JSON.stringify({ tokens, threshold })}`);
|
|
122
|
-
const compaction = lane
|
|
123
|
-
.compact(undefined, TODO_CONTEXT)
|
|
124
|
-
.then(async (result) => {
|
|
125
|
-
if (!result.ok) {
|
|
126
|
-
log.logInfo(`Idle compaction skipped: ${result.error.message}`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
log.logInfo(`Idle compaction ${result.value.compaction.status}`);
|
|
130
|
-
await this.reloadFromSession();
|
|
131
|
-
})
|
|
132
|
-
.catch((error) => {
|
|
133
|
-
log.logWarning("Idle compaction failed", errorMessage(error));
|
|
134
|
-
})
|
|
135
|
-
.finally(() => {
|
|
136
|
-
if (this.idleCompaction === compaction)
|
|
137
|
-
this.idleCompaction = undefined;
|
|
138
|
-
});
|
|
139
|
-
this.idleCompaction = compaction;
|
|
140
|
-
}
|
|
141
|
-
async cancelIdleCompaction() {
|
|
142
|
-
const compaction = this.idleCompaction;
|
|
143
|
-
if (!compaction || !this.lane)
|
|
144
|
-
return;
|
|
145
|
-
const aborted = await this.lane.abort(TODO_CONTEXT);
|
|
146
|
-
if (!aborted.ok)
|
|
147
|
-
log.logInfo(`Idle compaction abort: ${aborted.error.message}`);
|
|
148
|
-
await compaction;
|
|
149
|
-
}
|
|
150
97
|
async run(text, options) {
|
|
151
98
|
if (this.runActive)
|
|
152
99
|
throw new Error("Agent is already processing a prompt");
|
|
153
100
|
this.runActive = true;
|
|
154
101
|
this.runAborted = false;
|
|
155
|
-
this.pendingProviderRequest = undefined;
|
|
156
|
-
this.currentProviderRequest = undefined;
|
|
157
|
-
this.activeProviderRequest = undefined;
|
|
158
102
|
this.budgetExceededReason = undefined;
|
|
103
|
+
this.cancellation = undefined;
|
|
159
104
|
this.cancellationError = undefined;
|
|
160
105
|
this.retryAttempt = 0;
|
|
161
|
-
this.latestAssistantNeedsCall = false;
|
|
162
|
-
this.latestAssistantErrored = false;
|
|
163
106
|
this.runMessages = [];
|
|
107
|
+
this.runStarted = false;
|
|
108
|
+
this.runEndSeen = false;
|
|
109
|
+
this.runEnded = deferred();
|
|
110
|
+
this.activeRequest = undefined;
|
|
111
|
+
this.requestRunId = undefined;
|
|
164
112
|
this.loopGuard = new ToolLoopGuard();
|
|
165
113
|
this.loopNotices.clear();
|
|
166
114
|
this.runBudget = { ...this.settings.budget, ...options?.budget };
|
|
115
|
+
this.lastSessionRunStatus = undefined;
|
|
167
116
|
this.tally = {
|
|
168
117
|
usage: createEmptyUsage(),
|
|
169
118
|
llmCalls: 0,
|
|
@@ -172,8 +121,9 @@ export class MikanAgentSession {
|
|
|
172
121
|
startedAt: Date.now(),
|
|
173
122
|
};
|
|
174
123
|
let runFailure;
|
|
124
|
+
let stream;
|
|
125
|
+
let status = "completed";
|
|
175
126
|
try {
|
|
176
|
-
await this.idleCompaction;
|
|
177
127
|
if (!(await this.checkCallBudget()))
|
|
178
128
|
return;
|
|
179
129
|
this.armDeadline();
|
|
@@ -182,57 +132,63 @@ export class MikanAgentSession {
|
|
|
182
132
|
return;
|
|
183
133
|
if (!auth)
|
|
184
134
|
throw new Error(`No credentials for provider "${this.model.provider}". Set the provider API key environment variable.`);
|
|
185
|
-
await this.
|
|
135
|
+
const attached = await this.attach();
|
|
186
136
|
if (this.runAborted)
|
|
187
137
|
return;
|
|
188
|
-
const harness = this.harness;
|
|
189
|
-
const lane = this.lane;
|
|
190
138
|
const tools = this.toHarnessTools(options?.tools ?? this.options.tools).filter((tool) => (tool.name !== START_TASK_TOOL || options?.allowTaskHandoff === true) &&
|
|
191
139
|
(tool.name !== TASK_STATUS_TOOL || options?.allowTaskStatus === true));
|
|
192
|
-
await
|
|
193
|
-
await lane.setActiveTools(tools.map((tool) => tool.name), TODO_CONTEXT);
|
|
194
|
-
await this.reloadFromSession();
|
|
140
|
+
await this.installRunTools(attached, tools);
|
|
195
141
|
if (!(await this.checkCallBudget()))
|
|
196
142
|
return;
|
|
197
|
-
await this.
|
|
143
|
+
await this.sessionStore.recordRun({});
|
|
144
|
+
stream = await watchEvents(attached.harness, attached.conversation.id, context);
|
|
145
|
+
this.observedUsage = sumUsageState(stream.snapshot.usage);
|
|
146
|
+
stream.start((events) => this.handleDurableEvents(events));
|
|
147
|
+
status = await this.drive(attached, text, options?.images);
|
|
198
148
|
}
|
|
199
149
|
catch (error) {
|
|
200
150
|
runFailure = { error };
|
|
151
|
+
status = "failed";
|
|
201
152
|
throw error;
|
|
202
153
|
}
|
|
203
154
|
finally {
|
|
204
|
-
|
|
155
|
+
if (status !== "failed" && this.runAborted)
|
|
156
|
+
status = "aborted";
|
|
157
|
+
await this.cleanupRun(runFailure, stream, status);
|
|
205
158
|
}
|
|
206
159
|
}
|
|
207
|
-
async
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
this.requestCancellation();
|
|
213
|
-
const result = getOrThrow(await lane.resume(TODO_CONTEXT));
|
|
214
|
-
if (result.status === "failed")
|
|
215
|
-
throw new Error(result.error?.message ?? "Pi run failed");
|
|
216
|
-
return;
|
|
160
|
+
async drive({ harness, conversation }, text, images) {
|
|
161
|
+
const interrupted = (await harness.snapshot(LiveDoc, conversation.id, context))?.run;
|
|
162
|
+
if (interrupted) {
|
|
163
|
+
log.logWarning("Aborting a run interrupted before this process started");
|
|
164
|
+
await conversation.abort(context);
|
|
217
165
|
}
|
|
218
|
-
const
|
|
219
|
-
|
|
166
|
+
const content = images && images.length > 0 ? [{ type: "text", text }, ...images] : text;
|
|
167
|
+
const submission = await conversation.submit({ type: "input", content, whenBusy: "reject" }, context);
|
|
220
168
|
if (this.runAborted)
|
|
221
169
|
this.requestCancellation();
|
|
222
|
-
const
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
170
|
+
const settled = await submission.wait(context);
|
|
171
|
+
if (this.runStarted)
|
|
172
|
+
await this.runEnded.promise;
|
|
173
|
+
if (settled.status === "done")
|
|
174
|
+
return "completed";
|
|
175
|
+
if (this.runAborted || settled.reason === "aborted")
|
|
176
|
+
return "aborted";
|
|
177
|
+
if (settled.reason === "model_error")
|
|
178
|
+
return "failed";
|
|
179
|
+
throw new Error(`Pi run failed: ${settled.reason}`);
|
|
180
|
+
}
|
|
181
|
+
async cleanupRun(runFailure, stream, status) {
|
|
230
182
|
clearTimeout(this.deadlineTimer);
|
|
231
183
|
this.deadlineTimer = undefined;
|
|
232
184
|
let cleanupFailure;
|
|
233
185
|
try {
|
|
234
186
|
await this.cancellation;
|
|
235
187
|
await this.deadlineNotification;
|
|
188
|
+
await stream?.stop();
|
|
189
|
+
if (this.attached) {
|
|
190
|
+
await this.sessionStore.recordRun({ endedAt: Date.now(), status });
|
|
191
|
+
}
|
|
236
192
|
if (this.cancellationError)
|
|
237
193
|
throw this.cancellationError;
|
|
238
194
|
}
|
|
@@ -242,13 +198,10 @@ export class MikanAgentSession {
|
|
|
242
198
|
finally {
|
|
243
199
|
this.toolArgs.clear();
|
|
244
200
|
this.loopNotices.clear();
|
|
245
|
-
this.pendingProviderRequest = undefined;
|
|
246
|
-
this.currentProviderRequest = undefined;
|
|
247
|
-
this.activeProviderRequest = undefined;
|
|
248
|
-
this.operationId = undefined;
|
|
249
201
|
this.cancellation = undefined;
|
|
250
202
|
this.deadlineNotification = undefined;
|
|
251
203
|
this.tally.endedAt = Date.now();
|
|
204
|
+
this.lastSessionRunStatus = status;
|
|
252
205
|
this.runActive = false;
|
|
253
206
|
}
|
|
254
207
|
if (!cleanupFailure)
|
|
@@ -265,103 +218,71 @@ export class MikanAgentSession {
|
|
|
265
218
|
this.runAborted = true;
|
|
266
219
|
clearTimeout(this.deadlineTimer);
|
|
267
220
|
this.deadlineTimer = undefined;
|
|
268
|
-
if (firstAbort && this.
|
|
269
|
-
log.logInfo(`LLM request aborted ${JSON.stringify({
|
|
270
|
-
abort_reason: reason,
|
|
271
|
-
run_id: this.activeProviderRequest.runId,
|
|
272
|
-
})}`);
|
|
221
|
+
if (firstAbort && this.activeRequest !== undefined) {
|
|
222
|
+
log.logInfo(`LLM request aborted ${JSON.stringify({ abort_reason: reason, run_id: this.activeRequest.runId })}`);
|
|
273
223
|
}
|
|
274
224
|
this.requestCancellation();
|
|
275
225
|
}
|
|
276
226
|
requestCancellation() {
|
|
277
|
-
if (!this.
|
|
227
|
+
if (!this.attached || this.cancellation)
|
|
278
228
|
return;
|
|
279
|
-
this.cancellation = this.
|
|
280
|
-
.requestAbort(this.operationId, TODO_CONTEXT)
|
|
281
|
-
.then((result) => {
|
|
282
|
-
if (!result.ok && !(result.error instanceof OperationMismatch))
|
|
283
|
-
throw result.error;
|
|
284
|
-
})
|
|
285
|
-
.catch((error) => {
|
|
229
|
+
this.cancellation = this.attached.conversation.abort(context).catch((error) => {
|
|
286
230
|
this.cancellationError = error;
|
|
287
231
|
});
|
|
288
232
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
if (this.activeProviderRequest?.token === request.token) {
|
|
303
|
-
this.activeProviderRequest = undefined;
|
|
304
|
-
}
|
|
233
|
+
async attach() {
|
|
234
|
+
if (this.attached)
|
|
235
|
+
return this.attached;
|
|
236
|
+
const { retry, compaction } = this.settings;
|
|
237
|
+
this.attached = await this.sessionStore.bindHarness({
|
|
238
|
+
models: this.options.models.models,
|
|
239
|
+
requestModels: this.trackedModels(this.options.models.models),
|
|
240
|
+
env: () => this.options.toolContext?.env,
|
|
241
|
+
settings: { retry, compaction },
|
|
242
|
+
onReport: (error) => log.logWarning("Durable harness report", errorMessage(error)),
|
|
243
|
+
});
|
|
244
|
+
return this.attached;
|
|
305
245
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
246
|
+
trackedModels(models) {
|
|
247
|
+
const track = (options, token) => {
|
|
248
|
+
const onPayload = options?.onPayload;
|
|
249
|
+
const tracked = {
|
|
250
|
+
...options,
|
|
251
|
+
onPayload: async (...args) => {
|
|
252
|
+
const transformed = await onPayload?.(...args);
|
|
253
|
+
if (this.runActive) {
|
|
254
|
+
this.activeRequest = { token, runId: this.requestRunId ?? "background" };
|
|
255
|
+
}
|
|
256
|
+
return transformed;
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
return tracked;
|
|
260
|
+
};
|
|
261
|
+
const withSession = (options) => ({
|
|
311
262
|
...options,
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
263
|
+
sessionId: options.sessionId ?? this.sessionStore.getSessionId(),
|
|
264
|
+
});
|
|
265
|
+
const finish = (token) => {
|
|
266
|
+
if (this.activeRequest?.token === token)
|
|
267
|
+
this.activeRequest = undefined;
|
|
268
|
+
};
|
|
269
|
+
const stream = (start) => {
|
|
270
|
+
const token = Symbol("provider-request");
|
|
271
|
+
const result = start(token);
|
|
272
|
+
void result.result().then(() => finish(token), () => finish(token));
|
|
273
|
+
return result;
|
|
319
274
|
};
|
|
320
|
-
}
|
|
321
|
-
trackProviderStream(start) {
|
|
322
|
-
const request = this.beginProviderRequest();
|
|
323
|
-
try {
|
|
324
|
-
const stream = start(request);
|
|
325
|
-
if (request) {
|
|
326
|
-
void stream.result().then(() => this.endProviderRequest(request), () => this.endProviderRequest(request));
|
|
327
|
-
}
|
|
328
|
-
return stream;
|
|
329
|
-
}
|
|
330
|
-
catch (error) {
|
|
331
|
-
this.endProviderRequest(request);
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
trackProviderPromise(start) {
|
|
336
|
-
const request = this.beginProviderRequest();
|
|
337
|
-
try {
|
|
338
|
-
const promise = start(request);
|
|
339
|
-
if (!request)
|
|
340
|
-
return promise;
|
|
341
|
-
return promise.then((result) => {
|
|
342
|
-
this.endProviderRequest(request);
|
|
343
|
-
return result;
|
|
344
|
-
}, (error) => {
|
|
345
|
-
this.endProviderRequest(request);
|
|
346
|
-
throw error;
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
catch (error) {
|
|
350
|
-
this.endProviderRequest(request);
|
|
351
|
-
throw error;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
trackedModels(models) {
|
|
355
275
|
return new Proxy(models, {
|
|
356
276
|
get: (target, property) => {
|
|
357
277
|
if (property === "streamSimple") {
|
|
358
|
-
return (...[model,
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
278
|
+
return (...[model, request, options]) => {
|
|
279
|
+
const over = this.runActive ? this.callOverBudgetReason() : undefined;
|
|
280
|
+
if (over || this.runAborted)
|
|
281
|
+
return abortedStream(model, over ?? "Operation aborted");
|
|
282
|
+
if (this.runActive)
|
|
283
|
+
this.tally.llmCalls += 1;
|
|
284
|
+
return stream((token) => target.streamSimple(model, request, withSession(track(options, token))));
|
|
285
|
+
};
|
|
365
286
|
}
|
|
366
287
|
const value = Reflect.get(target, property, target);
|
|
367
288
|
return typeof value === "function" ? value.bind(target) : value;
|
|
@@ -371,69 +292,130 @@ export class MikanAgentSession {
|
|
|
371
292
|
toHarnessTools(tools) {
|
|
372
293
|
return tools.map((tool) => (isHarnessTool(tool) ? tool : adaptAgentTool(tool)));
|
|
373
294
|
}
|
|
374
|
-
async
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
295
|
+
async installRunTools(attached, selected) {
|
|
296
|
+
const tools = [...selected];
|
|
297
|
+
const deferredTools = tools.filter((tool) => tool.exposure === "deferred");
|
|
298
|
+
const authorized = new Set(deferredTools.map((tool) => tool.name));
|
|
299
|
+
const { harness, conversation, registry, extensionName } = attached;
|
|
300
|
+
const stored = (await harness.snapshot(AgentDoc, conversation.id, context))?.tools;
|
|
301
|
+
const loaded = new Set(Array.isArray(stored) ? stored.filter((name) => authorized.has(name)) : []);
|
|
302
|
+
if (tools.length > 0)
|
|
303
|
+
tools.push(withSecretRedaction(createCodemodeTool({
|
|
304
|
+
tools,
|
|
305
|
+
executeNested: (tool, call) => this.executeNestedTool(tool, call),
|
|
306
|
+
})));
|
|
307
|
+
if (deferredTools.length > 0) {
|
|
308
|
+
tools.push(withSecretRedaction(createToolSearchTool({
|
|
309
|
+
tools: deferredTools,
|
|
310
|
+
loaded,
|
|
311
|
+
load: async (names) => {
|
|
312
|
+
for (const name of names)
|
|
313
|
+
if (authorized.has(name))
|
|
314
|
+
loaded.add(name);
|
|
315
|
+
},
|
|
316
|
+
})));
|
|
317
|
+
}
|
|
318
|
+
const registered = tools;
|
|
319
|
+
registry.install(defineExtension({
|
|
320
|
+
name: extensionName,
|
|
321
|
+
tools: registered,
|
|
322
|
+
sections: [
|
|
323
|
+
section("mikan", () => this.sessionStore.withMcpInstructions(this.systemPrompt), {
|
|
324
|
+
tag: false,
|
|
325
|
+
}),
|
|
326
|
+
],
|
|
327
|
+
hooks: [
|
|
328
|
+
hook(GenerationTask, {
|
|
329
|
+
beforeRequest: async (request, api) => {
|
|
330
|
+
await this.beforeRequest(String(api.taskId));
|
|
331
|
+
return { messages: attached.tagRequest(request.messages) };
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
hook(CompactionTask, {
|
|
335
|
+
beforeCompact: (_compaction, api) => this.beforeCompact(String(api.taskId)),
|
|
336
|
+
}),
|
|
337
|
+
hook(ToolTask, {
|
|
338
|
+
beforeTool: async (call, api) => this.beforeTool(call, api),
|
|
339
|
+
afterTool: (call, result) => this.afterTool(call.id, result),
|
|
340
|
+
}),
|
|
341
|
+
],
|
|
342
|
+
}));
|
|
343
|
+
await conversation.configure({
|
|
344
|
+
model: { provider: this.model.provider, modelId: this.model.id },
|
|
380
345
|
thinkingLevel: this.options.thinkingLevel,
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
this.lane = await this.harness.lane("main", TODO_CONTEXT);
|
|
388
|
-
await this.lane.setModel({ provider: this.model.provider, modelId: this.model.id }, TODO_CONTEXT);
|
|
389
|
-
await this.lane.setThinkingLevel(this.options.thinkingLevel, TODO_CONTEXT);
|
|
390
|
-
this.harness.hooks.on("before_request", async (event) => {
|
|
391
|
-
if (!(await this.checkCallBudget()))
|
|
392
|
-
return undefined;
|
|
393
|
-
this.pendingProviderRequest = { token: Symbol("provider-request"), runId: event.runId };
|
|
394
|
-
this.tally.llmCalls += 1;
|
|
346
|
+
extensions: [registry.snapshot().extension(extensionName)],
|
|
347
|
+
tools: registered.filter((tool) => tool.exposure !== "deferred" || loaded.has(tool.name)),
|
|
348
|
+
}, context);
|
|
349
|
+
}
|
|
350
|
+
beforeCompact(taskId) {
|
|
351
|
+
if (!this.runActive)
|
|
395
352
|
return undefined;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
reason: `Call ${START_TASK_TOOL} alone, without other tools in the same batch.`,
|
|
406
|
-
},
|
|
407
|
-
};
|
|
408
|
-
}
|
|
353
|
+
if (this.runAborted || this.budgetExceededReason || this.callOverBudgetReason()) {
|
|
354
|
+
return { decline: true };
|
|
355
|
+
}
|
|
356
|
+
this.tally.llmCalls += 1;
|
|
357
|
+
this.activeRequest = { token: COMPACTION_REQUEST, runId: taskId };
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
async beforeRequest(runId) {
|
|
361
|
+
if (!(await this.checkCallBudget()))
|
|
409
362
|
return undefined;
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
363
|
+
this.requestRunId = runId;
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
async beforeTool(call, api) {
|
|
367
|
+
const round = (await api.snapshot(LiveDoc, api.conversationId, context))?.tools ?? [];
|
|
368
|
+
if (round.length > 1 && round.some((slot) => slot.name === START_TASK_TOOL)) {
|
|
369
|
+
return { block: `Call ${START_TASK_TOOL} alone, without other tools in the same batch.` };
|
|
370
|
+
}
|
|
371
|
+
return this.checkToolLoop(call.id, call.name, call.arguments);
|
|
372
|
+
}
|
|
373
|
+
afterTool(callId, result) {
|
|
374
|
+
const notice = this.loopNotices.get(callId);
|
|
375
|
+
if (notice === undefined)
|
|
376
|
+
return undefined;
|
|
377
|
+
this.loopNotices.delete(callId);
|
|
378
|
+
return { ...result, content: [...(result.content ?? []), { type: "text", text: notice }] };
|
|
379
|
+
}
|
|
380
|
+
async executeNestedTool(tool, call) {
|
|
381
|
+
const [args, api, toolContext] = call;
|
|
382
|
+
const id = api.callId;
|
|
383
|
+
if (this.runAborted || toolContext.abortSignal?.aborted)
|
|
384
|
+
throw new Error("Operation aborted");
|
|
385
|
+
await this.emitToolStart(id, tool.name, args);
|
|
386
|
+
let result;
|
|
387
|
+
try {
|
|
388
|
+
const verdict = await this.checkToolLoop(id, tool.name, args);
|
|
389
|
+
if (verdict?.block)
|
|
390
|
+
throw new Error(verdict.block);
|
|
391
|
+
toolContext.abortSignal?.throwIfAborted();
|
|
392
|
+
result = await tool.execute(args, api, toolContext);
|
|
393
|
+
result = this.afterTool(id, result) ?? result;
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
result = { content: [{ type: "text", text: errorMessage(error) }], isError: true };
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
this.loopNotices.delete(id);
|
|
400
|
+
}
|
|
401
|
+
await this.emitToolEnd(id, tool.name, { content: result.content ?? [], details: result.details }, result.isError === true);
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
async checkToolLoop(toolCallId, toolName, args) {
|
|
405
|
+
const verdict = this.loopGuard.observe(toolName, args);
|
|
406
|
+
switch (verdict.kind) {
|
|
407
|
+
case "allow":
|
|
431
408
|
return undefined;
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
409
|
+
case "notice":
|
|
410
|
+
this.loopNotices.set(toolCallId, verdict.text);
|
|
411
|
+
return undefined;
|
|
412
|
+
case "block":
|
|
413
|
+
return { block: verdict.reason };
|
|
414
|
+
case "stop":
|
|
415
|
+
await this.exceedBudget(verdict.reason);
|
|
416
|
+
return { block: verdict.reason };
|
|
417
|
+
default:
|
|
418
|
+
return verdict;
|
|
437
419
|
}
|
|
438
420
|
}
|
|
439
421
|
async emit(event) {
|
|
@@ -446,174 +428,147 @@ export class MikanAgentSession {
|
|
|
446
428
|
}
|
|
447
429
|
}
|
|
448
430
|
}
|
|
449
|
-
async
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
431
|
+
async handleDurableEvents(events) {
|
|
432
|
+
for (const event of events) {
|
|
433
|
+
try {
|
|
434
|
+
await this.handleDurableEvent(event);
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
log.logWarning("Durable event handling failed", errorMessage(error));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (this.runEndSeen)
|
|
441
|
+
this.runEnded.resolve();
|
|
442
|
+
}
|
|
443
|
+
async handleDurableEvent(event) {
|
|
454
444
|
switch (event.type) {
|
|
455
|
-
case "
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
this.
|
|
460
|
-
|
|
461
|
-
const message = event.entry.message;
|
|
462
|
-
this.latestAssistantErrored = message.stopReason === "error";
|
|
463
|
-
this.latestAssistantNeedsCall =
|
|
464
|
-
message.stopReason === "error" ||
|
|
465
|
-
message.content.some((part) => part.type === "toolCall");
|
|
466
|
-
}
|
|
467
|
-
await this.emit({ type: "message_end", message: event.entry.message });
|
|
445
|
+
case "run_start":
|
|
446
|
+
this.runStarted = true;
|
|
447
|
+
return;
|
|
448
|
+
case "run_end":
|
|
449
|
+
await this.endRetry(this.runAborted ? "Retry cancelled" : undefined);
|
|
450
|
+
this.runEndSeen = true;
|
|
468
451
|
return;
|
|
469
452
|
case "message_start":
|
|
453
|
+
if (event.message.role === "system")
|
|
454
|
+
return;
|
|
470
455
|
await this.emit({ type: "message_start", message: event.message });
|
|
471
456
|
return;
|
|
472
457
|
case "message_update":
|
|
473
|
-
|
|
474
|
-
type
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
return;
|
|
479
|
-
case "turn_start":
|
|
480
|
-
await this.emit({ type: "turn_start" });
|
|
481
|
-
return;
|
|
482
|
-
case "turn_end":
|
|
483
|
-
await this.emit({
|
|
484
|
-
type: "turn_end",
|
|
485
|
-
message: event.message,
|
|
486
|
-
toolResults: event.toolResults,
|
|
487
|
-
});
|
|
488
|
-
return;
|
|
489
|
-
case "tool_start":
|
|
490
|
-
case "tool_update":
|
|
491
|
-
case "tool_end":
|
|
492
|
-
return this.handlePiToolEvent(event);
|
|
493
|
-
case "usage":
|
|
494
|
-
return this.recordUsage(event.row.usage);
|
|
495
|
-
case "run_start":
|
|
496
|
-
case "run_end":
|
|
497
|
-
case "retry_scheduled":
|
|
498
|
-
case "retry_end":
|
|
499
|
-
case "compaction_start":
|
|
500
|
-
case "compaction_end":
|
|
501
|
-
return this.handlePiLifecycleEvent(event);
|
|
502
|
-
case "fault":
|
|
503
|
-
throw new Error(event.message);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
async recordUsage(usage) {
|
|
507
|
-
addUsage(this.tally.usage, usage);
|
|
508
|
-
const reason = (this.latestAssistantNeedsCall && this.callOverBudgetReason()) ||
|
|
509
|
-
this.resourceOverBudgetReason();
|
|
510
|
-
if (reason)
|
|
511
|
-
await this.exceedBudget(reason);
|
|
512
|
-
}
|
|
513
|
-
async handlePiToolEvent(event) {
|
|
514
|
-
switch (event.type) {
|
|
515
|
-
case "tool_start":
|
|
516
|
-
this.toolArgs.set(event.toolCallId, event.args);
|
|
517
|
-
this.tally.toolCalls += 1;
|
|
518
|
-
this.tally.toolCallCounts[event.toolName] =
|
|
519
|
-
(this.tally.toolCallCounts[event.toolName] ?? 0) + 1;
|
|
520
|
-
await this.emit({
|
|
521
|
-
type: "tool_execution_start",
|
|
522
|
-
toolCallId: event.toolCallId,
|
|
523
|
-
toolName: event.toolName,
|
|
524
|
-
args: event.args,
|
|
525
|
-
});
|
|
458
|
+
for (const change of event.changes) {
|
|
459
|
+
if (change.type === "text_delta" && change.delta) {
|
|
460
|
+
await this.emit({ type: "text_delta", delta: change.delta });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
526
463
|
return;
|
|
527
|
-
case "
|
|
464
|
+
case "message_end":
|
|
465
|
+
return this.endMessage(event.entry.model?.[0]);
|
|
466
|
+
case "tool_execution_start":
|
|
467
|
+
return this.emitToolStart(event.toolCallId, event.toolName, event.args);
|
|
468
|
+
case "tool_execution_update":
|
|
469
|
+
if (event.details === undefined)
|
|
470
|
+
return;
|
|
528
471
|
await this.emit({
|
|
529
472
|
type: "tool_execution_update",
|
|
530
473
|
toolCallId: event.toolCallId,
|
|
531
474
|
toolName: event.toolName,
|
|
532
|
-
|
|
533
|
-
partialResult: event.partialResult,
|
|
475
|
+
details: event.details,
|
|
534
476
|
});
|
|
535
477
|
return;
|
|
536
|
-
case "
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
toolCallId: event.toolCallId,
|
|
541
|
-
toolName: event.toolName,
|
|
542
|
-
result: event.result,
|
|
543
|
-
isError: event.isError,
|
|
544
|
-
});
|
|
545
|
-
return;
|
|
546
|
-
default:
|
|
547
|
-
event;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
async handlePiLifecycleEvent(event) {
|
|
551
|
-
switch (event.type) {
|
|
552
|
-
case "run_start":
|
|
553
|
-
this.operationId = event.runId;
|
|
554
|
-
if (this.runAborted)
|
|
555
|
-
this.requestCancellation();
|
|
556
|
-
await this.emit({ type: "agent_start" });
|
|
478
|
+
case "tool_execution_end": {
|
|
479
|
+
const message = event.entry?.model?.[0];
|
|
480
|
+
const toolResult = message?.role === "toolResult" ? message : undefined;
|
|
481
|
+
await this.emitToolEnd(event.toolCallId, event.toolName, { content: toolResult?.content ?? [], details: toolResult?.details }, toolResult?.isError ?? true);
|
|
557
482
|
return;
|
|
558
|
-
|
|
559
|
-
|
|
483
|
+
}
|
|
484
|
+
case "auto_retry_start":
|
|
485
|
+
this.retryAttempt = event.attempt;
|
|
560
486
|
await this.emit({
|
|
561
487
|
type: "auto_retry_start",
|
|
562
|
-
attempt:
|
|
563
|
-
maxAttempts:
|
|
564
|
-
delayMs: event.
|
|
488
|
+
attempt: event.attempt,
|
|
489
|
+
maxAttempts: this.settings.retry.maxRetries,
|
|
490
|
+
delayMs: Math.max(0, event.at - Date.now()),
|
|
565
491
|
errorMessage: event.errorMessage,
|
|
566
492
|
});
|
|
567
493
|
return;
|
|
568
|
-
case "
|
|
494
|
+
case "auto_retry_end":
|
|
569
495
|
this.retryAttempt = 0;
|
|
570
496
|
await this.emit({
|
|
571
497
|
type: "auto_retry_end",
|
|
572
|
-
attempt: event.attempt
|
|
573
|
-
success:
|
|
574
|
-
finalError:
|
|
498
|
+
attempt: event.attempt,
|
|
499
|
+
success: !this.runAborted,
|
|
500
|
+
finalError: this.runAborted ? "Retry cancelled" : undefined,
|
|
575
501
|
});
|
|
576
502
|
return;
|
|
577
503
|
case "compaction_start":
|
|
578
504
|
await this.emit({ type: "compaction_start", reason: event.reason });
|
|
579
505
|
return;
|
|
580
506
|
case "compaction_end": {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (entry?.type === "compaction")
|
|
585
|
-
await this.reloadFromSession();
|
|
507
|
+
if (this.activeRequest?.token === COMPACTION_REQUEST)
|
|
508
|
+
this.activeRequest = undefined;
|
|
509
|
+
const outcome = await this.compactionOutcome(event.taskId);
|
|
586
510
|
await this.emit({
|
|
587
511
|
type: "compaction_end",
|
|
588
512
|
reason: event.reason,
|
|
589
|
-
aborted:
|
|
590
|
-
errorMessage:
|
|
591
|
-
result: entry?.type === "compaction"
|
|
592
|
-
? {
|
|
593
|
-
summary: entry.summary,
|
|
594
|
-
retainedMessages: entry.retainedTail.length,
|
|
595
|
-
tokensBefore: entry.tokensBefore,
|
|
596
|
-
}
|
|
597
|
-
: undefined,
|
|
513
|
+
aborted: outcome?.status === "aborted",
|
|
514
|
+
errorMessage: outcome?.status === "failed" ? outcome.error.message : undefined,
|
|
598
515
|
});
|
|
599
516
|
return;
|
|
600
517
|
}
|
|
601
|
-
case "
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
attempt: this.retryAttempt,
|
|
606
|
-
success: false,
|
|
607
|
-
finalError: event.status === "aborted" ? "Retry cancelled" : event.error?.message,
|
|
608
|
-
});
|
|
609
|
-
this.retryAttempt = 0;
|
|
610
|
-
}
|
|
611
|
-
await this.emit({ type: "agent_end", messages: this.runMessages });
|
|
518
|
+
case "usage_changed":
|
|
519
|
+
return this.recordUsage(event.usage);
|
|
520
|
+
case "task_failed":
|
|
521
|
+
log.logWarning(`Durable task ${event.kind} failed`, event.message);
|
|
612
522
|
return;
|
|
613
523
|
default:
|
|
614
|
-
|
|
524
|
+
return;
|
|
615
525
|
}
|
|
616
526
|
}
|
|
527
|
+
async compactionOutcome(taskId) {
|
|
528
|
+
const task = await this.attached?.harness.getTask(taskId, context);
|
|
529
|
+
return task?.state.outcome;
|
|
530
|
+
}
|
|
531
|
+
async recordUsage(state) {
|
|
532
|
+
const total = sumUsageState(state);
|
|
533
|
+
addUsage(this.tally.usage, subtractUsage(total, this.observedUsage));
|
|
534
|
+
this.observedUsage = total;
|
|
535
|
+
const reason = this.resourceOverBudgetReason();
|
|
536
|
+
if (reason)
|
|
537
|
+
await this.exceedBudget(reason);
|
|
538
|
+
}
|
|
539
|
+
async endRetry(finalError) {
|
|
540
|
+
if (this.retryAttempt === 0)
|
|
541
|
+
return;
|
|
542
|
+
await this.emit({
|
|
543
|
+
type: "auto_retry_end",
|
|
544
|
+
attempt: this.retryAttempt,
|
|
545
|
+
success: false,
|
|
546
|
+
finalError,
|
|
547
|
+
});
|
|
548
|
+
this.retryAttempt = 0;
|
|
549
|
+
}
|
|
550
|
+
async endMessage(message) {
|
|
551
|
+
if (!message || message.role === "system")
|
|
552
|
+
return;
|
|
553
|
+
this.runMessages.push(message);
|
|
554
|
+
await this.emit({ type: "message_end", message });
|
|
555
|
+
if (message.role !== "assistant")
|
|
556
|
+
return;
|
|
557
|
+
const needsCall = message.stopReason === "error" || message.content.some((part) => part.type === "toolCall");
|
|
558
|
+
const reason = (needsCall && this.callOverBudgetReason()) || this.resourceOverBudgetReason();
|
|
559
|
+
if (reason)
|
|
560
|
+
await this.exceedBudget(reason);
|
|
561
|
+
}
|
|
562
|
+
async emitToolStart(toolCallId, toolName, args) {
|
|
563
|
+
this.toolArgs.set(toolCallId, args);
|
|
564
|
+
this.tally.toolCalls += 1;
|
|
565
|
+
this.tally.toolCallCounts[toolName] = (this.tally.toolCallCounts[toolName] ?? 0) + 1;
|
|
566
|
+
await this.emit({ type: "tool_execution_start", toolCallId, toolName, args });
|
|
567
|
+
}
|
|
568
|
+
async emitToolEnd(toolCallId, toolName, result, isError) {
|
|
569
|
+
this.toolArgs.delete(toolCallId);
|
|
570
|
+
await this.emit({ type: "tool_execution_end", toolCallId, toolName, result, isError });
|
|
571
|
+
}
|
|
617
572
|
async exceedBudget(reason) {
|
|
618
573
|
if (this.budgetExceededReason)
|
|
619
574
|
return;
|
|
@@ -669,6 +624,26 @@ export class MikanAgentSession {
|
|
|
669
624
|
}, Math.min(Math.max(0, remaining), 2_147_483_647));
|
|
670
625
|
}
|
|
671
626
|
}
|
|
627
|
+
function abortedAssistant(model, reason) {
|
|
628
|
+
return {
|
|
629
|
+
role: "assistant",
|
|
630
|
+
content: [],
|
|
631
|
+
api: model.api,
|
|
632
|
+
provider: model.provider,
|
|
633
|
+
model: model.id,
|
|
634
|
+
usage: createEmptyUsage(),
|
|
635
|
+
stopReason: "aborted",
|
|
636
|
+
errorMessage: reason,
|
|
637
|
+
timestamp: Date.now(),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function abortedStream(model, reason) {
|
|
641
|
+
const stream = createAssistantMessageEventStream();
|
|
642
|
+
const error = abortedAssistant(model, reason);
|
|
643
|
+
stream.push({ type: "error", reason: "aborted", error });
|
|
644
|
+
stream.end(error);
|
|
645
|
+
return stream;
|
|
646
|
+
}
|
|
672
647
|
export const DEFAULT_RETRY_SETTINGS = {
|
|
673
648
|
enabled: true,
|
|
674
649
|
maxRetries: 3,
|
|
@@ -682,7 +657,7 @@ export const DEFAULT_EVENT_BUDGET = {
|
|
|
682
657
|
};
|
|
683
658
|
export function resolveHarnessSettings(overrides) {
|
|
684
659
|
return {
|
|
685
|
-
compaction: { ...
|
|
660
|
+
compaction: { ...DEFAULT_COMPACTION_POLICY, ...overrides?.compaction },
|
|
686
661
|
retry: { ...DEFAULT_RETRY_SETTINGS, ...overrides?.retry },
|
|
687
662
|
budget: { ...DEFAULT_BUDGET_SETTINGS, ...overrides?.budget },
|
|
688
663
|
};
|
|
@@ -715,6 +690,30 @@ function addUsage(total, usage) {
|
|
|
715
690
|
total.reasoning = (total.reasoning ?? 0) + usage.reasoning;
|
|
716
691
|
}
|
|
717
692
|
}
|
|
693
|
+
function sumUsageState(state) {
|
|
694
|
+
const total = createEmptyUsage();
|
|
695
|
+
for (const usage of [...Object.values(state.models), ...Object.values(state.tools)]) {
|
|
696
|
+
addUsage(total, usage);
|
|
697
|
+
}
|
|
698
|
+
return total;
|
|
699
|
+
}
|
|
700
|
+
function subtractUsage(total, base) {
|
|
701
|
+
return {
|
|
702
|
+
input: total.input - base.input,
|
|
703
|
+
output: total.output - base.output,
|
|
704
|
+
cacheRead: total.cacheRead - base.cacheRead,
|
|
705
|
+
cacheWrite: total.cacheWrite - base.cacheWrite,
|
|
706
|
+
totalTokens: total.totalTokens - base.totalTokens,
|
|
707
|
+
cost: {
|
|
708
|
+
input: total.cost.input - base.cost.input,
|
|
709
|
+
output: total.cost.output - base.cost.output,
|
|
710
|
+
cacheRead: total.cost.cacheRead - base.cost.cacheRead,
|
|
711
|
+
cacheWrite: total.cost.cacheWrite - base.cost.cacheWrite,
|
|
712
|
+
total: total.cost.total - base.cost.total,
|
|
713
|
+
},
|
|
714
|
+
reasoning: total.reasoning === undefined ? undefined : total.reasoning - (base.reasoning ?? 0),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
718
717
|
export function copyUsage(usage) {
|
|
719
718
|
return { ...usage, cost: { ...usage.cost } };
|
|
720
719
|
}
|