@geminixiang/mikan 1.0.0-beta.26 → 1.0.0-beta.27
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 +23 -0
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +17 -3
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/slack/blocks.d.ts.map +1 -1
- package/dist/adapters/slack/blocks.js +13 -2
- package/dist/adapters/slack/blocks.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +13 -1
- 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 +3 -3
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +24 -1
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +73 -0
- package/dist/agent.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +8 -1
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/types.d.ts +2 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/harness/extensions/registry.d.ts +19 -7
- package/dist/harness/extensions/registry.d.ts.map +1 -1
- package/dist/harness/extensions/registry.js +65 -6
- package/dist/harness/extensions/registry.js.map +1 -1
- package/dist/harness/extensions/types.d.ts +27 -6
- package/dist/harness/extensions/types.d.ts.map +1 -1
- package/dist/harness/extensions/types.js.map +1 -1
- package/dist/harness/index.d.ts +1 -1
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/runner.d.ts +8 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +62 -11
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session-store.d.ts +5 -4
- package/dist/harness/session-store.d.ts.map +1 -1
- package/dist/harness/session-store.js +120 -28
- package/dist/harness/session-store.js.map +1 -1
- package/dist/runtime/conversation-runtime.js +86 -4
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +2 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts.map +1 -1
- package/dist/sessions/chat-history-sync.js +14 -3
- package/dist/sessions/chat-history-sync.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +1 -0
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +1 -0
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +1 -0
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +1 -0
- package/dist/tools/write.js.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -26,7 +26,7 @@ function generateEntryId(byId) {
|
|
|
26
26
|
}
|
|
27
27
|
return randomUUID();
|
|
28
28
|
}
|
|
29
|
-
/** Parse JSONL content tolerantly
|
|
29
|
+
/** Parse JSONL content tolerantly for compatibility with existing read-only callers. */
|
|
30
30
|
export function parseSessionFileEntries(content) {
|
|
31
31
|
const entries = [];
|
|
32
32
|
for (const line of content.split("\n")) {
|
|
@@ -42,7 +42,7 @@ export function parseSessionFileEntries(content) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
catch {
|
|
45
|
-
//
|
|
45
|
+
// This compatibility parser is intentionally tolerant. SessionStore.open is not.
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
return entries;
|
|
@@ -50,26 +50,120 @@ export function parseSessionFileEntries(content) {
|
|
|
50
50
|
function isSessionHeader(entry) {
|
|
51
51
|
return entry.type === "session" && typeof entry.id === "string";
|
|
52
52
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
function corrupted(filePath, detail) {
|
|
54
|
+
return new Error(`Session file is corrupted (${detail}): ${filePath}. Refusing to open for writing.`);
|
|
55
|
+
}
|
|
56
|
+
function parseSessionLine(line, filePath, lineNumber) {
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(line);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw corrupted(filePath, `line ${lineNumber} is not valid JSON`);
|
|
63
|
+
}
|
|
64
|
+
if (!parsed ||
|
|
65
|
+
typeof parsed !== "object" ||
|
|
66
|
+
typeof parsed.type !== "string") {
|
|
67
|
+
throw corrupted(filePath, `line ${lineNumber} is not a session object`);
|
|
68
|
+
}
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
function validateSessionTree(filePath, fileEntries) {
|
|
72
|
+
const records = fileEntries.slice(1);
|
|
73
|
+
const allIds = new Set();
|
|
74
|
+
const treeEntries = new Map();
|
|
75
|
+
for (const [index, record] of records.entries()) {
|
|
76
|
+
const lineNumber = index + 2;
|
|
77
|
+
if (isSessionHeader(record))
|
|
78
|
+
throw corrupted(filePath, `line ${lineNumber} repeats the header`);
|
|
79
|
+
const entry = record;
|
|
80
|
+
if (typeof entry.id !== "string" || !entry.id) {
|
|
81
|
+
throw corrupted(filePath, `line ${lineNumber} has no entry id`);
|
|
82
|
+
}
|
|
83
|
+
if (allIds.has(entry.id))
|
|
84
|
+
throw corrupted(filePath, `duplicate entry id ${entry.id}`);
|
|
85
|
+
allIds.add(entry.id);
|
|
86
|
+
if (entry.type !== "leaf")
|
|
87
|
+
treeEntries.set(entry.id, entry);
|
|
88
|
+
}
|
|
89
|
+
for (const [index, record] of records.entries()) {
|
|
90
|
+
const lineNumber = index + 2;
|
|
91
|
+
const entry = record;
|
|
92
|
+
if (entry.parentId !== null && typeof entry.parentId !== "string") {
|
|
93
|
+
throw corrupted(filePath, `line ${lineNumber} has an invalid parentId`);
|
|
94
|
+
}
|
|
95
|
+
if (entry.parentId === entry.id)
|
|
96
|
+
throw corrupted(filePath, `entry ${entry.id} is its own parent`);
|
|
97
|
+
if (entry.parentId !== null && !treeEntries.has(entry.parentId)) {
|
|
98
|
+
throw corrupted(filePath, `entry ${entry.id} references missing parent ${entry.parentId}`);
|
|
99
|
+
}
|
|
100
|
+
if (entry.type === "leaf") {
|
|
101
|
+
if (entry.targetId !== null && typeof entry.targetId !== "string") {
|
|
102
|
+
throw corrupted(filePath, `leaf ${entry.id} has an invalid target`);
|
|
103
|
+
}
|
|
104
|
+
if (entry.targetId !== null && !treeEntries.has(entry.targetId)) {
|
|
105
|
+
throw corrupted(filePath, `leaf ${entry.id} references missing target ${entry.targetId}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const entry of treeEntries.values()) {
|
|
110
|
+
const seen = new Set();
|
|
111
|
+
let current = entry;
|
|
112
|
+
while (current) {
|
|
113
|
+
if (seen.has(current.id))
|
|
114
|
+
throw corrupted(filePath, `parent cycle includes entry ${current.id}`);
|
|
115
|
+
seen.add(current.id);
|
|
116
|
+
current = current.parentId === null ? undefined : treeEntries.get(current.parentId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (const entry of treeEntries.values()) {
|
|
120
|
+
if (entry.type !== "compaction")
|
|
121
|
+
continue;
|
|
122
|
+
if (typeof entry.firstKeptEntryId !== "string" || !treeEntries.has(entry.firstKeptEntryId)) {
|
|
123
|
+
throw corrupted(filePath, `compaction ${entry.id} references missing first kept entry`);
|
|
124
|
+
}
|
|
125
|
+
let ancestorId = entry.parentId;
|
|
126
|
+
while (ancestorId !== null && ancestorId !== entry.firstKeptEntryId) {
|
|
127
|
+
ancestorId = treeEntries.get(ancestorId)?.parentId ?? null;
|
|
128
|
+
}
|
|
129
|
+
if (ancestorId === null) {
|
|
130
|
+
throw corrupted(filePath, `compaction ${entry.id} first kept entry ${entry.firstKeptEntryId} is not on its branch`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function loadValidatedSessionFile(filePath) {
|
|
59
135
|
if (!existsSync(filePath))
|
|
60
|
-
return [];
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
136
|
+
return { entries: [], discardedCrashTail: false };
|
|
137
|
+
const nonEmptyLines = readFileSync(filePath, "utf-8")
|
|
138
|
+
.split("\n")
|
|
139
|
+
.map((line, index) => ({ text: line.trim(), number: index + 1 }))
|
|
140
|
+
.filter(({ text }) => text.length > 0);
|
|
141
|
+
if (nonEmptyLines.length === 0)
|
|
142
|
+
return { entries: [], discardedCrashTail: false };
|
|
143
|
+
const entries = [];
|
|
144
|
+
let discardedCrashTail = false;
|
|
145
|
+
for (const [index, line] of nonEmptyLines.entries()) {
|
|
146
|
+
try {
|
|
147
|
+
entries.push(parseSessionLine(line.text, filePath, line.number));
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (index !== nonEmptyLines.length - 1 || index === 0)
|
|
151
|
+
throw error;
|
|
152
|
+
discardedCrashTail = true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
64
155
|
if (!isSessionHeader(entries[0]))
|
|
65
|
-
|
|
66
|
-
|
|
156
|
+
throw corrupted(filePath, "first non-empty line is not a valid header");
|
|
157
|
+
validateSessionTree(filePath, entries);
|
|
158
|
+
return { entries, discardedCrashTail };
|
|
67
159
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Load and validate a session file. Missing and whitespace-only files return
|
|
162
|
+
* no entries. A malformed final non-empty line is ignored as a crash tail;
|
|
163
|
+
* all other malformed JSON and invalid tree relationships throw.
|
|
164
|
+
*/
|
|
165
|
+
export function loadSessionFileEntries(filePath) {
|
|
166
|
+
return loadValidatedSessionFile(filePath).entries;
|
|
73
167
|
}
|
|
74
168
|
/**
|
|
75
169
|
* Append-oriented session store over one v3 JSONL file.
|
|
@@ -79,13 +173,14 @@ function hasSessionFileContent(filePath) {
|
|
|
79
173
|
* without a valid header gets its header materialized on the first append.
|
|
80
174
|
*/
|
|
81
175
|
export class SessionStore {
|
|
82
|
-
constructor(sessionFile, cwd, fileEntries) {
|
|
176
|
+
constructor(sessionFile, cwd, fileEntries, rewriteBeforeAppend = false) {
|
|
83
177
|
this.entries = [];
|
|
84
178
|
this.byId = new Map();
|
|
85
179
|
this.leafId = null;
|
|
86
180
|
this.sessionFile = sessionFile === null ? null : resolve(sessionFile);
|
|
87
181
|
this.header = fileEntries.length > 0 && isSessionHeader(fileEntries[0]) ? fileEntries[0] : null;
|
|
88
182
|
this.headerOnDisk = this.header !== null;
|
|
183
|
+
this.rewriteBeforeAppend = rewriteBeforeAppend;
|
|
89
184
|
this.sessionId = this.header?.id ?? randomUUID();
|
|
90
185
|
this.cwd = cwd;
|
|
91
186
|
for (const entry of fileEntries) {
|
|
@@ -110,14 +205,10 @@ export class SessionStore {
|
|
|
110
205
|
* @param cwdOverride Working directory override; defaults to the header cwd.
|
|
111
206
|
*/
|
|
112
207
|
static open(path, cwdOverride) {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
throw new Error(`Session file is corrupted (no valid session header): ${path}. ` +
|
|
116
|
-
`Refusing to open to avoid overwriting existing content.`);
|
|
117
|
-
}
|
|
118
|
-
const header = fileEntries.find(isSessionHeader);
|
|
208
|
+
const loaded = loadValidatedSessionFile(path);
|
|
209
|
+
const header = loaded.entries.find(isSessionHeader);
|
|
119
210
|
const cwd = cwdOverride ?? header?.cwd ?? process.cwd();
|
|
120
|
-
return new SessionStore(path, cwd,
|
|
211
|
+
return new SessionStore(path, cwd, loaded.entries, loaded.discardedCrashTail);
|
|
121
212
|
}
|
|
122
213
|
/** Create a new session file with a fresh header, replacing any existing file. */
|
|
123
214
|
static create(path, cwd, options) {
|
|
@@ -237,7 +328,7 @@ export class SessionStore {
|
|
|
237
328
|
persist(entry) {
|
|
238
329
|
if (this.sessionFile === null)
|
|
239
330
|
return;
|
|
240
|
-
if (!this.headerOnDisk) {
|
|
331
|
+
if (!this.headerOnDisk || this.rewriteBeforeAppend) {
|
|
241
332
|
this.header ??= {
|
|
242
333
|
type: "session",
|
|
243
334
|
version: CURRENT_SESSION_VERSION,
|
|
@@ -249,6 +340,7 @@ export class SessionStore {
|
|
|
249
340
|
mkdirSync(dirname(this.sessionFile), { recursive: true });
|
|
250
341
|
atomicWritePrivateFile(this.sessionFile, `${lines.join("\n")}\n`);
|
|
251
342
|
this.headerOnDisk = true;
|
|
343
|
+
this.rewriteBeforeAppend = false;
|
|
252
344
|
return;
|
|
253
345
|
}
|
|
254
346
|
appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/harness/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * mikan-owned session storage.\n *\n * Sessions are append-only trees stored as JSONL files: a `session` header\n * line followed by entries that carry `id`/`parentId` links. The format is\n * the v3 layout mikan has always written, and entry shapes are structurally\n * identical to pi-agent-core's `SessionTreeEntry`, so pi-agent-core's\n * context-building and compaction helpers operate on these entries directly.\n *\n * The store is synchronous by design: every mikan call site (session scope\n * resolution, chat-history sync, session view, admin portal) reads and\n * writes sessions inside synchronous flows.\n */\nimport { randomUUID } from \"crypto\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\nimport { buildSessionContext as buildContextFromEntries } from \"@earendil-works/pi-agent-core\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { atomicWritePrivateFile } from \"../utils/fs-atomic.js\";\nimport {\n CURRENT_SESSION_VERSION,\n type SessionContext,\n type SessionEntry,\n type SessionFileEntry,\n type SessionHeader,\n} from \"./types.js\";\n\n/** Distributive omit so each entry variant keeps its own fields. */\ntype PendingSessionEntry = SessionEntry extends infer TEntry\n ? TEntry extends SessionEntry\n ? Omit<TEntry, \"id\" | \"parentId\" | \"timestamp\">\n : never\n : never;\n\n/** Generate a unique short entry id (8 hex chars, collision-checked). */\nfunction generateEntryId(byId: Map<string, SessionEntry>): string {\n for (let i = 0; i < 100; i++) {\n const id = randomUUID().slice(0, 8);\n if (!byId.has(id)) return id;\n }\n return randomUUID();\n}\n\n/** Parse JSONL content tolerantly: malformed lines are skipped. */\nexport function parseSessionFileEntries(content: string): SessionFileEntry[] {\n const entries: SessionFileEntry[] = [];\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (\n parsed &&\n typeof parsed === \"object\" &&\n typeof (parsed as { type?: unknown }).type === \"string\"\n ) {\n entries.push(parsed as SessionFileEntry);\n }\n } catch {\n // Skip malformed lines\n }\n }\n return entries;\n}\n\nfunction isSessionHeader(entry: SessionFileEntry): entry is SessionHeader {\n return entry.type === \"session\" && typeof (entry as SessionHeader).id === \"string\";\n}\n\n/**\n * Load a session file. Returns no entries when the file is missing or its\n * first parseable line is not a valid session header (mirrors the tolerant\n * read behavior mikan has relied on). Throws only on I/O errors.\n */\nexport function loadSessionFileEntries(filePath: string): SessionFileEntry[] {\n if (!existsSync(filePath)) return [];\n const entries = parseSessionFileEntries(readFileSync(filePath, \"utf-8\"));\n if (entries.length === 0) return entries;\n if (!isSessionHeader(entries[0])) return [];\n return entries;\n}\n\n/** True when the file exists and holds any non-whitespace content on disk. */\nfunction hasSessionFileContent(filePath: string): boolean {\n if (!existsSync(filePath)) return false;\n return readFileSync(filePath, \"utf-8\").trim().length > 0;\n}\n\n/**\n * Append-oriented session store over one v3 JSONL file.\n *\n * Reading is tolerant (missing files and malformed lines yield an empty\n * session); appends persist immediately. A session opened from a file\n * without a valid header gets its header materialized on the first append.\n */\nexport class SessionStore {\n private readonly sessionFile: string | null;\n private readonly cwd: string;\n private readonly sessionId: string;\n private header: SessionHeader | null;\n private headerOnDisk: boolean;\n private entries: SessionEntry[] = [];\n private byId = new Map<string, SessionEntry>();\n private leafId: string | null = null;\n\n private constructor(sessionFile: string | null, cwd: string, fileEntries: SessionFileEntry[]) {\n this.sessionFile = sessionFile === null ? null : resolve(sessionFile);\n this.header = fileEntries.length > 0 && isSessionHeader(fileEntries[0]) ? fileEntries[0] : null;\n this.headerOnDisk = this.header !== null;\n this.sessionId = this.header?.id ?? randomUUID();\n this.cwd = cwd;\n for (const entry of fileEntries) {\n if (isSessionHeader(entry)) continue;\n if (entry.type === \"leaf\") {\n // Written by pi-agent-core JSONL sessions; mikan tracks the leaf implicitly.\n this.leafId = entry.targetId;\n continue;\n }\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n }\n }\n\n /**\n * Open a session file. A missing or empty file starts an empty session\n * whose header is written on the first append. A file that has content but\n * no leading session header is treated as corrupted and throws, rather than\n * being silently overwritten on the next append (which would erase the\n * existing history). Callers on read paths already wrap this in try/catch.\n * @param cwdOverride Working directory override; defaults to the header cwd.\n */\n static open(path: string, cwdOverride?: string): SessionStore {\n const fileEntries = loadSessionFileEntries(path);\n if (fileEntries.length === 0 && hasSessionFileContent(path)) {\n throw new Error(\n `Session file is corrupted (no valid session header): ${path}. ` +\n `Refusing to open to avoid overwriting existing content.`,\n );\n }\n const header = fileEntries.find(isSessionHeader);\n const cwd = cwdOverride ?? header?.cwd ?? process.cwd();\n return new SessionStore(path, cwd, fileEntries);\n }\n\n /** Create a new session file with a fresh header, replacing any existing file. */\n static create(path: string, cwd: string, options?: { id?: string }): SessionStore {\n const header: SessionHeader = {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: options?.id ?? randomUUID(),\n timestamp: new Date().toISOString(),\n cwd,\n };\n atomicWritePrivateFile(path, `${JSON.stringify(header)}\\n`);\n return new SessionStore(path, cwd, [header]);\n }\n\n /** Create an ephemeral session that never writes a session file. */\n static inMemory(cwd = process.cwd()): SessionStore {\n return new SessionStore(null, cwd, []);\n }\n\n getSessionFile(): string | undefined {\n return this.sessionFile ?? undefined;\n }\n\n isPersisted(): boolean {\n return this.sessionFile !== null;\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n /** Session header as read from disk, or null when the file had none. */\n getHeader(): SessionHeader | null {\n return this.header;\n }\n\n /** Stable session id: the header id, or a generated id for fresh sessions. */\n getSessionId(): string {\n return this.sessionId;\n }\n\n getLeafId(): string | null {\n return this.leafId;\n }\n\n getEntry(id: string): SessionEntry | undefined {\n return this.byId.get(id);\n }\n\n /** All session entries (header excluded). Returns a shallow copy. */\n getEntries(): SessionEntry[] {\n return [...this.entries];\n }\n\n /** Latest user-assigned session name, if any. */\n getSessionName(): string | undefined {\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const entry = this.entries[i];\n if (entry.type === \"session_info\") return entry.name?.trim() || undefined;\n }\n return undefined;\n }\n\n /**\n * Walk from an entry to the root, returning entries in path order\n * (root first). Defaults to the current leaf.\n */\n getBranch(fromId?: string): SessionEntry[] {\n const path: SessionEntry[] = [];\n let currentId = fromId ?? this.leafId;\n while (currentId) {\n const entry = this.byId.get(currentId);\n if (!entry) break;\n path.push(entry);\n currentId = entry.parentId;\n }\n return path.toReversed();\n }\n\n /**\n * Build the LLM context (messages, thinking level, model) from the current\n * branch, resolving compaction and branch summaries along the path.\n */\n buildSessionContext(): SessionContext {\n return buildContextFromEntries(this.getBranch());\n }\n\n /** Append a chat message as a child of the current leaf. Returns the entry id. */\n appendMessage(message: AgentMessage): string {\n return this.appendEntry({ type: \"message\", message });\n }\n\n /** Append an extension/application data entry (never sent to the LLM). */\n appendCustomEntry(customType: string, data?: unknown): string {\n return this.appendEntry({ type: \"custom\", customType, data });\n }\n\n /** Append a custom message entry that participates in LLM context. */\n appendCustomMessageEntry(\n customType: string,\n content: string | (TextContent | ImageContent)[],\n display: boolean,\n details?: unknown,\n ): string {\n return this.appendEntry({ type: \"custom_message\", customType, content, display, details });\n }\n\n /** Record a user-visible session name. */\n appendSessionInfo(name: string): string {\n return this.appendEntry({ type: \"session_info\", name });\n }\n\n /** Persist a compaction summary produced for this session. */\n appendCompaction(\n summary: string,\n firstKeptEntryId: string,\n tokensBefore: number,\n details?: unknown,\n fromHook?: boolean,\n ): string {\n return this.appendEntry({\n type: \"compaction\",\n summary,\n firstKeptEntryId,\n tokensBefore,\n ...(details !== undefined ? { details } : {}),\n ...(fromHook !== undefined ? { fromHook } : {}),\n });\n }\n\n private appendEntry(partial: PendingSessionEntry): string {\n const entry = {\n ...partial,\n id: generateEntryId(this.byId),\n parentId: this.leafId,\n timestamp: new Date().toISOString(),\n } as SessionEntry;\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n this.persist(entry);\n return entry.id;\n }\n\n private persist(entry: SessionEntry): void {\n if (this.sessionFile === null) return;\n if (!this.headerOnDisk) {\n this.header ??= {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: this.sessionId,\n timestamp: new Date().toISOString(),\n cwd: this.cwd,\n };\n const lines = [this.header, ...this.entries].map((line) => JSON.stringify(line));\n mkdirSync(dirname(this.sessionFile), { recursive: true });\n atomicWritePrivateFile(this.sessionFile, `${lines.join(\"\\n\")}\\n`);\n this.headerOnDisk = true;\n return;\n }\n appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\\n`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/harness/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * mikan-owned session storage.\n *\n * Sessions are append-only trees stored as JSONL files: a `session` header\n * line followed by entries that carry `id`/`parentId` links. The format is\n * the v3 layout mikan has always written, and entry shapes are structurally\n * identical to pi-agent-core's `SessionTreeEntry`, so pi-agent-core's\n * context-building and compaction helpers operate on these entries directly.\n *\n * The store is synchronous by design: every mikan call site (session scope\n * resolution, chat-history sync, session view, admin portal) reads and\n * writes sessions inside synchronous flows.\n */\nimport { randomUUID } from \"crypto\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\nimport { buildSessionContext as buildContextFromEntries } from \"@earendil-works/pi-agent-core\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { atomicWritePrivateFile } from \"../utils/fs-atomic.js\";\nimport {\n CURRENT_SESSION_VERSION,\n type SessionContext,\n type SessionEntry,\n type SessionFileEntry,\n type SessionHeader,\n} from \"./types.js\";\n\n/** Distributive omit so each entry variant keeps its own fields. */\ntype PendingSessionEntry = SessionEntry extends infer TEntry\n ? TEntry extends SessionEntry\n ? Omit<TEntry, \"id\" | \"parentId\" | \"timestamp\">\n : never\n : never;\n\n/** Generate a unique short entry id (8 hex chars, collision-checked). */\nfunction generateEntryId(byId: Map<string, SessionEntry>): string {\n for (let i = 0; i < 100; i++) {\n const id = randomUUID().slice(0, 8);\n if (!byId.has(id)) return id;\n }\n return randomUUID();\n}\n\n/** Parse JSONL content tolerantly for compatibility with existing read-only callers. */\nexport function parseSessionFileEntries(content: string): SessionFileEntry[] {\n const entries: SessionFileEntry[] = [];\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (\n parsed &&\n typeof parsed === \"object\" &&\n typeof (parsed as { type?: unknown }).type === \"string\"\n ) {\n entries.push(parsed as SessionFileEntry);\n }\n } catch {\n // This compatibility parser is intentionally tolerant. SessionStore.open is not.\n }\n }\n return entries;\n}\n\nfunction isSessionHeader(entry: SessionFileEntry): entry is SessionHeader {\n return entry.type === \"session\" && typeof (entry as SessionHeader).id === \"string\";\n}\n\ntype LoadedSessionFile = {\n entries: SessionFileEntry[];\n discardedCrashTail: boolean;\n};\n\nfunction corrupted(filePath: string, detail: string): Error {\n return new Error(\n `Session file is corrupted (${detail}): ${filePath}. Refusing to open for writing.`,\n );\n}\n\nfunction parseSessionLine(line: string, filePath: string, lineNumber: number): SessionFileEntry {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n throw corrupted(filePath, `line ${lineNumber} is not valid JSON`);\n }\n if (\n !parsed ||\n typeof parsed !== \"object\" ||\n typeof (parsed as { type?: unknown }).type !== \"string\"\n ) {\n throw corrupted(filePath, `line ${lineNumber} is not a session object`);\n }\n return parsed as SessionFileEntry;\n}\n\nfunction validateSessionTree(filePath: string, fileEntries: SessionFileEntry[]): void {\n const records = fileEntries.slice(1);\n const allIds = new Set<string>();\n const treeEntries = new Map<string, SessionEntry>();\n\n for (const [index, record] of records.entries()) {\n const lineNumber = index + 2;\n if (isSessionHeader(record)) throw corrupted(filePath, `line ${lineNumber} repeats the header`);\n const entry = record as SessionEntry;\n if (typeof entry.id !== \"string\" || !entry.id) {\n throw corrupted(filePath, `line ${lineNumber} has no entry id`);\n }\n if (allIds.has(entry.id)) throw corrupted(filePath, `duplicate entry id ${entry.id}`);\n allIds.add(entry.id);\n if (entry.type !== \"leaf\") treeEntries.set(entry.id, entry);\n }\n\n for (const [index, record] of records.entries()) {\n const lineNumber = index + 2;\n const entry = record as SessionEntry;\n if (entry.parentId !== null && typeof entry.parentId !== \"string\") {\n throw corrupted(filePath, `line ${lineNumber} has an invalid parentId`);\n }\n if (entry.parentId === entry.id)\n throw corrupted(filePath, `entry ${entry.id} is its own parent`);\n if (entry.parentId !== null && !treeEntries.has(entry.parentId)) {\n throw corrupted(filePath, `entry ${entry.id} references missing parent ${entry.parentId}`);\n }\n if (entry.type === \"leaf\") {\n if (entry.targetId !== null && typeof entry.targetId !== \"string\") {\n throw corrupted(filePath, `leaf ${entry.id} has an invalid target`);\n }\n if (entry.targetId !== null && !treeEntries.has(entry.targetId)) {\n throw corrupted(filePath, `leaf ${entry.id} references missing target ${entry.targetId}`);\n }\n }\n }\n\n for (const entry of treeEntries.values()) {\n const seen = new Set<string>();\n let current: SessionEntry | undefined = entry;\n while (current) {\n if (seen.has(current.id))\n throw corrupted(filePath, `parent cycle includes entry ${current.id}`);\n seen.add(current.id);\n current = current.parentId === null ? undefined : treeEntries.get(current.parentId);\n }\n }\n\n for (const entry of treeEntries.values()) {\n if (entry.type !== \"compaction\") continue;\n if (typeof entry.firstKeptEntryId !== \"string\" || !treeEntries.has(entry.firstKeptEntryId)) {\n throw corrupted(filePath, `compaction ${entry.id} references missing first kept entry`);\n }\n let ancestorId = entry.parentId;\n while (ancestorId !== null && ancestorId !== entry.firstKeptEntryId) {\n ancestorId = treeEntries.get(ancestorId)?.parentId ?? null;\n }\n if (ancestorId === null) {\n throw corrupted(\n filePath,\n `compaction ${entry.id} first kept entry ${entry.firstKeptEntryId} is not on its branch`,\n );\n }\n }\n}\n\nfunction loadValidatedSessionFile(filePath: string): LoadedSessionFile {\n if (!existsSync(filePath)) return { entries: [], discardedCrashTail: false };\n const nonEmptyLines = readFileSync(filePath, \"utf-8\")\n .split(\"\\n\")\n .map((line, index) => ({ text: line.trim(), number: index + 1 }))\n .filter(({ text }) => text.length > 0);\n if (nonEmptyLines.length === 0) return { entries: [], discardedCrashTail: false };\n\n const entries: SessionFileEntry[] = [];\n let discardedCrashTail = false;\n for (const [index, line] of nonEmptyLines.entries()) {\n try {\n entries.push(parseSessionLine(line.text, filePath, line.number));\n } catch (error) {\n if (index !== nonEmptyLines.length - 1 || index === 0) throw error;\n discardedCrashTail = true;\n }\n }\n if (!isSessionHeader(entries[0]))\n throw corrupted(filePath, \"first non-empty line is not a valid header\");\n validateSessionTree(filePath, entries);\n return { entries, discardedCrashTail };\n}\n\n/**\n * Load and validate a session file. Missing and whitespace-only files return\n * no entries. A malformed final non-empty line is ignored as a crash tail;\n * all other malformed JSON and invalid tree relationships throw.\n */\nexport function loadSessionFileEntries(filePath: string): SessionFileEntry[] {\n return loadValidatedSessionFile(filePath).entries;\n}\n\n/**\n * Append-oriented session store over one v3 JSONL file.\n *\n * Reading is tolerant (missing files and malformed lines yield an empty\n * session); appends persist immediately. A session opened from a file\n * without a valid header gets its header materialized on the first append.\n */\nexport class SessionStore {\n private readonly sessionFile: string | null;\n private readonly cwd: string;\n private readonly sessionId: string;\n private header: SessionHeader | null;\n private headerOnDisk: boolean;\n private rewriteBeforeAppend: boolean;\n private entries: SessionEntry[] = [];\n private byId = new Map<string, SessionEntry>();\n private leafId: string | null = null;\n\n private constructor(\n sessionFile: string | null,\n cwd: string,\n fileEntries: SessionFileEntry[],\n rewriteBeforeAppend = false,\n ) {\n this.sessionFile = sessionFile === null ? null : resolve(sessionFile);\n this.header = fileEntries.length > 0 && isSessionHeader(fileEntries[0]) ? fileEntries[0] : null;\n this.headerOnDisk = this.header !== null;\n this.rewriteBeforeAppend = rewriteBeforeAppend;\n this.sessionId = this.header?.id ?? randomUUID();\n this.cwd = cwd;\n for (const entry of fileEntries) {\n if (isSessionHeader(entry)) continue;\n if (entry.type === \"leaf\") {\n // Written by pi-agent-core JSONL sessions; mikan tracks the leaf implicitly.\n this.leafId = entry.targetId;\n continue;\n }\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n }\n }\n\n /**\n * Open a session file. A missing or empty file starts an empty session\n * whose header is written on the first append. A file that has content but\n * no leading session header is treated as corrupted and throws, rather than\n * being silently overwritten on the next append (which would erase the\n * existing history). Callers on read paths already wrap this in try/catch.\n * @param cwdOverride Working directory override; defaults to the header cwd.\n */\n static open(path: string, cwdOverride?: string): SessionStore {\n const loaded = loadValidatedSessionFile(path);\n const header = loaded.entries.find(isSessionHeader);\n const cwd = cwdOverride ?? header?.cwd ?? process.cwd();\n return new SessionStore(path, cwd, loaded.entries, loaded.discardedCrashTail);\n }\n\n /** Create a new session file with a fresh header, replacing any existing file. */\n static create(path: string, cwd: string, options?: { id?: string }): SessionStore {\n const header: SessionHeader = {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: options?.id ?? randomUUID(),\n timestamp: new Date().toISOString(),\n cwd,\n };\n atomicWritePrivateFile(path, `${JSON.stringify(header)}\\n`);\n return new SessionStore(path, cwd, [header]);\n }\n\n /** Create an ephemeral session that never writes a session file. */\n static inMemory(cwd = process.cwd()): SessionStore {\n return new SessionStore(null, cwd, []);\n }\n\n getSessionFile(): string | undefined {\n return this.sessionFile ?? undefined;\n }\n\n isPersisted(): boolean {\n return this.sessionFile !== null;\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n /** Session header as read from disk, or null when the file had none. */\n getHeader(): SessionHeader | null {\n return this.header;\n }\n\n /** Stable session id: the header id, or a generated id for fresh sessions. */\n getSessionId(): string {\n return this.sessionId;\n }\n\n getLeafId(): string | null {\n return this.leafId;\n }\n\n getEntry(id: string): SessionEntry | undefined {\n return this.byId.get(id);\n }\n\n /** All session entries (header excluded). Returns a shallow copy. */\n getEntries(): SessionEntry[] {\n return [...this.entries];\n }\n\n /** Latest user-assigned session name, if any. */\n getSessionName(): string | undefined {\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const entry = this.entries[i];\n if (entry.type === \"session_info\") return entry.name?.trim() || undefined;\n }\n return undefined;\n }\n\n /**\n * Walk from an entry to the root, returning entries in path order\n * (root first). Defaults to the current leaf.\n */\n getBranch(fromId?: string): SessionEntry[] {\n const path: SessionEntry[] = [];\n let currentId = fromId ?? this.leafId;\n while (currentId) {\n const entry = this.byId.get(currentId);\n if (!entry) break;\n path.push(entry);\n currentId = entry.parentId;\n }\n return path.toReversed();\n }\n\n /**\n * Build the LLM context (messages, thinking level, model) from the current\n * branch, resolving compaction and branch summaries along the path.\n */\n buildSessionContext(): SessionContext {\n return buildContextFromEntries(this.getBranch());\n }\n\n /** Append a chat message as a child of the current leaf. Returns the entry id. */\n appendMessage(message: AgentMessage): string {\n return this.appendEntry({ type: \"message\", message });\n }\n\n /** Append an extension/application data entry (never sent to the LLM). */\n appendCustomEntry(customType: string, data?: unknown): string {\n return this.appendEntry({ type: \"custom\", customType, data });\n }\n\n /** Append a custom message entry that participates in LLM context. */\n appendCustomMessageEntry(\n customType: string,\n content: string | (TextContent | ImageContent)[],\n display: boolean,\n details?: unknown,\n ): string {\n return this.appendEntry({ type: \"custom_message\", customType, content, display, details });\n }\n\n /** Record a user-visible session name. */\n appendSessionInfo(name: string): string {\n return this.appendEntry({ type: \"session_info\", name });\n }\n\n /** Persist a compaction summary produced for this session. */\n appendCompaction(\n summary: string,\n firstKeptEntryId: string,\n tokensBefore: number,\n details?: unknown,\n fromHook?: boolean,\n ): string {\n return this.appendEntry({\n type: \"compaction\",\n summary,\n firstKeptEntryId,\n tokensBefore,\n ...(details !== undefined ? { details } : {}),\n ...(fromHook !== undefined ? { fromHook } : {}),\n });\n }\n\n private appendEntry(partial: PendingSessionEntry): string {\n const entry = {\n ...partial,\n id: generateEntryId(this.byId),\n parentId: this.leafId,\n timestamp: new Date().toISOString(),\n } as SessionEntry;\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n this.persist(entry);\n return entry.id;\n }\n\n private persist(entry: SessionEntry): void {\n if (this.sessionFile === null) return;\n if (!this.headerOnDisk || this.rewriteBeforeAppend) {\n this.header ??= {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: this.sessionId,\n timestamp: new Date().toISOString(),\n cwd: this.cwd,\n };\n const lines = [this.header, ...this.entries].map((line) => JSON.stringify(line));\n mkdirSync(dirname(this.sessionFile), { recursive: true });\n atomicWritePrivateFile(this.sessionFile, `${lines.join(\"\\n\")}\\n`);\n this.headerOnDisk = true;\n this.rewriteBeforeAppend = false;\n return;\n }\n appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\\n`);\n }\n}\n"]}
|
|
@@ -35,6 +35,7 @@ class ConversationRuntimeImpl {
|
|
|
35
35
|
this.options = options;
|
|
36
36
|
this.sessions = new SessionLifecycle();
|
|
37
37
|
this.inFlightRuns = new Set();
|
|
38
|
+
this.memoryMaintenanceSettlements = new Set();
|
|
38
39
|
this.chatSessionManager = new ChatHistorySync();
|
|
39
40
|
this.isShuttingDown = false;
|
|
40
41
|
this.commandServices = {
|
|
@@ -86,16 +87,90 @@ class ConversationRuntimeImpl {
|
|
|
86
87
|
log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);
|
|
87
88
|
state.stopRequested = true;
|
|
88
89
|
state.runner.abort();
|
|
89
|
-
state.running = false;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
async handleNewCommand(sessionKey, conversationId, bot) {
|
|
92
|
+
async handleNewCommand(sessionKey, conversationId, bot, message, responder, platform) {
|
|
93
93
|
assertSessionKeyBelongsToConversation(sessionKey, conversationId);
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
94
|
+
const activeState = this.sessions.get(sessionKey);
|
|
95
|
+
if (activeState?.running) {
|
|
96
|
+
const activeSettlement = activeState.runSettlement;
|
|
97
|
+
if (activeSettlement && this.memoryMaintenanceSettlements.has(activeSettlement)) {
|
|
98
|
+
await activeSettlement;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
activeState.stopRequested = true;
|
|
102
|
+
activeState.runner.abort();
|
|
103
|
+
await activeSettlement;
|
|
104
|
+
}
|
|
105
|
+
const state = await this.getOrCreateState({
|
|
106
|
+
conversationId,
|
|
107
|
+
sessionKey,
|
|
108
|
+
conversationKind: message.conversationKind,
|
|
109
|
+
});
|
|
110
|
+
if (state.running) {
|
|
111
|
+
const activeSettlement = state.runSettlement;
|
|
112
|
+
if (activeSettlement && this.memoryMaintenanceSettlements.has(activeSettlement)) {
|
|
113
|
+
await activeSettlement;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
96
116
|
state.stopRequested = true;
|
|
97
117
|
state.runner.abort();
|
|
118
|
+
await activeSettlement;
|
|
119
|
+
return this.handleNewCommand(sessionKey, conversationId, bot, message, responder, platform);
|
|
120
|
+
}
|
|
121
|
+
state.running = true;
|
|
122
|
+
state.stopRequested = false;
|
|
123
|
+
state.startedAt = Date.now();
|
|
124
|
+
state.lastActivityAt = Date.now();
|
|
125
|
+
const maintenanceSettlement = Promise.resolve().then(async () => {
|
|
126
|
+
let working = false;
|
|
127
|
+
try {
|
|
128
|
+
await responder.setWorking(true);
|
|
129
|
+
working = true;
|
|
130
|
+
let result;
|
|
131
|
+
try {
|
|
132
|
+
result = await state.runner.maintainMemory(message, platform);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
await responder.respondDiagnostic(`Could not preserve memory, so the current conversation was not reset. ${err instanceof Error ? err.message : String(err)}`, { style: "error" });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (result.stopReason === "error" || result.stopReason === "blocked") {
|
|
139
|
+
const detail = result.errorMessage ? ` ${result.errorMessage}` : "";
|
|
140
|
+
await responder.respondDiagnostic(`Could not preserve memory, so the current conversation was not reset.${detail}`, { style: "error" });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
await responder.setWorking(false);
|
|
144
|
+
working = false;
|
|
145
|
+
await this.resetSession(sessionKey, conversationId, bot);
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
try {
|
|
149
|
+
if (working)
|
|
150
|
+
await responder.setWorking(false);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
state.running = false;
|
|
154
|
+
state.startedAt = 0;
|
|
155
|
+
state.lastAccessedAt = Date.now();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
state.runSettlement = maintenanceSettlement;
|
|
160
|
+
this.memoryMaintenanceSettlements.add(maintenanceSettlement);
|
|
161
|
+
this.inFlightRuns.add(maintenanceSettlement);
|
|
162
|
+
try {
|
|
163
|
+
await maintenanceSettlement;
|
|
98
164
|
}
|
|
165
|
+
finally {
|
|
166
|
+
this.memoryMaintenanceSettlements.delete(maintenanceSettlement);
|
|
167
|
+
this.inFlightRuns.delete(maintenanceSettlement);
|
|
168
|
+
if (state.runSettlement === maintenanceSettlement)
|
|
169
|
+
state.runSettlement = undefined;
|
|
170
|
+
this.sessions.evictIdle();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async resetSession(sessionKey, conversationId, bot) {
|
|
99
174
|
const conversationDir = join(this.options.workingDir, conversationId);
|
|
100
175
|
const runtimeCwd = runtimeCwdForSandbox(this.options.sandbox, this.options.workingDir, conversationId);
|
|
101
176
|
this.chatSessionManager.resetSession({ conversationDir, sessionKey, cwd: runtimeCwd });
|
|
@@ -120,6 +195,7 @@ class ConversationRuntimeImpl {
|
|
|
120
195
|
responder: context.responder,
|
|
121
196
|
platform: context.platform.name,
|
|
122
197
|
platformUserId: event.user,
|
|
198
|
+
platformUserName: context.message.userName,
|
|
123
199
|
conversationId,
|
|
124
200
|
vaultConversationId: event.vaultConversationId,
|
|
125
201
|
sessionKey,
|
|
@@ -129,6 +205,9 @@ class ConversationRuntimeImpl {
|
|
|
129
205
|
});
|
|
130
206
|
if (handledCommand)
|
|
131
207
|
return;
|
|
208
|
+
const activeSettlement = this.sessions.get(sessionKey)?.runSettlement;
|
|
209
|
+
if (activeSettlement)
|
|
210
|
+
await activeSettlement;
|
|
132
211
|
const conversationDir = join(this.options.workingDir, conversationId);
|
|
133
212
|
const waitedForParent = await waitForThreadSessionBootstrap({
|
|
134
213
|
parentSessionKey: conversationId,
|
|
@@ -211,12 +290,15 @@ class ConversationRuntimeImpl {
|
|
|
211
290
|
}
|
|
212
291
|
})();
|
|
213
292
|
this.inFlightRuns.add(runPromise);
|
|
293
|
+
state.runSettlement = runPromise;
|
|
214
294
|
Sentry.metrics.gauge("agent.sessions.active", this.inFlightRuns.size);
|
|
215
295
|
try {
|
|
216
296
|
await runPromise;
|
|
217
297
|
}
|
|
218
298
|
finally {
|
|
219
299
|
this.inFlightRuns.delete(runPromise);
|
|
300
|
+
if (state.runSettlement === runPromise)
|
|
301
|
+
state.runSettlement = undefined;
|
|
220
302
|
}
|
|
221
303
|
}
|
|
222
304
|
async runWithInstrumentation(context, meta, body) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-runtime.js","sourceRoot":"","sources":["../../src/runtime/conversation-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n MessagingBot,\n ConversationContext,\n ConversationEvent,\n PlatformName,\n RunningSession,\n} from \"../adapter.js\";\nimport { createRunner } from \"../agent.js\";\nimport { defaultCommandHandlers, dispatchCommand } from \"../commands/registry.js\";\nimport type { CommandHandler, CommandServices } from \"../commands/types.js\";\nimport { isPrivateConversation } from \"../commands/utils.js\";\nimport * as log from \"../log.js\";\nimport {\n addLifecycleBreadcrumb,\n applyRunScope,\n createRunAttributionAttributes,\n registerTraceAttribution,\n reportUserFacingError,\n} from \"../observability/sentry.js\";\nimport {\n ChatHistorySync,\n hasMaterializedChatSession,\n waitForThreadSessionBootstrap,\n} from \"../sessions/chat-history-sync.js\";\nimport {\n assertSessionKeyBelongsToConversation,\n deriveSessionKey,\n} from \"../sessions/session-key.js\";\nimport { formatNothingRunning, formatStopped, formatStopping } from \"../platform-messages.js\";\nimport * as Sentry from \"@sentry/node\";\nimport { join } from \"path\";\nimport { getUnresolvedSandboxPathContext } from \"../sandbox/index.js\";\nimport { disabledVaultManager } from \"../vault/disabled.js\";\nimport type { ConversationRuntimeState } from \"./types.js\";\nimport { SessionLifecycle } from \"./session-lifecycle.js\";\n\ntype ConversationState = ConversationRuntimeState;\n\nexport type {\n RunSessionOptions,\n ConversationRuntime,\n ConversationRuntimeOptions,\n} from \"./types.js\";\nimport type {\n RunSessionOptions,\n ConversationRuntime,\n ConversationRuntimeOptions,\n SessionStateOptions,\n} from \"./types.js\";\n\n/**\n * Placeholder token store for embedders that run without the web portal.\n * Command handlers guard on `portalBaseUrl` before minting tokens, so this\n * only fires when a portal URL is configured without its backing store.\n */\nfunction portalNotConfiguredTokenStore(portal: string): { create: () => never } {\n return {\n create: () => {\n throw new Error(`${portal} portal not configured`);\n },\n };\n}\n\nfunction runtimeCwdForSandbox(\n sandbox: ConversationRuntimeOptions[\"sandbox\"],\n hostWorkspaceRoot: string,\n conversationId: string,\n): string {\n const runtimeWorkspaceRoot = getUnresolvedSandboxPathContext(\n sandbox,\n hostWorkspaceRoot,\n ).runtimeWorkspaceRoot;\n return `${runtimeWorkspaceRoot.replace(/\\/+$/, \"\")}/${conversationId}`;\n}\n\nexport function createConversationRuntime(\n options: ConversationRuntimeOptions,\n): ConversationRuntime {\n return new ConversationRuntimeImpl(options);\n}\n\nclass ConversationRuntimeImpl implements ConversationRuntime {\n private readonly sessions = new SessionLifecycle();\n private readonly inFlightRuns = new Set<Promise<void>>();\n private readonly chatSessionManager = new ChatHistorySync();\n private readonly commandServices: CommandServices;\n private readonly commandHandlers: readonly CommandHandler[];\n private isShuttingDown = false;\n\n constructor(private readonly options: ConversationRuntimeOptions) {\n this.commandServices = {\n ...options,\n resourceController: options.resourceController ?? options.provisioner,\n vaultManager: options.vaultManager ?? disabledVaultManager,\n linkTokenStore: options.linkTokenStore ?? portalNotConfiguredTokenStore(\"Login\"),\n sessionViewTokenStore:\n options.sessionViewTokenStore ?? portalNotConfiguredTokenStore(\"Session viewer\"),\n adminTokenStore: options.adminTokenStore ?? portalNotConfiguredTokenStore(\"Admin\"),\n runtime: this,\n };\n this.commandHandlers = options.commandHandlers ?? defaultCommandHandlers();\n }\n\n isRunning(sessionKey: string): boolean {\n const state = this.sessions.get(sessionKey);\n return !!state?.running;\n }\n\n getRunningSessions(): RunningSession[] {\n const sessions: RunningSession[] = [];\n for (const [sessionKey, state] of this.sessions.runningStates()) {\n if (state.startedAt) {\n const currentStep = state.runner.getCurrentStep();\n sessions.push({\n sessionKey,\n startedAt: state.startedAt,\n lastActivityAt: state.lastActivityAt,\n currentTool: currentStep?.label || currentStep?.toolName,\n });\n }\n }\n return sessions;\n }\n\n async handleStop(sessionKey: string, conversationId: string, bot: MessagingBot): Promise<void> {\n assertSessionKeyBelongsToConversation(sessionKey, conversationId);\n const state = this.sessions.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n const ts = await bot.postMessage(conversationId, formatStopping(bot));\n state.stopMessageTs = ts;\n } else {\n await bot.postMessage(conversationId, formatNothingRunning(bot));\n }\n }\n\n forceStop(sessionKey: string): void {\n const state = this.sessions.get(sessionKey);\n if (state?.running) {\n log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);\n state.stopRequested = true;\n state.runner.abort();\n state.running = false;\n }\n }\n\n async handleNewCommand(\n sessionKey: string,\n conversationId: string,\n bot: MessagingBot,\n ): Promise<void> {\n assertSessionKeyBelongsToConversation(sessionKey, conversationId);\n const state = this.sessions.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n }\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const runtimeCwd = runtimeCwdForSandbox(\n this.options.sandbox,\n this.options.workingDir,\n conversationId,\n );\n this.chatSessionManager.resetSession({ conversationDir, sessionKey, cwd: runtimeCwd });\n\n this.sessions.discard(sessionKey);\n\n log.logInfo(`[${conversationId}] Session reset: ${sessionKey}`);\n await bot.postMessage(conversationId, \"Conversation reset. Send a new message to start fresh.\");\n }\n\n async handleEvent(\n event: ConversationEvent,\n bot: MessagingBot,\n context: ConversationContext,\n ): Promise<void> {\n const sessionKey = deriveSessionKey(event);\n await this.sessions.enqueue(sessionKey, () => this.runSession({ event, bot, context }));\n }\n\n async runSession({ event, bot, context }: RunSessionOptions): Promise<void> {\n const conversationId = event.conversationId;\n if (this.isShuttingDown) {\n log.logInfo(\n `[${conversationId}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const sessionKey = deriveSessionKey(event);\n const privateConversation = isPrivateConversation(event);\n const handledCommand = await dispatchCommand(this.commandHandlers, {\n bot,\n responder: context.responder,\n platform: context.platform.name as PlatformName,\n platformUserId: event.user,\n conversationId,\n vaultConversationId: event.vaultConversationId,\n sessionKey,\n commandText: event.text,\n privateConversation,\n services: this.commandServices,\n });\n if (handledCommand) return;\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const waitedForParent = await waitForThreadSessionBootstrap({\n parentSessionKey: conversationId,\n sessionKey,\n hasThreadSession: () => hasMaterializedChatSession({ conversationDir, sessionKey }),\n isParentRunning: () => this.sessions.get(conversationId)?.running === true,\n });\n if (waitedForParent) {\n log.logInfo(\n `[${conversationId}] Delayed thread bootstrap until parent session sealed: ${sessionKey}`,\n );\n }\n\n let state: ConversationState;\n try {\n state = await this.getOrCreateState({\n conversationId,\n sessionKey,\n currentMessageId: event.ts,\n conversationKind: event.conversationKind,\n });\n state.runner.syncChatHistory(event.ts);\n\n // Extension-contributed commands: deterministic dispatch, no agent run.\n // Built-in commands already had their chance above, so extensions can\n // never shadow them; unmatched slash text falls through to the agent.\n if (event.text.trim().startsWith(\"/\")) {\n const handled = await state.runner.tryExtensionCommand(context.message, context.responder);\n if (handled) {\n state.lastAccessedAt = Date.now();\n return;\n }\n }\n } catch (err) {\n reportUserFacingError(err, {\n domain: \"mikan\",\n surface: \"session_setup\",\n operation: \"get_or_create_state\",\n severity: \"error\",\n platform: context.platform.name,\n context: {\n conversationId,\n sessionKey,\n messageId: context.message.id,\n threadTs: context.message.threadTs,\n attachmentCount: context.message.attachments?.length ?? 0,\n },\n });\n throw err;\n }\n\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${conversationId}] Starting run: ${event.text.substring(0, 50)}`);\n\n const runPromise = (async () => {\n try {\n const result = await this.runWithInstrumentation(\n context,\n { conversationId, sessionKey, startedAt: state.startedAt },\n async () => {\n await context.responder.setTyping(true);\n await context.responder.setWorking(true);\n try {\n return await state.runner.run(context.message, context.responder, context.platform);\n } finally {\n await context.responder.setWorking(false);\n }\n },\n );\n\n if (result?.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(conversationId, state.stopMessageTs, formatStopped(bot));\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(conversationId, formatStopped(bot));\n }\n }\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size - 1);\n this.sessions.evictIdle();\n }\n })();\n\n this.inFlightRuns.add(runPromise);\n Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size);\n try {\n await runPromise;\n } finally {\n this.inFlightRuns.delete(runPromise);\n }\n }\n\n private async runWithInstrumentation(\n context: ConversationContext,\n meta: {\n conversationId: string;\n sessionKey: string;\n startedAt: number;\n },\n body: () => Promise<{ stopReason: string; errorMessage?: string }>,\n ): Promise<{ stopReason: string; errorMessage?: string } | undefined> {\n const { conversationId, sessionKey, startedAt } = meta;\n const { message, platform } = context;\n\n const attribution = createRunAttributionAttributes({\n conversationId,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n });\n\n Sentry.metrics.count(\"agent.run.started\", 1, {\n attributes: attribution,\n });\n\n return Sentry.startSpan(\n { name: \"agent.run\", op: \"agent\", attributes: attribution },\n async (span) =>\n Sentry.withScope(async (scope) => {\n registerTraceAttribution(span, attribution);\n applyRunScope(scope, {\n conversationId,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n });\n addLifecycleBreadcrumb(\"agent.run.started\", {\n channel_id: conversationId,\n platform: platform.name,\n has_attachments: (message.attachments?.length ?? 0) > 0,\n });\n\n try {\n const result = await body();\n const durationMs = Date.now() - startedAt;\n const completionAttrs = {\n ...attribution,\n stop_reason: result.stopReason,\n };\n Sentry.metrics.distribution(\"agent.run.duration\", durationMs, {\n unit: \"millisecond\",\n attributes: completionAttrs,\n });\n Sentry.metrics.count(\"agent.run.completed\", 1, { attributes: completionAttrs });\n addLifecycleBreadcrumb(\"agent.run.completed\", {\n channel_id: conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n duration_ms: durationMs,\n });\n return result;\n } catch (err) {\n scope.setContext(\"agent_run_error\", {\n conversationId,\n sessionKey,\n platform: platform.name,\n messageId: message.id,\n threadTs: message.threadTs,\n });\n reportUserFacingError(err, {\n domain: \"mikan\",\n surface: \"agent_run\",\n operation: \"run\",\n severity: \"error\",\n platform: platform.name,\n context: {\n conversationId,\n sessionKey,\n messageId: message.id,\n threadTs: message.threadTs,\n attachmentCount: message.attachments?.length ?? 0,\n },\n });\n Sentry.metrics.count(\"agent.run.errors\", 1, {\n attributes: attribution,\n });\n log.logWarning(\n `[${conversationId}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n }),\n );\n }\n\n switchConversationModel(conversationId: string, _provider: string, _model: string): boolean {\n return this.clearConversationStates(\n conversationId,\n `[${conversationId}] Model switched; cleared cached session runners`,\n );\n }\n\n refreshConversationEnvironment(conversationId: string): boolean {\n return this.clearConversationStates(\n conversationId,\n `[${conversationId}] Environment refreshed; cleared cached session runners`,\n );\n }\n\n refreshAllConversations(): { busy: string[] } {\n const busy: string[] = [];\n for (const conversationId of this.sessions.conversationIds()) {\n const cleared = this.clearConversationStates(\n conversationId,\n `[${conversationId}] Global settings changed; cleared cached session runners`,\n );\n if (!cleared) busy.push(conversationId);\n }\n return { busy };\n }\n\n private clearConversationStates(conversationId: string, message: string): boolean {\n const cleared = this.sessions.clearConversation(conversationId);\n if (cleared) log.logInfo(message);\n return cleared;\n }\n\n private async getOrCreateState(\n options: SessionStateOptions & { currentMessageId?: string },\n ): Promise<ConversationState> {\n const { conversationId, sessionKey, currentMessageId } = options;\n const existing = this.sessions.get(sessionKey);\n if (existing?.running) return existing;\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const runtimeCwd = runtimeCwdForSandbox(\n this.options.sandbox,\n this.options.workingDir,\n conversationId,\n );\n const sessionScope = await this.chatSessionManager.resolveSessionScope({\n conversationDir,\n sessionKey,\n cwd: runtimeCwd,\n currentMessageId,\n rotateTopLevelSession: options.conversationKind === \"shared\" && sessionKey === conversationId,\n });\n\n if (existing && existing.sessionFile === sessionScope.contextFile) {\n existing.lastAccessedAt = Date.now();\n return existing;\n }\n\n // A stale state (rotated session file) is being replaced: release the old\n // runner's extension resources before the new one takes the slot.\n if (existing) this.sessions.discard(sessionKey);\n\n const state: ConversationState = {\n running: false,\n runner: await createRunner({\n sandboxConfig: this.options.sandbox,\n sessionKey,\n conversationId,\n conversationDir,\n workspaceDir: this.options.workingDir,\n sessionScope,\n vaultManager: this.options.vaultManager,\n provisioner: this.options.provisioner,\n resourceController: this.options.resourceController,\n sessionView: this.options.sessionViewTokenStore\n ? {\n tokenStore: this.options.sessionViewTokenStore,\n portalBaseUrl: this.options.portalBaseUrl,\n }\n : undefined,\n platformNotifier: this.options.platformNotifier,\n platformReactor: this.options.platformReactor,\n platformUploader: this.options.platformUploader,\n platformToolPackFactories: this.options.platformToolPackFactories,\n models: this.options.models,\n }),\n stopRequested: false,\n lastAccessedAt: Date.now(),\n sessionFile: sessionScope.contextFile,\n startedAt: 0,\n };\n this.sessions.set(sessionKey, state);\n return state;\n }\n\n async shutdown(timeoutMs = 30_000): Promise<void> {\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + timeoutMs;\n while (this.inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (this.inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${this.inFlightRuns.size} runs still in progress`);\n reportUserFacingError(new Error(\"Shutdown forced with in-flight agent runs\"), {\n domain: \"mikan\",\n surface: \"shutdown\",\n operation: \"force_exit_with_inflight_runs\",\n severity: \"warning\",\n context: { inFlightRuns: this.inFlightRuns.size, timeoutMs },\n });\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"conversation-runtime.js","sourceRoot":"","sources":["../../src/runtime/conversation-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n MessagingBot,\n ConversationContext,\n ConversationEvent,\n PlatformName,\n RunningSession,\n} from \"../adapter.js\";\nimport { createRunner } from \"../agent.js\";\nimport { defaultCommandHandlers, dispatchCommand } from \"../commands/registry.js\";\nimport type { CommandHandler, CommandServices } from \"../commands/types.js\";\nimport { isPrivateConversation } from \"../commands/utils.js\";\nimport * as log from \"../log.js\";\nimport {\n addLifecycleBreadcrumb,\n applyRunScope,\n createRunAttributionAttributes,\n registerTraceAttribution,\n reportUserFacingError,\n} from \"../observability/sentry.js\";\nimport {\n ChatHistorySync,\n hasMaterializedChatSession,\n waitForThreadSessionBootstrap,\n} from \"../sessions/chat-history-sync.js\";\nimport {\n assertSessionKeyBelongsToConversation,\n deriveSessionKey,\n} from \"../sessions/session-key.js\";\nimport { formatNothingRunning, formatStopped, formatStopping } from \"../platform-messages.js\";\nimport * as Sentry from \"@sentry/node\";\nimport { join } from \"path\";\nimport { getUnresolvedSandboxPathContext } from \"../sandbox/index.js\";\nimport { disabledVaultManager } from \"../vault/disabled.js\";\nimport type { ConversationRuntimeState } from \"./types.js\";\nimport { SessionLifecycle } from \"./session-lifecycle.js\";\n\ntype ConversationState = ConversationRuntimeState;\n\nexport type {\n RunSessionOptions,\n ConversationRuntime,\n ConversationRuntimeOptions,\n} from \"./types.js\";\nimport type {\n RunSessionOptions,\n ConversationRuntime,\n ConversationRuntimeOptions,\n SessionStateOptions,\n} from \"./types.js\";\n\n/**\n * Placeholder token store for embedders that run without the web portal.\n * Command handlers guard on `portalBaseUrl` before minting tokens, so this\n * only fires when a portal URL is configured without its backing store.\n */\nfunction portalNotConfiguredTokenStore(portal: string): { create: () => never } {\n return {\n create: () => {\n throw new Error(`${portal} portal not configured`);\n },\n };\n}\n\nfunction runtimeCwdForSandbox(\n sandbox: ConversationRuntimeOptions[\"sandbox\"],\n hostWorkspaceRoot: string,\n conversationId: string,\n): string {\n const runtimeWorkspaceRoot = getUnresolvedSandboxPathContext(\n sandbox,\n hostWorkspaceRoot,\n ).runtimeWorkspaceRoot;\n return `${runtimeWorkspaceRoot.replace(/\\/+$/, \"\")}/${conversationId}`;\n}\n\nexport function createConversationRuntime(\n options: ConversationRuntimeOptions,\n): ConversationRuntime {\n return new ConversationRuntimeImpl(options);\n}\n\nclass ConversationRuntimeImpl implements ConversationRuntime {\n private readonly sessions = new SessionLifecycle();\n private readonly inFlightRuns = new Set<Promise<void>>();\n private readonly memoryMaintenanceSettlements = new Set<Promise<void>>();\n private readonly chatSessionManager = new ChatHistorySync();\n private readonly commandServices: CommandServices;\n private readonly commandHandlers: readonly CommandHandler[];\n private isShuttingDown = false;\n\n constructor(private readonly options: ConversationRuntimeOptions) {\n this.commandServices = {\n ...options,\n resourceController: options.resourceController ?? options.provisioner,\n vaultManager: options.vaultManager ?? disabledVaultManager,\n linkTokenStore: options.linkTokenStore ?? portalNotConfiguredTokenStore(\"Login\"),\n sessionViewTokenStore:\n options.sessionViewTokenStore ?? portalNotConfiguredTokenStore(\"Session viewer\"),\n adminTokenStore: options.adminTokenStore ?? portalNotConfiguredTokenStore(\"Admin\"),\n runtime: this,\n };\n this.commandHandlers = options.commandHandlers ?? defaultCommandHandlers();\n }\n\n isRunning(sessionKey: string): boolean {\n const state = this.sessions.get(sessionKey);\n return !!state?.running;\n }\n\n getRunningSessions(): RunningSession[] {\n const sessions: RunningSession[] = [];\n for (const [sessionKey, state] of this.sessions.runningStates()) {\n if (state.startedAt) {\n const currentStep = state.runner.getCurrentStep();\n sessions.push({\n sessionKey,\n startedAt: state.startedAt,\n lastActivityAt: state.lastActivityAt,\n currentTool: currentStep?.label || currentStep?.toolName,\n });\n }\n }\n return sessions;\n }\n\n async handleStop(sessionKey: string, conversationId: string, bot: MessagingBot): Promise<void> {\n assertSessionKeyBelongsToConversation(sessionKey, conversationId);\n const state = this.sessions.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n const ts = await bot.postMessage(conversationId, formatStopping(bot));\n state.stopMessageTs = ts;\n } else {\n await bot.postMessage(conversationId, formatNothingRunning(bot));\n }\n }\n\n forceStop(sessionKey: string): void {\n const state = this.sessions.get(sessionKey);\n if (state?.running) {\n log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);\n state.stopRequested = true;\n state.runner.abort();\n }\n }\n\n async handleNewCommand(\n sessionKey: string,\n conversationId: string,\n bot: MessagingBot,\n message: ConversationContext[\"message\"],\n responder: ConversationContext[\"responder\"],\n platform: ConversationContext[\"platform\"],\n ): Promise<void> {\n assertSessionKeyBelongsToConversation(sessionKey, conversationId);\n const activeState = this.sessions.get(sessionKey);\n if (activeState?.running) {\n const activeSettlement = activeState.runSettlement;\n if (activeSettlement && this.memoryMaintenanceSettlements.has(activeSettlement)) {\n await activeSettlement;\n return;\n }\n activeState.stopRequested = true;\n activeState.runner.abort();\n await activeSettlement;\n }\n\n const state = await this.getOrCreateState({\n conversationId,\n sessionKey,\n conversationKind: message.conversationKind,\n });\n if (state.running) {\n const activeSettlement = state.runSettlement;\n if (activeSettlement && this.memoryMaintenanceSettlements.has(activeSettlement)) {\n await activeSettlement;\n return;\n }\n state.stopRequested = true;\n state.runner.abort();\n await activeSettlement;\n return this.handleNewCommand(sessionKey, conversationId, bot, message, responder, platform);\n }\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n const maintenanceSettlement = Promise.resolve().then(async () => {\n let working = false;\n try {\n await responder.setWorking(true);\n working = true;\n let result: { stopReason: string; errorMessage?: string };\n try {\n result = await state.runner.maintainMemory(message, platform);\n } catch (err) {\n await responder.respondDiagnostic(\n `Could not preserve memory, so the current conversation was not reset. ${err instanceof Error ? err.message : String(err)}`,\n { style: \"error\" },\n );\n return;\n }\n if (result.stopReason === \"error\" || result.stopReason === \"blocked\") {\n const detail = result.errorMessage ? ` ${result.errorMessage}` : \"\";\n await responder.respondDiagnostic(\n `Could not preserve memory, so the current conversation was not reset.${detail}`,\n { style: \"error\" },\n );\n return;\n }\n\n await responder.setWorking(false);\n working = false;\n await this.resetSession(sessionKey, conversationId, bot);\n } finally {\n try {\n if (working) await responder.setWorking(false);\n } finally {\n state.running = false;\n state.startedAt = 0;\n state.lastAccessedAt = Date.now();\n }\n }\n });\n\n state.runSettlement = maintenanceSettlement;\n this.memoryMaintenanceSettlements.add(maintenanceSettlement);\n this.inFlightRuns.add(maintenanceSettlement);\n try {\n await maintenanceSettlement;\n } finally {\n this.memoryMaintenanceSettlements.delete(maintenanceSettlement);\n this.inFlightRuns.delete(maintenanceSettlement);\n if (state.runSettlement === maintenanceSettlement) state.runSettlement = undefined;\n this.sessions.evictIdle();\n }\n }\n\n private async resetSession(\n sessionKey: string,\n conversationId: string,\n bot: MessagingBot,\n ): Promise<void> {\n const conversationDir = join(this.options.workingDir, conversationId);\n const runtimeCwd = runtimeCwdForSandbox(\n this.options.sandbox,\n this.options.workingDir,\n conversationId,\n );\n this.chatSessionManager.resetSession({ conversationDir, sessionKey, cwd: runtimeCwd });\n\n this.sessions.discard(sessionKey);\n\n log.logInfo(`[${conversationId}] Session reset: ${sessionKey}`);\n await bot.postMessage(conversationId, \"Conversation reset. Send a new message to start fresh.\");\n }\n\n async handleEvent(\n event: ConversationEvent,\n bot: MessagingBot,\n context: ConversationContext,\n ): Promise<void> {\n const sessionKey = deriveSessionKey(event);\n await this.sessions.enqueue(sessionKey, () => this.runSession({ event, bot, context }));\n }\n\n async runSession({ event, bot, context }: RunSessionOptions): Promise<void> {\n const conversationId = event.conversationId;\n if (this.isShuttingDown) {\n log.logInfo(\n `[${conversationId}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const sessionKey = deriveSessionKey(event);\n const privateConversation = isPrivateConversation(event);\n const handledCommand = await dispatchCommand(this.commandHandlers, {\n bot,\n responder: context.responder,\n platform: context.platform.name as PlatformName,\n platformUserId: event.user,\n platformUserName: context.message.userName,\n conversationId,\n vaultConversationId: event.vaultConversationId,\n sessionKey,\n commandText: event.text,\n privateConversation,\n services: this.commandServices,\n });\n if (handledCommand) return;\n\n const activeSettlement = this.sessions.get(sessionKey)?.runSettlement;\n if (activeSettlement) await activeSettlement;\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const waitedForParent = await waitForThreadSessionBootstrap({\n parentSessionKey: conversationId,\n sessionKey,\n hasThreadSession: () => hasMaterializedChatSession({ conversationDir, sessionKey }),\n isParentRunning: () => this.sessions.get(conversationId)?.running === true,\n });\n if (waitedForParent) {\n log.logInfo(\n `[${conversationId}] Delayed thread bootstrap until parent session sealed: ${sessionKey}`,\n );\n }\n\n let state: ConversationState;\n try {\n state = await this.getOrCreateState({\n conversationId,\n sessionKey,\n currentMessageId: event.ts,\n conversationKind: event.conversationKind,\n });\n state.runner.syncChatHistory(event.ts);\n\n // Extension-contributed commands: deterministic dispatch, no agent run.\n // Built-in commands already had their chance above, so extensions can\n // never shadow them; unmatched slash text falls through to the agent.\n if (event.text.trim().startsWith(\"/\")) {\n const handled = await state.runner.tryExtensionCommand(context.message, context.responder);\n if (handled) {\n state.lastAccessedAt = Date.now();\n return;\n }\n }\n } catch (err) {\n reportUserFacingError(err, {\n domain: \"mikan\",\n surface: \"session_setup\",\n operation: \"get_or_create_state\",\n severity: \"error\",\n platform: context.platform.name,\n context: {\n conversationId,\n sessionKey,\n messageId: context.message.id,\n threadTs: context.message.threadTs,\n attachmentCount: context.message.attachments?.length ?? 0,\n },\n });\n throw err;\n }\n\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${conversationId}] Starting run: ${event.text.substring(0, 50)}`);\n\n const runPromise = (async () => {\n try {\n const result = await this.runWithInstrumentation(\n context,\n { conversationId, sessionKey, startedAt: state.startedAt },\n async () => {\n await context.responder.setTyping(true);\n await context.responder.setWorking(true);\n try {\n return await state.runner.run(context.message, context.responder, context.platform);\n } finally {\n await context.responder.setWorking(false);\n }\n },\n );\n\n if (result?.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(conversationId, state.stopMessageTs, formatStopped(bot));\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(conversationId, formatStopped(bot));\n }\n }\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size - 1);\n this.sessions.evictIdle();\n }\n })();\n\n this.inFlightRuns.add(runPromise);\n state.runSettlement = runPromise;\n Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size);\n try {\n await runPromise;\n } finally {\n this.inFlightRuns.delete(runPromise);\n if (state.runSettlement === runPromise) state.runSettlement = undefined;\n }\n }\n\n private async runWithInstrumentation(\n context: ConversationContext,\n meta: {\n conversationId: string;\n sessionKey: string;\n startedAt: number;\n },\n body: () => Promise<{ stopReason: string; errorMessage?: string }>,\n ): Promise<{ stopReason: string; errorMessage?: string } | undefined> {\n const { conversationId, sessionKey, startedAt } = meta;\n const { message, platform } = context;\n\n const attribution = createRunAttributionAttributes({\n conversationId,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n });\n\n Sentry.metrics.count(\"agent.run.started\", 1, {\n attributes: attribution,\n });\n\n return Sentry.startSpan(\n { name: \"agent.run\", op: \"agent\", attributes: attribution },\n async (span) =>\n Sentry.withScope(async (scope) => {\n registerTraceAttribution(span, attribution);\n applyRunScope(scope, {\n conversationId,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n });\n addLifecycleBreadcrumb(\"agent.run.started\", {\n channel_id: conversationId,\n platform: platform.name,\n has_attachments: (message.attachments?.length ?? 0) > 0,\n });\n\n try {\n const result = await body();\n const durationMs = Date.now() - startedAt;\n const completionAttrs = {\n ...attribution,\n stop_reason: result.stopReason,\n };\n Sentry.metrics.distribution(\"agent.run.duration\", durationMs, {\n unit: \"millisecond\",\n attributes: completionAttrs,\n });\n Sentry.metrics.count(\"agent.run.completed\", 1, { attributes: completionAttrs });\n addLifecycleBreadcrumb(\"agent.run.completed\", {\n channel_id: conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n duration_ms: durationMs,\n });\n return result;\n } catch (err) {\n scope.setContext(\"agent_run_error\", {\n conversationId,\n sessionKey,\n platform: platform.name,\n messageId: message.id,\n threadTs: message.threadTs,\n });\n reportUserFacingError(err, {\n domain: \"mikan\",\n surface: \"agent_run\",\n operation: \"run\",\n severity: \"error\",\n platform: platform.name,\n context: {\n conversationId,\n sessionKey,\n messageId: message.id,\n threadTs: message.threadTs,\n attachmentCount: message.attachments?.length ?? 0,\n },\n });\n Sentry.metrics.count(\"agent.run.errors\", 1, {\n attributes: attribution,\n });\n log.logWarning(\n `[${conversationId}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n }),\n );\n }\n\n switchConversationModel(conversationId: string, _provider: string, _model: string): boolean {\n return this.clearConversationStates(\n conversationId,\n `[${conversationId}] Model switched; cleared cached session runners`,\n );\n }\n\n refreshConversationEnvironment(conversationId: string): boolean {\n return this.clearConversationStates(\n conversationId,\n `[${conversationId}] Environment refreshed; cleared cached session runners`,\n );\n }\n\n refreshAllConversations(): { busy: string[] } {\n const busy: string[] = [];\n for (const conversationId of this.sessions.conversationIds()) {\n const cleared = this.clearConversationStates(\n conversationId,\n `[${conversationId}] Global settings changed; cleared cached session runners`,\n );\n if (!cleared) busy.push(conversationId);\n }\n return { busy };\n }\n\n private clearConversationStates(conversationId: string, message: string): boolean {\n const cleared = this.sessions.clearConversation(conversationId);\n if (cleared) log.logInfo(message);\n return cleared;\n }\n\n private async getOrCreateState(\n options: SessionStateOptions & { currentMessageId?: string },\n ): Promise<ConversationState> {\n const { conversationId, sessionKey, currentMessageId } = options;\n const existing = this.sessions.get(sessionKey);\n if (existing?.running) return existing;\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const runtimeCwd = runtimeCwdForSandbox(\n this.options.sandbox,\n this.options.workingDir,\n conversationId,\n );\n const sessionScope = await this.chatSessionManager.resolveSessionScope({\n conversationDir,\n sessionKey,\n cwd: runtimeCwd,\n currentMessageId,\n rotateTopLevelSession: options.conversationKind === \"shared\" && sessionKey === conversationId,\n });\n\n if (existing && existing.sessionFile === sessionScope.contextFile) {\n existing.lastAccessedAt = Date.now();\n return existing;\n }\n\n // A stale state (rotated session file) is being replaced: release the old\n // runner's extension resources before the new one takes the slot.\n if (existing) this.sessions.discard(sessionKey);\n\n const state: ConversationState = {\n running: false,\n runner: await createRunner({\n sandboxConfig: this.options.sandbox,\n sessionKey,\n conversationId,\n conversationDir,\n workspaceDir: this.options.workingDir,\n sessionScope,\n vaultManager: this.options.vaultManager,\n provisioner: this.options.provisioner,\n resourceController: this.options.resourceController,\n sessionView: this.options.sessionViewTokenStore\n ? {\n tokenStore: this.options.sessionViewTokenStore,\n portalBaseUrl: this.options.portalBaseUrl,\n }\n : undefined,\n platformNotifier: this.options.platformNotifier,\n platformReactor: this.options.platformReactor,\n platformUploader: this.options.platformUploader,\n platformToolPackFactories: this.options.platformToolPackFactories,\n models: this.options.models,\n }),\n stopRequested: false,\n lastAccessedAt: Date.now(),\n sessionFile: sessionScope.contextFile,\n startedAt: 0,\n };\n this.sessions.set(sessionKey, state);\n return state;\n }\n\n async shutdown(timeoutMs = 30_000): Promise<void> {\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + timeoutMs;\n while (this.inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (this.inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${this.inFlightRuns.size} runs still in progress`);\n reportUserFacingError(new Error(\"Shutdown forced with in-flight agent runs\"), {\n domain: \"mikan\",\n surface: \"shutdown\",\n operation: \"force_exit_with_inflight_runs\",\n severity: \"warning\",\n context: { inFlightRuns: this.inFlightRuns.size, timeoutMs },\n });\n }\n }\n}\n"]}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface SessionLifecycleOptions {
|
|
|
11
11
|
}
|
|
12
12
|
export interface ConversationRuntimeState {
|
|
13
13
|
running: boolean;
|
|
14
|
+
/** Exact settlement of the current run, including post-run response handling. */
|
|
15
|
+
runSettlement?: Promise<void>;
|
|
14
16
|
runner: PiAgentWrapper;
|
|
15
17
|
stopRequested: boolean;
|
|
16
18
|
stopMessageTs?: string;
|