@geminixiang/mikan 1.2.2 → 1.3.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +1 -0
- package/dist/adapters/commands/compact.d.ts +5 -0
- package/dist/adapters/commands/compact.d.ts.map +1 -0
- package/dist/adapters/commands/compact.js +36 -0
- package/dist/adapters/commands/compact.js.map +1 -0
- package/dist/adapters/commands/manifest.d.ts.map +1 -1
- package/dist/adapters/commands/manifest.js +14 -0
- package/dist/adapters/commands/manifest.js.map +1 -1
- package/dist/adapters/commands/registry.d.ts.map +1 -1
- package/dist/adapters/commands/registry.js +2 -0
- package/dist/adapters/commands/registry.js.map +1 -1
- package/dist/adapters/commands/types.d.ts +3 -1
- package/dist/adapters/commands/types.d.ts.map +1 -1
- package/dist/adapters/commands/types.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +28 -27
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/task-status.d.ts +0 -1
- package/dist/adapters/slack/task-status.d.ts.map +1 -1
- package/dist/adapters/slack/task-status.js +47 -53
- package/dist/adapters/slack/task-status.js.map +1 -1
- package/dist/adapters/web/admin/portal.d.ts.map +1 -1
- package/dist/adapters/web/admin/portal.js +13 -31
- package/dist/adapters/web/admin/portal.js.map +1 -1
- package/dist/harness/mcp-result.d.ts +3 -7
- package/dist/harness/mcp-result.d.ts.map +1 -1
- package/dist/harness/mcp-result.js +4 -48
- package/dist/harness/mcp-result.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +1 -0
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.d.ts +5 -0
- package/dist/harness/session.d.ts.map +1 -1
- package/dist/harness/session.js +25 -2
- package/dist/harness/session.js.map +1 -1
- package/dist/harness/tools/codemode.d.ts.map +1 -1
- package/dist/harness/tools/codemode.js +19 -2
- package/dist/harness/tools/codemode.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-usage.d.ts +3 -0
- package/dist/migrations/session-usage.d.ts.map +1 -0
- package/dist/migrations/session-usage.js +22 -0
- package/dist/migrations/session-usage.js.map +1 -0
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +26 -18
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/session-lifecycle.d.ts +0 -4
- package/dist/runtime/session-lifecycle.d.ts.map +1 -1
- package/dist/runtime/session-lifecycle.js +0 -57
- package/dist/runtime/session-lifecycle.js.map +1 -1
- package/dist/sessions/session-store.d.ts +12 -10
- package/dist/sessions/session-store.d.ts.map +1 -1
- package/dist/sessions/session-store.js +183 -15
- package/dist/sessions/session-store.js.map +1 -1
- package/dist/sessions/types.d.ts +24 -1
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/types.d.ts +14 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,10 +3,11 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
5
5
|
import { calculateContextTokens } from "@earendil-works/pi-ai/utils/estimate";
|
|
6
|
-
import { AssistantEntry, CompactionEntry, Harness, LiveDoc, MemoryStorage, ProviderDoc, ROOT_CONVERSATION_ID, ToolResultEntry, UserEntry, createRegistry, defineDoc, defineEntry, defineExtension, defineTask, } from "@earendil-works/pi-durable";
|
|
6
|
+
import { AssistantEntry, CompactionEntry, Harness, LiveDoc, MemoryStorage, ProviderDoc, ROOT_CONVERSATION_ID, ToolResultEntry, UsageDoc, UserEntry, createRegistry, defineDoc, defineEntry, defineExtension, defineTask, } from "@earendil-works/pi-durable";
|
|
7
7
|
import { openNodeSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/node";
|
|
8
8
|
import { RUN_CAUSE_CUSTOM_TYPE } from "./types.js";
|
|
9
9
|
import { loadMcpTools } from "../harness/mcp.js";
|
|
10
|
+
import { addUsage, createEmptyUsage, sumUsageState } from "../harness/session.js";
|
|
10
11
|
import * as log from "../log.js";
|
|
11
12
|
import { compactionSummaryOf, wrapCompactionSummary } from "./compaction-summary.js";
|
|
12
13
|
import { isThreadSessionKey } from "./session-key.js";
|
|
@@ -31,6 +32,14 @@ const SessionDoc = defineDoc({
|
|
|
31
32
|
fork: "initial",
|
|
32
33
|
initial: () => ({}),
|
|
33
34
|
});
|
|
35
|
+
export const ScriptStoreDoc = defineDoc({
|
|
36
|
+
kind: "mikan.script-store",
|
|
37
|
+
version: 1,
|
|
38
|
+
scope: "conversation",
|
|
39
|
+
history: "rewindable",
|
|
40
|
+
fork: "asOf",
|
|
41
|
+
initial: () => ({ values: {} }),
|
|
42
|
+
});
|
|
34
43
|
const SessionIndexDoc = defineDoc({
|
|
35
44
|
kind: "mikan.sessions",
|
|
36
45
|
version: 1,
|
|
@@ -276,6 +285,44 @@ class OfficeStorage {
|
|
|
276
285
|
await this.harness.close(context);
|
|
277
286
|
}
|
|
278
287
|
}
|
|
288
|
+
const OWNED_CONVERSATION_PAGE_SIZE = 100;
|
|
289
|
+
async function ownedConversations(storage, ownerConversationId) {
|
|
290
|
+
return storage.harness.commit(async (tx) => {
|
|
291
|
+
const owned = [];
|
|
292
|
+
let cursor;
|
|
293
|
+
do {
|
|
294
|
+
const page = await tx.scanConversations({ ownerConversationId: ownerConversationId, order: "descending" }, OWNED_CONVERSATION_PAGE_SIZE, cursor);
|
|
295
|
+
owned.push(...page.items);
|
|
296
|
+
cursor = page.next;
|
|
297
|
+
} while (cursor !== undefined);
|
|
298
|
+
return owned;
|
|
299
|
+
}, context);
|
|
300
|
+
}
|
|
301
|
+
async function readTaskState(storage, sessionKey, conversationId, anchorId) {
|
|
302
|
+
const anchor = await storage.harness.commit((tx) => tx.task(anchorId), context);
|
|
303
|
+
return {
|
|
304
|
+
sessionKey,
|
|
305
|
+
acknowledgement: briefAcknowledgement(anchor?.input),
|
|
306
|
+
...(await readExecution(storage, conversationId)),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
async function readExecution(storage, conversationId) {
|
|
310
|
+
const live = await storage.harness.snapshot(LiveDoc, conversationId, context);
|
|
311
|
+
const run = (await storage.harness.snapshot(SessionDoc, conversationId, context))?.run;
|
|
312
|
+
const open = live?.run !== undefined;
|
|
313
|
+
return {
|
|
314
|
+
open,
|
|
315
|
+
started: open || run !== undefined,
|
|
316
|
+
result: run?.endedAt !== undefined && run.status !== undefined
|
|
317
|
+
? { status: run.status, endedAt: run.endedAt }
|
|
318
|
+
: undefined,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function briefAcknowledgement(input) {
|
|
322
|
+
return isRecord(input) && typeof input.acknowledgement === "string"
|
|
323
|
+
? input.acknowledgement
|
|
324
|
+
: undefined;
|
|
325
|
+
}
|
|
279
326
|
function writeSessionIndex(tx, key, record) {
|
|
280
327
|
return tx.doc(SessionIndexDoc).then((doc) => {
|
|
281
328
|
doc.sessions[key] = record;
|
|
@@ -304,12 +351,12 @@ async function createSession(storage, key) {
|
|
|
304
351
|
}
|
|
305
352
|
return { conversation, record };
|
|
306
353
|
}
|
|
307
|
-
async function createTaskSession(storage, key, requesterKey) {
|
|
354
|
+
async function createTaskSession(storage, key, requesterKey, brief) {
|
|
308
355
|
const requester = await lookupSession(storage, requesterKey);
|
|
309
356
|
if (!requester)
|
|
310
357
|
throw new Error(`Task requester session not found: ${requesterKey}`);
|
|
311
358
|
const record = await requester.conversation.commit(async (tx) => {
|
|
312
|
-
const anchor = await tx.createTask(TaskAnchor,
|
|
359
|
+
const anchor = await tx.createTask(TaskAnchor, brief, {
|
|
313
360
|
ownership: { kind: "conversation" },
|
|
314
361
|
background: true,
|
|
315
362
|
});
|
|
@@ -429,6 +476,70 @@ export async function importOfficeSessions(path, sessions) {
|
|
|
429
476
|
}
|
|
430
477
|
protectStorageFiles(path);
|
|
431
478
|
}
|
|
479
|
+
export async function backfillImportedUsage(path, dryRun) {
|
|
480
|
+
const harness = await Harness.open(await openNodeSqliteStorage(path), { models: {}, registry: createRegistry(), settings: { extensions: [] } }, context);
|
|
481
|
+
try {
|
|
482
|
+
let backfilled = 0;
|
|
483
|
+
let cursor;
|
|
484
|
+
do {
|
|
485
|
+
const page = await harness.commit((tx) => tx.scanConversations({}, 100, cursor), context);
|
|
486
|
+
for (const conversation of page.items) {
|
|
487
|
+
const imported = await importedUsage(harness, conversation.id);
|
|
488
|
+
if (imported.size === 0)
|
|
489
|
+
continue;
|
|
490
|
+
backfilled += 1;
|
|
491
|
+
if (!dryRun)
|
|
492
|
+
await recordImportedUsage(harness, conversation.id, imported);
|
|
493
|
+
}
|
|
494
|
+
cursor = page.next;
|
|
495
|
+
} while (cursor !== undefined);
|
|
496
|
+
return backfilled;
|
|
497
|
+
}
|
|
498
|
+
finally {
|
|
499
|
+
await harness.close(context);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async function importedUsage(harness, conversationId) {
|
|
503
|
+
const byModel = new Map();
|
|
504
|
+
if ((await harness.snapshot(SessionDoc, conversationId, context))?.importedUsage)
|
|
505
|
+
return byModel;
|
|
506
|
+
let cursor;
|
|
507
|
+
do {
|
|
508
|
+
const page = await harness.commit((tx) => tx.scanEntries({ conversationId }, ENTRY_PAGE_SIZE, cursor), context);
|
|
509
|
+
for (const record of page.items) {
|
|
510
|
+
const message = record.model?.[0];
|
|
511
|
+
if (record.conversationId !== conversationId ||
|
|
512
|
+
record.byTaskId !== undefined ||
|
|
513
|
+
!AssistantEntry.is(record) ||
|
|
514
|
+
message?.role !== "assistant") {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const key = `${message.provider}/${message.model}`;
|
|
518
|
+
const total = byModel.get(key) ?? createEmptyUsage();
|
|
519
|
+
addUsage(total, message.usage);
|
|
520
|
+
byModel.set(key, total);
|
|
521
|
+
}
|
|
522
|
+
cursor = page.next;
|
|
523
|
+
} while (cursor !== undefined);
|
|
524
|
+
return byModel;
|
|
525
|
+
}
|
|
526
|
+
async function recordImportedUsage(harness, conversationId, imported) {
|
|
527
|
+
await harness.commit(async (tx) => {
|
|
528
|
+
const doc = await tx.doc(SessionDoc, conversationId);
|
|
529
|
+
if (doc.importedUsage)
|
|
530
|
+
return;
|
|
531
|
+
const ledger = await tx.doc(UsageDoc, conversationId);
|
|
532
|
+
for (const [key, usage] of imported) {
|
|
533
|
+
const merged = createEmptyUsage();
|
|
534
|
+
const recorded = ledger.models[key];
|
|
535
|
+
if (recorded)
|
|
536
|
+
addUsage(merged, recorded);
|
|
537
|
+
addUsage(merged, usage);
|
|
538
|
+
ledger.models[key] = merged;
|
|
539
|
+
}
|
|
540
|
+
doc.importedUsage = true;
|
|
541
|
+
}, context);
|
|
542
|
+
}
|
|
432
543
|
export async function readImportedContexts(path) {
|
|
433
544
|
const harness = await Harness.open(await openNodeSqliteStorage(path), { models: {}, registry: createRegistry(), settings: { extensions: [] } }, context);
|
|
434
545
|
try {
|
|
@@ -486,8 +597,8 @@ export class SessionStore {
|
|
|
486
597
|
static open(office, key) {
|
|
487
598
|
return SessionStore.openOrCreate(office, key, (storage) => createSession(storage, key));
|
|
488
599
|
}
|
|
489
|
-
static openTask(office, key, requesterKey) {
|
|
490
|
-
return SessionStore.openOrCreate(office, key, (storage) => createTaskSession(storage, key, requesterKey));
|
|
600
|
+
static openTask(office, key, requesterKey, brief) {
|
|
601
|
+
return SessionStore.openOrCreate(office, key, (storage) => createTaskSession(storage, key, requesterKey, brief));
|
|
491
602
|
}
|
|
492
603
|
static async openOrCreate(office, key, create) {
|
|
493
604
|
const storage = await OfficeStorage.acquire(office.sessionsPath);
|
|
@@ -637,6 +748,25 @@ export class SessionStore {
|
|
|
637
748
|
return interrupted;
|
|
638
749
|
});
|
|
639
750
|
}
|
|
751
|
+
static async listTasks(office, requesterKey) {
|
|
752
|
+
if (!existsSync(office.sessionsPath))
|
|
753
|
+
return [];
|
|
754
|
+
return withOfficeStorage(office, async (storage) => {
|
|
755
|
+
const index = await storage.index();
|
|
756
|
+
const requester = index[requesterKey];
|
|
757
|
+
if (!requester)
|
|
758
|
+
return [];
|
|
759
|
+
const keyOf = new Map(Object.entries(index).map(([key, record]) => [record.conversationId, key]));
|
|
760
|
+
const tasks = [];
|
|
761
|
+
for (const child of await ownedConversations(storage, requester.conversationId)) {
|
|
762
|
+
const sessionKey = keyOf.get(child.id);
|
|
763
|
+
if (sessionKey === undefined || child.owner === undefined)
|
|
764
|
+
continue;
|
|
765
|
+
tasks.push(await readTaskState(storage, sessionKey, child.id, child.owner.taskId));
|
|
766
|
+
}
|
|
767
|
+
return tasks;
|
|
768
|
+
});
|
|
769
|
+
}
|
|
640
770
|
static async inspectExecution(office, key) {
|
|
641
771
|
if (!existsSync(office.sessionsPath))
|
|
642
772
|
return { open: false, started: false };
|
|
@@ -644,19 +774,52 @@ export class SessionStore {
|
|
|
644
774
|
const session = await lookupSession(storage, key);
|
|
645
775
|
if (!session)
|
|
646
776
|
return { open: false, started: false };
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
777
|
+
return readExecution(storage, session.conversation.id);
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
static async spend(office, key) {
|
|
781
|
+
if (!existsSync(office.sessionsPath))
|
|
782
|
+
return undefined;
|
|
783
|
+
return withOfficeStorage(office, async (storage) => {
|
|
784
|
+
const session = await lookupSession(storage, key);
|
|
785
|
+
if (!session)
|
|
786
|
+
return undefined;
|
|
787
|
+
const ledger = await storage.harness.snapshot(UsageDoc, session.conversation.id, context);
|
|
788
|
+
const newest = (await session.conversation.entries({}, 1, undefined, context)).items[0];
|
|
789
|
+
const timestamp = newest && toSessionEntry(newest, session.record.id)?.timestamp;
|
|
651
790
|
return {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
result: run?.endedAt !== undefined && run.status !== undefined
|
|
655
|
-
? { status: run.status, endedAt: run.endedAt }
|
|
656
|
-
: undefined,
|
|
791
|
+
usage: sumUsageState(ledger ?? { models: {}, tools: {} }),
|
|
792
|
+
updatedAt: timestamp || session.record.createdAt,
|
|
657
793
|
};
|
|
658
794
|
});
|
|
659
795
|
}
|
|
796
|
+
static async ownResponseUsage(office, key) {
|
|
797
|
+
if (!existsSync(office.sessionsPath))
|
|
798
|
+
return [];
|
|
799
|
+
return withOfficeStorage(office, async (storage) => {
|
|
800
|
+
const session = await lookupSession(storage, key);
|
|
801
|
+
if (!session)
|
|
802
|
+
return [];
|
|
803
|
+
const id = session.conversation.id;
|
|
804
|
+
return (await readRecords(session.conversation)).flatMap((record) => {
|
|
805
|
+
const message = record.model?.[0];
|
|
806
|
+
return record.conversationId === id && message?.role === "assistant"
|
|
807
|
+
? [{ timestamp: message.timestamp, usage: message.usage }]
|
|
808
|
+
: [];
|
|
809
|
+
});
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
static async isTask(office, key) {
|
|
813
|
+
if (!existsSync(office.sessionsPath))
|
|
814
|
+
return false;
|
|
815
|
+
return withOfficeStorage(office, async (storage) => {
|
|
816
|
+
const record = (await storage.index())[key];
|
|
817
|
+
if (!record)
|
|
818
|
+
return false;
|
|
819
|
+
const conversation = await storage.harness.commit((tx) => tx.conversation(record.conversationId), context);
|
|
820
|
+
return conversation?.owner !== undefined;
|
|
821
|
+
});
|
|
822
|
+
}
|
|
660
823
|
getHeader() {
|
|
661
824
|
this.assertOpen();
|
|
662
825
|
return structuredClone(this.header);
|
|
@@ -774,7 +937,12 @@ export class SessionStore {
|
|
|
774
937
|
});
|
|
775
938
|
}
|
|
776
939
|
async reset() {
|
|
777
|
-
await this.mutate(() =>
|
|
940
|
+
await this.mutate(async () => {
|
|
941
|
+
await this.conversation.reset(undefined, context);
|
|
942
|
+
await this.conversation.commit(async (tx) => {
|
|
943
|
+
(await tx.doc(ScriptStoreDoc, this.conversation.id)).values = {};
|
|
944
|
+
}, context);
|
|
945
|
+
});
|
|
778
946
|
}
|
|
779
947
|
async setSessionName(name) {
|
|
780
948
|
await this.updateSessionDoc((doc) => {
|