@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
|
@@ -15,6 +15,5 @@ export declare function parseUserBody(raw: string): {
|
|
|
15
15
|
header: string | null;
|
|
16
16
|
content: string;
|
|
17
17
|
};
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function loadSessionViewModel(sessionFile: string): Promise<SessionViewModel>;
|
|
18
|
+
export declare function loadSessionViewModel(office: Office, sessionKey: string): Promise<SessionViewModel>;
|
|
20
19
|
//# sourceMappingURL=portal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../../src/adapters/web/session-view/portal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../../src/adapters/web/session-view/portal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAOjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAGV,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAmB3B,qBAAa,6BAA8B,SAAQ,kBAAkB,CAAC,gBAAgB,CAAC;IACrF,MAAM,CAAC,OAAO,EAAE,6BAA6B,GAAG,gBAAgB,CAS/D;CACF;AA6DD,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAIhE,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,GAAG,EACR,qBAAqB,CAAC,EAAE,6BAA6B,EACrD,WAAW,CAAC,EAAE,6BAA6B,GAC1C,OAAO,CAAC,OAAO,CAAC,CAsElB;AA4ID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAC1C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CA4BA;AAm6CD,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,gBAAgB,CAAC,CA4C3B"}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import { dirname, join, resolve } from "node:path";
|
|
2
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
3
1
|
import { SessionStore } from "../../../sessions/session-store.js";
|
|
4
|
-
import { getThreadSessionFile, tryResolveCurrentSession, tryResolveThreadSession, } from "../../../sessions/store.js";
|
|
5
|
-
import { isPlatformHistorySession } from "../../../sessions/store.js";
|
|
6
|
-
import { basename } from "node:path";
|
|
7
2
|
import { InMemoryTokenStore } from "../token-store.js";
|
|
8
3
|
import MarkdownIt from "markdown-it";
|
|
9
4
|
import { createConversationEvent, createConversationMessage, createOfficeAddress, } from "../../../office/index.js";
|
|
@@ -21,7 +16,7 @@ export class InMemorySessionViewTokenStore extends InMemoryTokenStore {
|
|
|
21
16
|
platformUserName: options.platformUserName || undefined,
|
|
22
17
|
conversationId: options.conversationId,
|
|
23
18
|
sessionKey: options.sessionKey,
|
|
24
|
-
|
|
19
|
+
office: options.office,
|
|
25
20
|
});
|
|
26
21
|
}
|
|
27
22
|
}
|
|
@@ -85,9 +80,9 @@ export async function handleSessionViewRequest(req, res, url, sessionViewTokenSt
|
|
|
85
80
|
res.end(renderStatusPage("Session unavailable", target.message));
|
|
86
81
|
return true;
|
|
87
82
|
}
|
|
88
|
-
const { activeSessionKey, entry
|
|
83
|
+
const { activeSessionKey, entry } = target;
|
|
89
84
|
try {
|
|
90
|
-
const model = await loadSessionViewModel(
|
|
85
|
+
const model = await loadSessionViewModel(entry.office, activeSessionKey);
|
|
91
86
|
const displayedSessionKey = activeSessionKey;
|
|
92
87
|
const isRunning = interactive?.handler.isRunning(createOfficeAddress(entry.platform, entry.conversationId), displayedSessionKey) ?? false;
|
|
93
88
|
res.writeHead(200, {
|
|
@@ -104,7 +99,7 @@ export async function handleSessionViewRequest(req, res, url, sessionViewTokenSt
|
|
|
104
99
|
}));
|
|
105
100
|
}
|
|
106
101
|
catch (error) {
|
|
107
|
-
log.logWarning(`[${entry.conversationId}] Failed to render session ${
|
|
102
|
+
log.logWarning(`[${entry.conversationId}] Failed to render session ${activeSessionKey}`, errorMessage(error));
|
|
108
103
|
reportUserFacingError(error, {
|
|
109
104
|
domain: "session_view",
|
|
110
105
|
surface: "session_view",
|
|
@@ -113,8 +108,7 @@ export async function handleSessionViewRequest(req, res, url, sessionViewTokenSt
|
|
|
113
108
|
platform: entry.platform,
|
|
114
109
|
context: {
|
|
115
110
|
conversationId: entry.conversationId,
|
|
116
|
-
sessionKey:
|
|
117
|
-
sessionFile: basename(targetSessionFile),
|
|
111
|
+
sessionKey: activeSessionKey,
|
|
118
112
|
},
|
|
119
113
|
});
|
|
120
114
|
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
@@ -131,46 +125,11 @@ async function resolveSessionRequestTarget(sessionViewTokenStore, rawToken, requ
|
|
|
131
125
|
if (!entry) {
|
|
132
126
|
return { ok: false, status: 400, message: "This session link is invalid or has expired." };
|
|
133
127
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
targetSessionFile = await resolveRequestedSessionFile(entry.sessionFile, requestedSession);
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
log.logWarning(`[${entry.conversationId}] Corrupted session file referenced for ${entry.sessionFile}`, errorMessage(error));
|
|
140
|
-
reportUserFacingError(error, {
|
|
141
|
-
domain: "session_view",
|
|
142
|
-
surface: "session_view",
|
|
143
|
-
operation: "resolve_requested_session",
|
|
144
|
-
severity: "error",
|
|
145
|
-
platform: entry.platform,
|
|
146
|
-
context: {
|
|
147
|
-
conversationId: entry.conversationId,
|
|
148
|
-
sessionKey: entry.sessionKey,
|
|
149
|
-
sessionFile: basename(entry.sessionFile),
|
|
150
|
-
requestedSession,
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
return { ok: false, status: 500, message: "The selected session file is corrupted." };
|
|
154
|
-
}
|
|
155
|
-
if (!targetSessionFile) {
|
|
128
|
+
const requested = requestedSession?.trim() || entry.sessionKey;
|
|
129
|
+
if (!(await SessionStore.exists(entry.office, requested))) {
|
|
156
130
|
return { ok: false, status: 400, message: "The selected session is invalid." };
|
|
157
131
|
}
|
|
158
|
-
return {
|
|
159
|
-
ok: true,
|
|
160
|
-
entry,
|
|
161
|
-
targetSessionFile,
|
|
162
|
-
activeSessionKey: resolveDisplayedSessionKey(entry, targetSessionFile),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
function resolveDisplayedSessionKey(entry, sessionFile) {
|
|
166
|
-
if (entry.platform === "slack") {
|
|
167
|
-
const fileName = basename(sessionFile, ".jsonl");
|
|
168
|
-
if (/^\d+\.\d+$/.test(fileName)) {
|
|
169
|
-
return `${entry.conversationId}:${fileName}`;
|
|
170
|
-
}
|
|
171
|
-
return entry.conversationId;
|
|
172
|
-
}
|
|
173
|
-
return entry.sessionKey;
|
|
132
|
+
return { ok: true, entry, activeSessionKey: requested };
|
|
174
133
|
}
|
|
175
134
|
function sessionStreamKey(entry) {
|
|
176
135
|
return `${entry.platform}:${entry.conversationId}:${entry.sessionKey}`;
|
|
@@ -207,7 +166,7 @@ function renderSessionPage(options) {
|
|
|
207
166
|
|
|
208
167
|
<div class="session-detail-row">
|
|
209
168
|
<span class="session-detail"><span class="session-detail-label">Session</span><code>${esc(model.sessionId.slice(0, 8))}</code></span>
|
|
210
|
-
<span class="session-detail"><span class="session-detail-label">
|
|
169
|
+
<span class="session-detail"><span class="session-detail-label">Session</span><code>${esc(model.sessionKey)}</code></span>
|
|
211
170
|
<span class="session-detail"><span class="session-detail-label">Expires</span><span>${esc(formatDate(new Date(expiresAt).toISOString()))}</span></span>
|
|
212
171
|
</div>
|
|
213
172
|
|
|
@@ -224,7 +183,7 @@ function renderSessionPage(options) {
|
|
|
224
183
|
<section class="composer-card">
|
|
225
184
|
<form class="composer-form" data-session-composer>
|
|
226
185
|
<input type="hidden" name="token" value="${esc(token)}">
|
|
227
|
-
<input type="hidden" name="session" value="${esc(model.
|
|
186
|
+
<input type="hidden" name="session" value="${esc(model.sessionKey)}">
|
|
228
187
|
<input type="hidden" name="sessionKey" value="${esc(displayedSessionKey)}">
|
|
229
188
|
<textarea name="text" rows="1" placeholder="Ask mikan in this session… (replies stay in Session View)" required></textarea>
|
|
230
189
|
<div class="composer-actions">
|
|
@@ -236,13 +195,13 @@ function renderSessionPage(options) {
|
|
|
236
195
|
return renderHtmlDocument(`${model.title} · Session Viewer`, body, isRunning);
|
|
237
196
|
}
|
|
238
197
|
function renderRelationCard(relation, token) {
|
|
239
|
-
const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.
|
|
198
|
+
const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.sessionKey)}`;
|
|
240
199
|
const summary = relation.summary ? `<p class="related-summary">${esc(relation.summary)}</p>` : "";
|
|
241
200
|
return `<a class="related-link" href="${href}">
|
|
242
201
|
<span class="related-copy">
|
|
243
202
|
<strong class="related-title">${esc(relation.title)}</strong>
|
|
244
203
|
${summary}
|
|
245
|
-
<span class="related-meta">${esc(formatDate(relation.updatedAt))} · ${esc(String(relation.entryCount))} entries · ${esc(relation.
|
|
204
|
+
<span class="related-meta">${esc(formatDate(relation.updatedAt))} · ${esc(String(relation.entryCount))} entries · ${esc(relation.sessionKey)}</span>
|
|
246
205
|
</span>
|
|
247
206
|
<span class="related-arrow" aria-hidden="true">→</span>
|
|
248
207
|
</a>`;
|
|
@@ -252,7 +211,7 @@ function renderThreadLinks(relations, token) {
|
|
|
252
211
|
return "";
|
|
253
212
|
return `<div class="thread-links">${relations
|
|
254
213
|
.map((relation) => {
|
|
255
|
-
const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.
|
|
214
|
+
const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.sessionKey)}`;
|
|
256
215
|
return `<a class="thread-link" href="${href}" title="Open ${esc(relation.title)}">
|
|
257
216
|
<span class="thread-dot" aria-hidden="true"></span>
|
|
258
217
|
<span class="thread-text">Thread</span>
|
|
@@ -418,7 +377,7 @@ async function handleSessionStreamRequest(req, res, url, sessionViewTokenStore,
|
|
|
418
377
|
const send = (event) => res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
419
378
|
const unsubscribe = sessionViewStreamHub.subscribe(streamKey, send);
|
|
420
379
|
const liveRun = createLiveRunProjection(send, async () => {
|
|
421
|
-
const model = await loadSessionViewModel(target.
|
|
380
|
+
const model = await loadSessionViewModel(target.entry.office, target.activeSessionKey);
|
|
422
381
|
return {
|
|
423
382
|
type: "refresh",
|
|
424
383
|
timelineHtml: renderTimelineItems(model.items, url.searchParams.get("token") ?? ""),
|
|
@@ -1650,29 +1609,24 @@ function entryIsoTime(entry) {
|
|
|
1650
1609
|
}
|
|
1651
1610
|
return new Date(entry.timestamp).toISOString();
|
|
1652
1611
|
}
|
|
1653
|
-
export function
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
return tryResolveCurrentSession(office.sessionsDir);
|
|
1658
|
-
}
|
|
1659
|
-
export async function loadSessionViewModel(sessionFile) {
|
|
1660
|
-
const resolvedFile = resolve(sessionFile);
|
|
1661
|
-
const sm = await SessionStore.inspect(resolvedFile);
|
|
1612
|
+
export async function loadSessionViewModel(office, sessionKey) {
|
|
1613
|
+
const sm = await SessionStore.inspect(office, sessionKey);
|
|
1614
|
+
if (!sm)
|
|
1615
|
+
throw new Error(`Session not found: ${sessionKey}`);
|
|
1662
1616
|
const header = sm.getHeader();
|
|
1663
1617
|
const entries = await sm.getEntries();
|
|
1664
|
-
const updatedAt = entryIsoTime(entries.at(-1)) ?? header.
|
|
1618
|
+
const updatedAt = entryIsoTime(entries.at(-1)) ?? new Date(header.createdAt).toISOString();
|
|
1665
1619
|
const title = (await sm.getSessionName()) || `Session ${header.id.slice(0, 8)}`;
|
|
1666
|
-
const
|
|
1667
|
-
const
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
const relation = await buildSessionRelation(
|
|
1620
|
+
const listing = await SessionStore.list(office);
|
|
1621
|
+
const parentListing = listing.find((session) => session.id === header.parentSessionId);
|
|
1622
|
+
const parent = parentListing ? await buildSessionRelation(office, parentListing) : undefined;
|
|
1623
|
+
const threads = [];
|
|
1624
|
+
for (const child of listing.filter((session) => session.parentSessionId === header.id)) {
|
|
1625
|
+
const relation = await buildSessionRelation(office, child);
|
|
1672
1626
|
if (relation)
|
|
1673
|
-
|
|
1627
|
+
threads.push(relation);
|
|
1674
1628
|
}
|
|
1675
|
-
|
|
1629
|
+
threads.sort((a, b) => (a.updatedAt < b.updatedAt ? -1 : a.updatedAt > b.updatedAt ? 1 : 0));
|
|
1676
1630
|
const threadsByEntryId = new Map();
|
|
1677
1631
|
for (const thread of threads) {
|
|
1678
1632
|
if (!thread.anchorEntryId)
|
|
@@ -1685,270 +1639,37 @@ export async function loadSessionViewModel(sessionFile) {
|
|
|
1685
1639
|
const item = mapEntryToItem(entry);
|
|
1686
1640
|
if (!item)
|
|
1687
1641
|
return [];
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
item.threads = anchoredThreads;
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1642
|
+
const anchoredThreads = item.entryId ? threadsByEntryId.get(item.entryId) : undefined;
|
|
1643
|
+
if (anchoredThreads)
|
|
1644
|
+
item.threads = anchoredThreads;
|
|
1694
1645
|
return [item];
|
|
1695
1646
|
});
|
|
1696
1647
|
return {
|
|
1697
1648
|
sessionId: header.id,
|
|
1698
|
-
|
|
1649
|
+
sessionKey,
|
|
1699
1650
|
title,
|
|
1700
|
-
createdAt: header.
|
|
1651
|
+
createdAt: new Date(header.createdAt).toISOString(),
|
|
1701
1652
|
updatedAt,
|
|
1702
1653
|
entryCount: entries.length,
|
|
1703
1654
|
items,
|
|
1704
1655
|
parent: parent ?? undefined,
|
|
1705
|
-
threads,
|
|
1706
1656
|
};
|
|
1707
1657
|
}
|
|
1708
|
-
async function
|
|
1709
|
-
const
|
|
1710
|
-
if (!
|
|
1711
|
-
return resolvedBase;
|
|
1712
|
-
const trimmed = requestedFileName.trim();
|
|
1713
|
-
if (!trimmed)
|
|
1714
|
-
return resolvedBase;
|
|
1715
|
-
const fileName = basename(trimmed);
|
|
1716
|
-
if (fileName !== trimmed || !fileName.endsWith(".jsonl"))
|
|
1717
|
-
return null;
|
|
1718
|
-
const candidate = join(dirname(resolvedBase), fileName);
|
|
1719
|
-
if (!existsSync(candidate))
|
|
1658
|
+
async function buildSessionRelation(office, session) {
|
|
1659
|
+
const sm = await SessionStore.inspect(office, session.key);
|
|
1660
|
+
if (!sm)
|
|
1720
1661
|
return null;
|
|
1721
|
-
try {
|
|
1722
|
-
if (!SessionStore.readHeader(candidate))
|
|
1723
|
-
throw new Error("Invalid session header");
|
|
1724
|
-
}
|
|
1725
|
-
catch (err) {
|
|
1726
|
-
throw new Error(`Session file is corrupted: ${candidate}: ${errorMessage(err)}`, {
|
|
1727
|
-
cause: err,
|
|
1728
|
-
});
|
|
1729
|
-
}
|
|
1730
|
-
return candidate;
|
|
1731
|
-
}
|
|
1732
|
-
function listRelatedSessionFiles(sessionFile) {
|
|
1733
|
-
const dir = dirname(sessionFile);
|
|
1734
|
-
if (!existsSync(dir))
|
|
1735
|
-
return [];
|
|
1736
|
-
return readdirSync(dir)
|
|
1737
|
-
.filter((name) => name.endsWith(".jsonl"))
|
|
1738
|
-
.map((fileName) => join(dir, fileName));
|
|
1739
|
-
}
|
|
1740
|
-
async function buildSessionRelation(sessionFile, kind, expectedParent, expectedParentId, expectedParentEntries) {
|
|
1741
|
-
let sm;
|
|
1742
|
-
try {
|
|
1743
|
-
sm = await SessionStore.inspect(sessionFile);
|
|
1744
|
-
}
|
|
1745
|
-
catch (err) {
|
|
1746
|
-
log.logWarning(`Skipping corrupted session file while building ${kind} relation: ${sessionFile}`, errorMessage(err));
|
|
1747
|
-
return null;
|
|
1748
|
-
}
|
|
1749
1662
|
const header = sm.getHeader();
|
|
1750
|
-
if (kind === "thread" &&
|
|
1751
|
-
!isChildThreadSession(sessionFile, expectedParent, expectedParentId, header.parentSessionId)) {
|
|
1752
|
-
return null;
|
|
1753
|
-
}
|
|
1754
1663
|
const entries = await sm.getEntries();
|
|
1755
|
-
const updatedAt = entryIsoTime(entries.at(-1)) ?? header.timestamp;
|
|
1756
|
-
const threadId = kind === "thread" ? getFixedThreadSessionId(sessionFile) : null;
|
|
1757
|
-
const anchorEntryId = kind === "thread" && expectedParent
|
|
1758
|
-
? findThreadAnchorEntryId(expectedParentEntries ??
|
|
1759
|
-
(await (await SessionStore.inspect(expectedParent)).getEntries()), entries, threadId)
|
|
1760
|
-
: undefined;
|
|
1761
1664
|
return {
|
|
1762
|
-
|
|
1763
|
-
fileName: basename(sessionFile),
|
|
1764
|
-
sessionId: header.id,
|
|
1665
|
+
sessionKey: session.key,
|
|
1765
1666
|
title: (await sm.getSessionName()) || `Session ${header.id.slice(0, 8)}`,
|
|
1766
|
-
updatedAt,
|
|
1667
|
+
updatedAt: entryIsoTime(entries.at(-1)) ?? new Date(header.createdAt).toISOString(),
|
|
1767
1668
|
entryCount: entries.length,
|
|
1768
1669
|
summary: extractSessionSummary(entries),
|
|
1769
|
-
anchorEntryId,
|
|
1670
|
+
anchorEntryId: session.forkEntryId,
|
|
1770
1671
|
};
|
|
1771
1672
|
}
|
|
1772
|
-
async function resolveParentRelation(sessionFile, header) {
|
|
1773
|
-
if (header.parentSessionId) {
|
|
1774
|
-
const found = findSessionFileById(dirname(sessionFile), header.parentSessionId);
|
|
1775
|
-
if (found)
|
|
1776
|
-
return (await buildSessionRelation(found, "parent")) ?? undefined;
|
|
1777
|
-
}
|
|
1778
|
-
return buildInferredThreadParentRelation(sessionFile);
|
|
1779
|
-
}
|
|
1780
|
-
function findSessionFileById(sessionDir, targetId) {
|
|
1781
|
-
if (!existsSync(sessionDir))
|
|
1782
|
-
return null;
|
|
1783
|
-
for (const name of readdirSync(sessionDir)) {
|
|
1784
|
-
if (!name.endsWith(".jsonl"))
|
|
1785
|
-
continue;
|
|
1786
|
-
try {
|
|
1787
|
-
const filePath = join(sessionDir, name);
|
|
1788
|
-
if (SessionStore.readHeader(filePath)?.id === targetId)
|
|
1789
|
-
return filePath;
|
|
1790
|
-
}
|
|
1791
|
-
catch {
|
|
1792
|
-
continue;
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
return null;
|
|
1796
|
-
}
|
|
1797
|
-
async function buildInferredThreadParentRelation(sessionFile) {
|
|
1798
|
-
if (!getFixedThreadSessionId(sessionFile))
|
|
1799
|
-
return undefined;
|
|
1800
|
-
const parentSession = tryResolveCurrentSession(dirname(sessionFile));
|
|
1801
|
-
if (!parentSession || parentSession === sessionFile)
|
|
1802
|
-
return undefined;
|
|
1803
|
-
return (await buildSessionRelation(parentSession, "parent")) ?? undefined;
|
|
1804
|
-
}
|
|
1805
|
-
function isChildThreadSession(sessionFile, expectedParent, expectedParentId, threadParentSessionId) {
|
|
1806
|
-
if (!expectedParent)
|
|
1807
|
-
return false;
|
|
1808
|
-
if (threadParentSessionId) {
|
|
1809
|
-
if (threadParentSessionId === expectedParentId)
|
|
1810
|
-
return true;
|
|
1811
|
-
const parent = findSessionFileById(dirname(sessionFile), threadParentSessionId);
|
|
1812
|
-
return (parent !== null &&
|
|
1813
|
-
isPlatformHistorySession(expectedParent) &&
|
|
1814
|
-
isPlatformHistorySession(parent));
|
|
1815
|
-
}
|
|
1816
|
-
if (!getFixedThreadSessionId(sessionFile))
|
|
1817
|
-
return false;
|
|
1818
|
-
return tryResolveCurrentSession(dirname(sessionFile)) === expectedParent;
|
|
1819
|
-
}
|
|
1820
|
-
function getFixedThreadSessionId(sessionFile) {
|
|
1821
|
-
const fileName = basename(sessionFile);
|
|
1822
|
-
if (!fileName.endsWith(".jsonl"))
|
|
1823
|
-
return null;
|
|
1824
|
-
if (/^\d{4}-\d{2}-\d{2}T.+_[0-9a-f]{8}\.jsonl$/i.test(fileName))
|
|
1825
|
-
return null;
|
|
1826
|
-
return fileName.slice(0, -".jsonl".length);
|
|
1827
|
-
}
|
|
1828
|
-
function findThreadAnchorEntryId(parentEntries, childEntries, threadId) {
|
|
1829
|
-
const threadTimestamp = threadId ? Number(threadId) * 1000 : undefined;
|
|
1830
|
-
if (threadTimestamp !== undefined && Number.isFinite(threadTimestamp)) {
|
|
1831
|
-
const timestampAnchor = findEntryIdByMessageTimestamp(parentEntries, threadTimestamp);
|
|
1832
|
-
if (timestampAnchor)
|
|
1833
|
-
return timestampAnchor;
|
|
1834
|
-
}
|
|
1835
|
-
let sharedCount = 0;
|
|
1836
|
-
while (sharedCount < parentEntries.length &&
|
|
1837
|
-
sharedCount < childEntries.length &&
|
|
1838
|
-
parentEntries[sharedCount]?.id === childEntries[sharedCount]?.id) {
|
|
1839
|
-
sharedCount += 1;
|
|
1840
|
-
}
|
|
1841
|
-
if (sharedCount > 0) {
|
|
1842
|
-
return parentEntries[sharedCount - 1]?.id;
|
|
1843
|
-
}
|
|
1844
|
-
const contentAnchor = findSharedContentAnchorEntryId(parentEntries, childEntries);
|
|
1845
|
-
if (contentAnchor)
|
|
1846
|
-
return contentAnchor;
|
|
1847
|
-
const childRoot = findComparableUserMessage(childEntries);
|
|
1848
|
-
if (!childRoot)
|
|
1849
|
-
return undefined;
|
|
1850
|
-
return findParentAnchorByRootMessage(parentEntries, childRoot);
|
|
1851
|
-
}
|
|
1852
|
-
function findEntryIdByMessageTimestamp(entries, timestamp) {
|
|
1853
|
-
for (const entry of entries) {
|
|
1854
|
-
if (entry.type !== "message")
|
|
1855
|
-
continue;
|
|
1856
|
-
const messageTimestamp = entry.message.timestamp;
|
|
1857
|
-
if (messageTimestamp === timestamp)
|
|
1858
|
-
return entry.id;
|
|
1859
|
-
}
|
|
1860
|
-
return undefined;
|
|
1861
|
-
}
|
|
1862
|
-
function findSharedContentAnchorEntryId(parentEntries, childEntries) {
|
|
1863
|
-
const parentMessages = parentEntries.flatMap((entry) => {
|
|
1864
|
-
const comparable = getComparableSessionMessage(entry);
|
|
1865
|
-
return comparable ? [comparable] : [];
|
|
1866
|
-
});
|
|
1867
|
-
const childMessages = childEntries.flatMap((entry) => {
|
|
1868
|
-
const comparable = getComparableSessionMessage(entry);
|
|
1869
|
-
return comparable ? [comparable] : [];
|
|
1870
|
-
});
|
|
1871
|
-
if (parentMessages.length === 0 || childMessages.length === 0)
|
|
1872
|
-
return undefined;
|
|
1873
|
-
let bestParentEnd = -1;
|
|
1874
|
-
let bestLength = 0;
|
|
1875
|
-
for (let parentStart = 0; parentStart < parentMessages.length; parentStart++) {
|
|
1876
|
-
let length = 0;
|
|
1877
|
-
while (parentStart + length < parentMessages.length && length < childMessages.length) {
|
|
1878
|
-
const parentMessage = parentMessages[parentStart + length];
|
|
1879
|
-
const childMessage = childMessages[length];
|
|
1880
|
-
if (parentMessage === undefined ||
|
|
1881
|
-
childMessage === undefined ||
|
|
1882
|
-
parentMessage.role !== childMessage.role ||
|
|
1883
|
-
parentMessage.normalizedText !== childMessage.normalizedText) {
|
|
1884
|
-
break;
|
|
1885
|
-
}
|
|
1886
|
-
length += 1;
|
|
1887
|
-
}
|
|
1888
|
-
if (length > bestLength) {
|
|
1889
|
-
bestLength = length;
|
|
1890
|
-
bestParentEnd = parentStart + length - 1;
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
return bestLength > 0 ? parentMessages[bestParentEnd]?.entryId : undefined;
|
|
1894
|
-
}
|
|
1895
|
-
function getComparableSessionMessage(entry) {
|
|
1896
|
-
if (entry.type !== "message")
|
|
1897
|
-
return null;
|
|
1898
|
-
const role = entry.message.role;
|
|
1899
|
-
if (role !== "user" && role !== "assistant")
|
|
1900
|
-
return null;
|
|
1901
|
-
const body = contentToText(entry.message.content);
|
|
1902
|
-
const normalizedText = normalizeComparableSessionText(body, role);
|
|
1903
|
-
if (!normalizedText)
|
|
1904
|
-
return null;
|
|
1905
|
-
return { entryId: entry.id, role, normalizedText };
|
|
1906
|
-
}
|
|
1907
|
-
function normalizeComparableSessionText(text, role) {
|
|
1908
|
-
const normalized = role === "user" ? normalizeComparableUserText(text) : text.trim();
|
|
1909
|
-
return normalized.replace(/\s+/g, " ").trim();
|
|
1910
|
-
}
|
|
1911
|
-
function findParentAnchorByRootMessage(parentEntries, childRoot) {
|
|
1912
|
-
let textMatchId;
|
|
1913
|
-
for (const entry of parentEntries) {
|
|
1914
|
-
const comparable = getComparableUserMessage(entry);
|
|
1915
|
-
if (!comparable)
|
|
1916
|
-
continue;
|
|
1917
|
-
if (comparable.normalizedText !== childRoot.normalizedText)
|
|
1918
|
-
continue;
|
|
1919
|
-
if (childRoot.messageTimestamp !== undefined &&
|
|
1920
|
-
comparable.messageTimestamp !== undefined &&
|
|
1921
|
-
comparable.messageTimestamp === childRoot.messageTimestamp) {
|
|
1922
|
-
return entry.id;
|
|
1923
|
-
}
|
|
1924
|
-
textMatchId ??= entry.id;
|
|
1925
|
-
}
|
|
1926
|
-
return textMatchId;
|
|
1927
|
-
}
|
|
1928
|
-
function findComparableUserMessage(entries) {
|
|
1929
|
-
for (const entry of entries) {
|
|
1930
|
-
const comparable = getComparableUserMessage(entry);
|
|
1931
|
-
if (comparable)
|
|
1932
|
-
return comparable;
|
|
1933
|
-
}
|
|
1934
|
-
return null;
|
|
1935
|
-
}
|
|
1936
|
-
function getComparableUserMessage(entry) {
|
|
1937
|
-
if (entry.type !== "message" || entry.message.role !== "user")
|
|
1938
|
-
return null;
|
|
1939
|
-
const body = contentToText(entry.message.content);
|
|
1940
|
-
const normalizedText = normalizeComparableUserText(body);
|
|
1941
|
-
if (!normalizedText)
|
|
1942
|
-
return null;
|
|
1943
|
-
const messageTimestamp = typeof entry.message.timestamp === "number" ? entry.message.timestamp : undefined;
|
|
1944
|
-
return { normalizedText, messageTimestamp };
|
|
1945
|
-
}
|
|
1946
|
-
function normalizeComparableUserText(text) {
|
|
1947
|
-
const withoutTimestamp = text.replace(/^\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}\]\s+(?=\[[^\]]+\](?:\s+\[in-thread:[^\]]+\])?:\s)/, "");
|
|
1948
|
-
return withoutTimestamp
|
|
1949
|
-
.replace(/\n*<slack_attachments>\n[\s\S]*?\n<\/slack_attachments>\s*$/g, "")
|
|
1950
|
-
.trim();
|
|
1951
|
-
}
|
|
1952
1673
|
function extractSessionSummary(entries) {
|
|
1953
1674
|
for (const entry of entries) {
|
|
1954
1675
|
if (entry.type !== "message")
|
|
@@ -1970,8 +1691,6 @@ function mapEntryToItem(entry) {
|
|
|
1970
1691
|
return mapMessageEntry(entry);
|
|
1971
1692
|
case "compaction":
|
|
1972
1693
|
return mapCompactionEntry(entry);
|
|
1973
|
-
case "branch_summary":
|
|
1974
|
-
return mapSessionSummaryEntry(entry);
|
|
1975
1694
|
case "custom":
|
|
1976
1695
|
return {
|
|
1977
1696
|
kind: "system",
|
|
@@ -2077,15 +1796,6 @@ function mapCompactionEntry(entry) {
|
|
|
2077
1796
|
kind: "system",
|
|
2078
1797
|
title: "Context compacted",
|
|
2079
1798
|
body: entry.summary,
|
|
2080
|
-
meta: `${entryIsoTime(entry) ?? ""} · ${entry.tokensBefore} tokens before compaction`,
|
|
2081
|
-
tone: "muted",
|
|
2082
|
-
};
|
|
2083
|
-
}
|
|
2084
|
-
function mapSessionSummaryEntry(entry) {
|
|
2085
|
-
return {
|
|
2086
|
-
kind: "system",
|
|
2087
|
-
title: "Session summary",
|
|
2088
|
-
body: entry.summary,
|
|
2089
1799
|
meta: entryIsoTime(entry) ?? "",
|
|
2090
1800
|
tone: "muted",
|
|
2091
1801
|
};
|