@geminixiang/mikan 1.2.2 → 1.3.0-beta.1
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 +45 -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 +186 -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,73 @@ 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
|
+
!recordsUsage(message)) {
|
|
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
|
+
function recordsUsage(message) {
|
|
527
|
+
return message?.role === "assistant" && isRecord(message.usage);
|
|
528
|
+
}
|
|
529
|
+
async function recordImportedUsage(harness, conversationId, imported) {
|
|
530
|
+
await harness.commit(async (tx) => {
|
|
531
|
+
const doc = await tx.doc(SessionDoc, conversationId);
|
|
532
|
+
if (doc.importedUsage)
|
|
533
|
+
return;
|
|
534
|
+
const ledger = await tx.doc(UsageDoc, conversationId);
|
|
535
|
+
for (const [key, usage] of imported) {
|
|
536
|
+
const merged = createEmptyUsage();
|
|
537
|
+
const recorded = ledger.models[key];
|
|
538
|
+
if (recorded)
|
|
539
|
+
addUsage(merged, recorded);
|
|
540
|
+
addUsage(merged, usage);
|
|
541
|
+
ledger.models[key] = merged;
|
|
542
|
+
}
|
|
543
|
+
doc.importedUsage = true;
|
|
544
|
+
}, context);
|
|
545
|
+
}
|
|
432
546
|
export async function readImportedContexts(path) {
|
|
433
547
|
const harness = await Harness.open(await openNodeSqliteStorage(path), { models: {}, registry: createRegistry(), settings: { extensions: [] } }, context);
|
|
434
548
|
try {
|
|
@@ -486,8 +600,8 @@ export class SessionStore {
|
|
|
486
600
|
static open(office, key) {
|
|
487
601
|
return SessionStore.openOrCreate(office, key, (storage) => createSession(storage, key));
|
|
488
602
|
}
|
|
489
|
-
static openTask(office, key, requesterKey) {
|
|
490
|
-
return SessionStore.openOrCreate(office, key, (storage) => createTaskSession(storage, key, requesterKey));
|
|
603
|
+
static openTask(office, key, requesterKey, brief) {
|
|
604
|
+
return SessionStore.openOrCreate(office, key, (storage) => createTaskSession(storage, key, requesterKey, brief));
|
|
491
605
|
}
|
|
492
606
|
static async openOrCreate(office, key, create) {
|
|
493
607
|
const storage = await OfficeStorage.acquire(office.sessionsPath);
|
|
@@ -637,6 +751,25 @@ export class SessionStore {
|
|
|
637
751
|
return interrupted;
|
|
638
752
|
});
|
|
639
753
|
}
|
|
754
|
+
static async listTasks(office, requesterKey) {
|
|
755
|
+
if (!existsSync(office.sessionsPath))
|
|
756
|
+
return [];
|
|
757
|
+
return withOfficeStorage(office, async (storage) => {
|
|
758
|
+
const index = await storage.index();
|
|
759
|
+
const requester = index[requesterKey];
|
|
760
|
+
if (!requester)
|
|
761
|
+
return [];
|
|
762
|
+
const keyOf = new Map(Object.entries(index).map(([key, record]) => [record.conversationId, key]));
|
|
763
|
+
const tasks = [];
|
|
764
|
+
for (const child of await ownedConversations(storage, requester.conversationId)) {
|
|
765
|
+
const sessionKey = keyOf.get(child.id);
|
|
766
|
+
if (sessionKey === undefined || child.owner === undefined)
|
|
767
|
+
continue;
|
|
768
|
+
tasks.push(await readTaskState(storage, sessionKey, child.id, child.owner.taskId));
|
|
769
|
+
}
|
|
770
|
+
return tasks;
|
|
771
|
+
});
|
|
772
|
+
}
|
|
640
773
|
static async inspectExecution(office, key) {
|
|
641
774
|
if (!existsSync(office.sessionsPath))
|
|
642
775
|
return { open: false, started: false };
|
|
@@ -644,19 +777,52 @@ export class SessionStore {
|
|
|
644
777
|
const session = await lookupSession(storage, key);
|
|
645
778
|
if (!session)
|
|
646
779
|
return { open: false, started: false };
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
780
|
+
return readExecution(storage, session.conversation.id);
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
static async spend(office, key) {
|
|
784
|
+
if (!existsSync(office.sessionsPath))
|
|
785
|
+
return undefined;
|
|
786
|
+
return withOfficeStorage(office, async (storage) => {
|
|
787
|
+
const session = await lookupSession(storage, key);
|
|
788
|
+
if (!session)
|
|
789
|
+
return undefined;
|
|
790
|
+
const ledger = await storage.harness.snapshot(UsageDoc, session.conversation.id, context);
|
|
791
|
+
const newest = (await session.conversation.entries({}, 1, undefined, context)).items[0];
|
|
792
|
+
const timestamp = newest && toSessionEntry(newest, session.record.id)?.timestamp;
|
|
651
793
|
return {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
result: run?.endedAt !== undefined && run.status !== undefined
|
|
655
|
-
? { status: run.status, endedAt: run.endedAt }
|
|
656
|
-
: undefined,
|
|
794
|
+
usage: sumUsageState(ledger ?? { models: {}, tools: {} }),
|
|
795
|
+
updatedAt: timestamp || session.record.createdAt,
|
|
657
796
|
};
|
|
658
797
|
});
|
|
659
798
|
}
|
|
799
|
+
static async ownResponseUsage(office, key) {
|
|
800
|
+
if (!existsSync(office.sessionsPath))
|
|
801
|
+
return [];
|
|
802
|
+
return withOfficeStorage(office, async (storage) => {
|
|
803
|
+
const session = await lookupSession(storage, key);
|
|
804
|
+
if (!session)
|
|
805
|
+
return [];
|
|
806
|
+
const id = session.conversation.id;
|
|
807
|
+
return (await readRecords(session.conversation)).flatMap((record) => {
|
|
808
|
+
const message = record.model?.[0];
|
|
809
|
+
return record.conversationId === id && recordsUsage(message)
|
|
810
|
+
? [{ timestamp: message.timestamp, usage: message.usage }]
|
|
811
|
+
: [];
|
|
812
|
+
});
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
static async isTask(office, key) {
|
|
816
|
+
if (!existsSync(office.sessionsPath))
|
|
817
|
+
return false;
|
|
818
|
+
return withOfficeStorage(office, async (storage) => {
|
|
819
|
+
const record = (await storage.index())[key];
|
|
820
|
+
if (!record)
|
|
821
|
+
return false;
|
|
822
|
+
const conversation = await storage.harness.commit((tx) => tx.conversation(record.conversationId), context);
|
|
823
|
+
return conversation?.owner !== undefined;
|
|
824
|
+
});
|
|
825
|
+
}
|
|
660
826
|
getHeader() {
|
|
661
827
|
this.assertOpen();
|
|
662
828
|
return structuredClone(this.header);
|
|
@@ -774,7 +940,12 @@ export class SessionStore {
|
|
|
774
940
|
});
|
|
775
941
|
}
|
|
776
942
|
async reset() {
|
|
777
|
-
await this.mutate(() =>
|
|
943
|
+
await this.mutate(async () => {
|
|
944
|
+
await this.conversation.reset(undefined, context);
|
|
945
|
+
await this.conversation.commit(async (tx) => {
|
|
946
|
+
(await tx.doc(ScriptStoreDoc, this.conversation.id)).values = {};
|
|
947
|
+
}, context);
|
|
948
|
+
});
|
|
778
949
|
}
|
|
779
950
|
async setSessionName(name) {
|
|
780
951
|
await this.updateSessionDoc((doc) => {
|