@ycodium-ai/plugin-conversation 0.2.3244 → 0.2.3250
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/README.md +5 -3
- package/assets/ui.js +306 -278
- package/dist/index.js +413 -385
- package/locales/en.json +91 -76
- package/locales/zh-CN.json +91 -76
- package/package.json +7 -15
package/dist/index.js
CHANGED
|
@@ -3,51 +3,9 @@ import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
|
3
3
|
|
|
4
4
|
// src/sessionMap/activate.ts
|
|
5
5
|
import * as NodeCrypto from "node:crypto";
|
|
6
|
-
import {
|
|
7
|
-
TURN_PROGRESS_TRIGGER_KIND,
|
|
8
|
-
projectOrNull
|
|
9
|
-
} from "@ycodium-ai/plugin-api";
|
|
6
|
+
import { TURN_PROGRESS_TRIGGER_KIND } from "@ycodium-ai/plugin-api";
|
|
10
7
|
|
|
11
8
|
// src/sessionMap/actions.ts
|
|
12
|
-
function createCommands(input) {
|
|
13
|
-
return [
|
|
14
|
-
{
|
|
15
|
-
requestId: input.commandId,
|
|
16
|
-
threadId: input.threadId,
|
|
17
|
-
projectId: input.projectId,
|
|
18
|
-
title: input.title,
|
|
19
|
-
modelSelection: input.modelSelection,
|
|
20
|
-
runtimeMode: input.runtimeMode,
|
|
21
|
-
interactionMode: input.interactionMode,
|
|
22
|
-
branch: null,
|
|
23
|
-
worktreePath: null
|
|
24
|
-
},
|
|
25
|
-
turnCommand({
|
|
26
|
-
threadId: input.threadId,
|
|
27
|
-
text: input.text,
|
|
28
|
-
messageId: input.messageId,
|
|
29
|
-
commandId: input.turnCommandId,
|
|
30
|
-
runtimeMode: input.runtimeMode,
|
|
31
|
-
interactionMode: input.interactionMode,
|
|
32
|
-
modelSelection: input.modelSelection
|
|
33
|
-
})
|
|
34
|
-
];
|
|
35
|
-
}
|
|
36
|
-
function turnCommand(input) {
|
|
37
|
-
return {
|
|
38
|
-
requestId: input.commandId,
|
|
39
|
-
threadId: input.threadId,
|
|
40
|
-
message: {
|
|
41
|
-
messageId: input.messageId,
|
|
42
|
-
role: "user",
|
|
43
|
-
text: input.text,
|
|
44
|
-
attachments: []
|
|
45
|
-
},
|
|
46
|
-
runtimeMode: input.runtimeMode,
|
|
47
|
-
interactionMode: input.interactionMode,
|
|
48
|
-
...input.modelSelection == null ? {} : { modelSelection: input.modelSelection }
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
9
|
function forkCommand(input) {
|
|
52
10
|
return {
|
|
53
11
|
requestId: input.commandId,
|
|
@@ -56,11 +14,6 @@ function forkCommand(input) {
|
|
|
56
14
|
sourceMessageId: input.sourceMessageId
|
|
57
15
|
};
|
|
58
16
|
}
|
|
59
|
-
function titleFromText(text2) {
|
|
60
|
-
const line = text2.trim().split("\n")[0] ?? "";
|
|
61
|
-
const title = line.trim().slice(0, 80);
|
|
62
|
-
return title.length > 0 ? title : "New session";
|
|
63
|
-
}
|
|
64
17
|
function forkPointOf(value) {
|
|
65
18
|
if (typeof value !== "object" || value === null) return null;
|
|
66
19
|
const point = value;
|
|
@@ -73,13 +26,12 @@ function forkPointOf(value) {
|
|
|
73
26
|
// src/ui/sessionMap/limits.ts
|
|
74
27
|
var MAX_CARD_TEXT = 8e3;
|
|
75
28
|
var MAX_PREVIEW_TEXT = 480;
|
|
76
|
-
var MAX_DETAIL_MESSAGES = 40;
|
|
77
|
-
var MAX_DETAIL_TEXT = 32e3;
|
|
78
29
|
var MAX_TURNS_PER_THREAD = 48;
|
|
79
30
|
var MAX_TOOLS_PER_TURN = 12;
|
|
80
31
|
var MAX_TOOL_LABEL = 180;
|
|
81
32
|
var THREAD_PAGE_SIZE = 200;
|
|
82
33
|
var PROJECTION_NAME = "card";
|
|
34
|
+
var MAX_LOAD_PER_CALL = 60;
|
|
83
35
|
|
|
84
36
|
// src/ui/sessionMap/text.ts
|
|
85
37
|
function clipText(value, limit = MAX_CARD_TEXT) {
|
|
@@ -127,22 +79,8 @@ function canvasJson(state) {
|
|
|
127
79
|
return `${JSON.stringify(state)}
|
|
128
80
|
`;
|
|
129
81
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
const hidden = new Set(state.hiddenThreadIds);
|
|
133
|
-
let changed = false;
|
|
134
|
-
for (const threadId of threadIds) {
|
|
135
|
-
if (threadId.length === 0 || seen.has(threadId)) continue;
|
|
136
|
-
seen.add(threadId);
|
|
137
|
-
changed = true;
|
|
138
|
-
if (!state.autoProjection) hidden.add(threadId);
|
|
139
|
-
}
|
|
140
|
-
if (!changed) return state;
|
|
141
|
-
return {
|
|
142
|
-
...state,
|
|
143
|
-
seenThreadIds: [...seen].slice(-MAX_IDS),
|
|
144
|
-
hiddenThreadIds: [...hidden].slice(-MAX_IDS)
|
|
145
|
-
};
|
|
82
|
+
function showArchived(state, show) {
|
|
83
|
+
return state.showArchived === show ? state : { ...state, showArchived: show };
|
|
146
84
|
}
|
|
147
85
|
function hideThread(state, threadId) {
|
|
148
86
|
if (state.hiddenThreadIds.includes(threadId)) return state;
|
|
@@ -152,34 +90,6 @@ function restoreThread(state, threadId) {
|
|
|
152
90
|
if (!state.hiddenThreadIds.includes(threadId)) return state;
|
|
153
91
|
return { ...state, hiddenThreadIds: state.hiddenThreadIds.filter((id) => id !== threadId) };
|
|
154
92
|
}
|
|
155
|
-
function clearPositions(state) {
|
|
156
|
-
if (state.positions.length === 0) return state;
|
|
157
|
-
return { ...state, positions: [] };
|
|
158
|
-
}
|
|
159
|
-
function rememberPosition(state, position) {
|
|
160
|
-
const next = state.positions.filter((item) => item.cardId !== position.cardId);
|
|
161
|
-
next.push({
|
|
162
|
-
cardId: position.cardId,
|
|
163
|
-
x: finite(position.x),
|
|
164
|
-
y: finite(position.y)
|
|
165
|
-
});
|
|
166
|
-
return { ...state, positions: next.slice(-MAX_POSITIONS) };
|
|
167
|
-
}
|
|
168
|
-
function patchCanvas(state, patch) {
|
|
169
|
-
const anchors = { ...state.anchors };
|
|
170
|
-
if (patch.anchor === null) {
|
|
171
|
-
} else if (patch.anchor !== void 0) {
|
|
172
|
-
if (patch.anchor.cardId.length === 0) delete anchors[patch.anchor.threadId];
|
|
173
|
-
else anchors[patch.anchor.threadId] = patch.anchor.cardId.slice(0, 200);
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
...state,
|
|
177
|
-
autoProjection: patch.autoProjection ?? state.autoProjection,
|
|
178
|
-
workspaceTitle: patch.workspaceTitle === void 0 ? state.workspaceTitle : patch.workspaceTitle.trim().slice(0, MAX_TITLE),
|
|
179
|
-
showArchived: patch.showArchived ?? state.showArchived,
|
|
180
|
-
anchors
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
93
|
function readRevision(value) {
|
|
184
94
|
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value < 1e9 ? value : 0;
|
|
185
95
|
}
|
|
@@ -486,15 +396,28 @@ var TranscriptCache = class {
|
|
|
486
396
|
}
|
|
487
397
|
};
|
|
488
398
|
|
|
399
|
+
// src/sessionMap/turnOutline.ts
|
|
400
|
+
function buildTurnOutline(messages, failedIds, waitingId) {
|
|
401
|
+
const failed = new Set(failedIds);
|
|
402
|
+
const users = messages.filter((message) => message.role === "user");
|
|
403
|
+
return users.map((message, index) => ({
|
|
404
|
+
messageId: message.messageId,
|
|
405
|
+
ordinal: index + 1,
|
|
406
|
+
...failed.has(message.messageId) ? { failed: true } : {},
|
|
407
|
+
...message.messageId === waitingId ? { waiting: true } : {}
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
|
|
489
411
|
// src/sessionMap/runtime.ts
|
|
490
|
-
async function listSessionShells(ctx) {
|
|
412
|
+
async function listSessionShells(ctx, projectId) {
|
|
491
413
|
const shells = [];
|
|
492
414
|
let cursor = null;
|
|
493
415
|
for (; ; ) {
|
|
494
416
|
const page = await ctx.threads.list({
|
|
495
417
|
limit: THREAD_PAGE_SIZE,
|
|
496
418
|
cursor,
|
|
497
|
-
archived: "include"
|
|
419
|
+
archived: "include",
|
|
420
|
+
...projectId === void 0 ? {} : { projectId }
|
|
498
421
|
});
|
|
499
422
|
shells.push(...page.items);
|
|
500
423
|
if (page.nextCursor === null || page.nextCursor === cursor) return shells;
|
|
@@ -507,19 +430,19 @@ function threadFromShell(shell) {
|
|
|
507
430
|
threadId: shell.threadId,
|
|
508
431
|
projectId: shell.projectId,
|
|
509
432
|
title: shell.title,
|
|
433
|
+
createdAt: shell.createdAt,
|
|
510
434
|
updatedAt: shell.updatedAt,
|
|
511
435
|
archived: shell.archivedAt !== null,
|
|
512
436
|
running: runningOf(shell.sessionStatus),
|
|
437
|
+
status: statusOfShell(shell.status),
|
|
513
438
|
sessionStatus: shell.sessionStatus,
|
|
514
|
-
forkedFrom: forkPointOf(shell.forkedFrom)
|
|
515
|
-
runtimeMode: shell.runtimeMode,
|
|
516
|
-
interactionMode: shell.interactionMode,
|
|
517
|
-
modelSelection: shell.modelSelection
|
|
439
|
+
forkedFrom: forkPointOf(shell.forkedFrom)
|
|
518
440
|
},
|
|
519
441
|
messages: [],
|
|
520
442
|
parentMessages: null,
|
|
521
443
|
tools: [],
|
|
522
|
-
textLimit: MAX_PREVIEW_TEXT
|
|
444
|
+
textLimit: MAX_PREVIEW_TEXT,
|
|
445
|
+
loaded: false
|
|
523
446
|
});
|
|
524
447
|
}
|
|
525
448
|
function projectThread(input) {
|
|
@@ -532,13 +455,22 @@ function projectThread(input) {
|
|
|
532
455
|
threadId: input.execution.threadId,
|
|
533
456
|
projectId: input.execution.projectId,
|
|
534
457
|
title: input.execution.title,
|
|
458
|
+
createdAt: input.execution.createdAt,
|
|
535
459
|
updatedAt: input.execution.updatedAt,
|
|
536
460
|
archived: input.execution.archived,
|
|
537
461
|
running: input.execution.running,
|
|
462
|
+
status: input.execution.status,
|
|
463
|
+
turnCount: own.filter((message) => message.role === "user").length,
|
|
464
|
+
loaded: input.loaded ?? true,
|
|
538
465
|
forkedFrom: input.execution.forkedFrom,
|
|
539
466
|
inheritedMessageCount: inherited,
|
|
540
467
|
cutWalkedBack: false,
|
|
541
|
-
turns: buildTurns(own, input.tools, input.execution.running, input.textLimit)
|
|
468
|
+
turns: buildTurns(own, input.tools, input.execution.running, input.textLimit),
|
|
469
|
+
turnOutline: buildTurnOutline(
|
|
470
|
+
own,
|
|
471
|
+
input.execution.failedMessageIds ?? [],
|
|
472
|
+
input.execution.status === "waiting" ? input.execution.waitingMessageId ?? own.findLast((message) => message.role === "user")?.messageId ?? null : null
|
|
473
|
+
)
|
|
542
474
|
};
|
|
543
475
|
}
|
|
544
476
|
function boardSnapshot(input) {
|
|
@@ -548,6 +480,14 @@ function boardSnapshot(input) {
|
|
|
548
480
|
const thread = byId.get(threadId);
|
|
549
481
|
return thread === void 0 ? [] : [{ threadId, title: thread.title }];
|
|
550
482
|
});
|
|
483
|
+
const sources = /* @__PURE__ */ new Map();
|
|
484
|
+
for (const thread of input.threads) {
|
|
485
|
+
const fork = thread.forkedFrom;
|
|
486
|
+
if (fork === null) continue;
|
|
487
|
+
const ids = sources.get(fork.sourceThreadId) ?? /* @__PURE__ */ new Set();
|
|
488
|
+
ids.add(fork.sourceMessageId);
|
|
489
|
+
sources.set(fork.sourceThreadId, ids);
|
|
490
|
+
}
|
|
551
491
|
const visible = input.threads.filter((thread) => {
|
|
552
492
|
if (hidden.has(thread.threadId)) return false;
|
|
553
493
|
if (!input.canvas.showArchived && thread.archived) return false;
|
|
@@ -556,20 +496,25 @@ function boardSnapshot(input) {
|
|
|
556
496
|
const ranked = [...visible].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
557
497
|
return {
|
|
558
498
|
projects: input.projects,
|
|
559
|
-
threads: ranked.map(
|
|
499
|
+
threads: ranked.map(
|
|
500
|
+
(thread) => previewThread({
|
|
501
|
+
...thread,
|
|
502
|
+
...thread.turnOutline === void 0 ? {} : {
|
|
503
|
+
turnOutline: thread.turnOutline.map(
|
|
504
|
+
(turn) => sources.get(thread.threadId)?.has(turn.messageId) ? { ...turn, forkSource: true } : turn
|
|
505
|
+
)
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
),
|
|
560
509
|
hiddenThreadIds: input.canvas.hiddenThreadIds,
|
|
561
510
|
hiddenThreads,
|
|
562
|
-
|
|
563
|
-
workspaceTitle: input.canvas.workspaceTitle,
|
|
564
|
-
autoProjection: input.canvas.autoProjection,
|
|
565
|
-
showArchived: input.canvas.showArchived,
|
|
566
|
-
positions: input.canvas.positions
|
|
511
|
+
showArchived: input.canvas.showArchived
|
|
567
512
|
};
|
|
568
513
|
}
|
|
569
514
|
function previewThread(thread) {
|
|
570
515
|
return {
|
|
571
516
|
...thread,
|
|
572
|
-
turns: thread.turns.map((turn) => {
|
|
517
|
+
turns: thread.turns.slice(-3).map((turn) => {
|
|
573
518
|
const question = clipText(turn.question, MAX_PREVIEW_TEXT);
|
|
574
519
|
const answer = clipText(turn.answer, MAX_PREVIEW_TEXT);
|
|
575
520
|
return {
|
|
@@ -582,6 +527,34 @@ function previewThread(thread) {
|
|
|
582
527
|
})
|
|
583
528
|
};
|
|
584
529
|
}
|
|
530
|
+
function statusOfShell(status) {
|
|
531
|
+
switch (status) {
|
|
532
|
+
case "needs-approval":
|
|
533
|
+
case "needs-input":
|
|
534
|
+
case "plan-ready":
|
|
535
|
+
return "waiting";
|
|
536
|
+
case "running":
|
|
537
|
+
case "background":
|
|
538
|
+
return "running";
|
|
539
|
+
case "failed":
|
|
540
|
+
return "failed";
|
|
541
|
+
case "stopped":
|
|
542
|
+
case "idle":
|
|
543
|
+
return "done";
|
|
544
|
+
default: {
|
|
545
|
+
const unreachable = status;
|
|
546
|
+
return unreachable;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function statusOfExecution(execution, running) {
|
|
551
|
+
if (execution.hasPendingApprovals || execution.hasPendingUserInput) return "waiting";
|
|
552
|
+
if (running) return "running";
|
|
553
|
+
if (execution.latestTurn?.state === "error" || execution.session?.status === "error") {
|
|
554
|
+
return "failed";
|
|
555
|
+
}
|
|
556
|
+
return "done";
|
|
557
|
+
}
|
|
585
558
|
function runningOf(status) {
|
|
586
559
|
return status === "starting" || status === "running";
|
|
587
560
|
}
|
|
@@ -591,14 +564,22 @@ function executionRow(execution) {
|
|
|
591
564
|
threadId: execution.threadId,
|
|
592
565
|
projectId: execution.projectId,
|
|
593
566
|
title: execution.title,
|
|
567
|
+
createdAt: execution.createdAt,
|
|
594
568
|
updatedAt: execution.updatedAt,
|
|
595
569
|
archived: execution.archivedAt !== null,
|
|
596
570
|
running,
|
|
571
|
+
status: statusOfExecution(execution, running),
|
|
597
572
|
sessionStatus: execution.session?.status ?? null,
|
|
598
573
|
forkedFrom: forkPointOf(execution.forkedFrom),
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
574
|
+
failedMessageIds: [
|
|
575
|
+
...(execution.turnIntents ?? []).flatMap(
|
|
576
|
+
(intent) => intent.turn?.state === "error" ? [intent.turn.userMessageId] : []
|
|
577
|
+
),
|
|
578
|
+
...execution.latestTurn?.state === "error" && execution.latestTurn.userMessageId !== void 0 ? [execution.latestTurn.userMessageId] : []
|
|
579
|
+
],
|
|
580
|
+
waitingMessageId: (execution.turnIntents ?? []).find(
|
|
581
|
+
(intent) => intent.turn?.turnId != null && intent.turn.turnId === execution.session?.activeTurnId
|
|
582
|
+
)?.turn?.userMessageId ?? execution.latestTurn?.userMessageId ?? null
|
|
602
583
|
};
|
|
603
584
|
}
|
|
604
585
|
async function executionOf(ctx, threadId) {
|
|
@@ -607,11 +588,109 @@ async function executionOf(ctx, threadId) {
|
|
|
607
588
|
return executionRow(execution);
|
|
608
589
|
}
|
|
609
590
|
|
|
591
|
+
// src/sessionMap/mapDirectory.ts
|
|
592
|
+
var MapDirectory = class {
|
|
593
|
+
rows = /* @__PURE__ */ new Map();
|
|
594
|
+
projects = /* @__PURE__ */ new Map();
|
|
595
|
+
sources = /* @__PURE__ */ new Map();
|
|
596
|
+
ready = /* @__PURE__ */ new Set();
|
|
597
|
+
flights = /* @__PURE__ */ new Map();
|
|
598
|
+
ctx;
|
|
599
|
+
constructor(ctx) {
|
|
600
|
+
this.ctx = ctx;
|
|
601
|
+
}
|
|
602
|
+
isReady(projectId) {
|
|
603
|
+
return this.ready.has(null) || this.ready.has(projectId);
|
|
604
|
+
}
|
|
605
|
+
ensure(projectId) {
|
|
606
|
+
if (this.isReady(projectId)) return Promise.resolve();
|
|
607
|
+
const pending = this.flights.get(projectId);
|
|
608
|
+
if (pending !== void 0) return pending;
|
|
609
|
+
const flight = listSessionShells(this.ctx, projectId ?? void 0).then((shells) => {
|
|
610
|
+
for (const shell of shells) this.set(threadFromShell(shell));
|
|
611
|
+
this.ready.add(projectId);
|
|
612
|
+
}).finally(() => this.flights.delete(projectId));
|
|
613
|
+
this.flights.set(projectId, flight);
|
|
614
|
+
return flight;
|
|
615
|
+
}
|
|
616
|
+
set(thread) {
|
|
617
|
+
const previous = this.rows.get(thread.threadId);
|
|
618
|
+
if (previous?.forkedFrom != null) this.removeSource(previous);
|
|
619
|
+
if (previous !== void 0 && previous.projectId !== thread.projectId)
|
|
620
|
+
this.projects.get(previous.projectId)?.delete(thread.threadId);
|
|
621
|
+
this.rows.set(thread.threadId, thread);
|
|
622
|
+
const ids = this.projects.get(thread.projectId) ?? /* @__PURE__ */ new Set();
|
|
623
|
+
ids.add(thread.threadId);
|
|
624
|
+
this.projects.set(thread.projectId, ids);
|
|
625
|
+
if (thread.forkedFrom !== null) {
|
|
626
|
+
const fork = thread.forkedFrom;
|
|
627
|
+
const messages = this.sources.get(fork.sourceThreadId) ?? /* @__PURE__ */ new Map();
|
|
628
|
+
const children = messages.get(fork.sourceMessageId) ?? /* @__PURE__ */ new Set();
|
|
629
|
+
children.add(thread.threadId);
|
|
630
|
+
messages.set(fork.sourceMessageId, children);
|
|
631
|
+
this.sources.set(fork.sourceThreadId, messages);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
decorate(thread) {
|
|
635
|
+
const sources = this.sources.get(thread.threadId);
|
|
636
|
+
if (sources === void 0 || thread.turnOutline === void 0) return thread;
|
|
637
|
+
return {
|
|
638
|
+
...thread,
|
|
639
|
+
turnOutline: thread.turnOutline.map(
|
|
640
|
+
(turn) => sources.has(turn.messageId) ? { ...turn, forkSource: true } : turn
|
|
641
|
+
)
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
get(threadId) {
|
|
645
|
+
return this.rows.get(threadId);
|
|
646
|
+
}
|
|
647
|
+
list(projectId) {
|
|
648
|
+
if (projectId === null) return [...this.rows.values()];
|
|
649
|
+
return [...this.projects.get(projectId) ?? []].flatMap((id) => {
|
|
650
|
+
const row = this.rows.get(id);
|
|
651
|
+
return row === void 0 ? [] : [row];
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
delete(threadId) {
|
|
655
|
+
const row = this.rows.get(threadId);
|
|
656
|
+
if (row === void 0) return null;
|
|
657
|
+
this.removeSource(row);
|
|
658
|
+
this.rows.delete(threadId);
|
|
659
|
+
this.projects.get(row.projectId)?.delete(threadId);
|
|
660
|
+
return row.projectId;
|
|
661
|
+
}
|
|
662
|
+
removeSource(thread) {
|
|
663
|
+
const fork = thread.forkedFrom;
|
|
664
|
+
if (fork === null) return;
|
|
665
|
+
const messages = this.sources.get(fork.sourceThreadId);
|
|
666
|
+
const children = messages?.get(fork.sourceMessageId);
|
|
667
|
+
children?.delete(thread.threadId);
|
|
668
|
+
if (children?.size === 0) messages?.delete(fork.sourceMessageId);
|
|
669
|
+
if (messages?.size === 0) this.sources.delete(fork.sourceThreadId);
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
// src/sessionMap/turnCards.ts
|
|
674
|
+
function turnCards(messages, ids, tools, running) {
|
|
675
|
+
const wanted = new Set(ids.slice(0, 60));
|
|
676
|
+
const starts = messages.flatMap((message, index) => message.role === "user" ? [index] : []);
|
|
677
|
+
return starts.flatMap((start, index) => {
|
|
678
|
+
const message = messages[start];
|
|
679
|
+
if (message === void 0 || !wanted.has(message.messageId)) return [];
|
|
680
|
+
const latest = index === starts.length - 1;
|
|
681
|
+
return buildTurns(
|
|
682
|
+
messages.slice(start, starts[index + 1]),
|
|
683
|
+
latest ? tools : [],
|
|
684
|
+
latest && running,
|
|
685
|
+
MAX_PREVIEW_TEXT
|
|
686
|
+
);
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
|
|
610
690
|
// src/sessionMap/threadReads.ts
|
|
611
691
|
import {
|
|
612
692
|
isThreadNotFound,
|
|
613
693
|
threadMessagesNewestFirst,
|
|
614
|
-
threadOrNull,
|
|
615
694
|
THREAD_ACTIVITIES_LIST_LIMIT,
|
|
616
695
|
TOOL_LIFECYCLE_ACTIVITY_KINDS
|
|
617
696
|
} from "@ycodium-ai/plugin-api";
|
|
@@ -697,37 +776,16 @@ async function readToolTitles(threads, threadId) {
|
|
|
697
776
|
}
|
|
698
777
|
return titles;
|
|
699
778
|
}
|
|
700
|
-
async function readDetail(threads, threadId) {
|
|
701
|
-
const thread = await threadOrNull(threads, threadId);
|
|
702
|
-
if (thread === null) return { found: false, messages: [] };
|
|
703
|
-
let latest;
|
|
704
|
-
try {
|
|
705
|
-
latest = await threads.messages.list({
|
|
706
|
-
threadId,
|
|
707
|
-
limit: MAX_DETAIL_MESSAGES,
|
|
708
|
-
includeTotal: true
|
|
709
|
-
});
|
|
710
|
-
} catch (error) {
|
|
711
|
-
if (isThreadNotFound(error)) return { found: false, messages: [] };
|
|
712
|
-
throw error;
|
|
713
|
-
}
|
|
714
|
-
return {
|
|
715
|
-
found: true,
|
|
716
|
-
title: thread.title,
|
|
717
|
-
omitted: Math.max(0, (latest.total ?? 0) - latest.items.length),
|
|
718
|
-
messages: latest.items.toReversed().map((message) => {
|
|
719
|
-
const clipped = clipText(message.text, MAX_DETAIL_TEXT);
|
|
720
|
-
return {
|
|
721
|
-
messageId: message.id,
|
|
722
|
-
role: message.role,
|
|
723
|
-
text: clipped.text,
|
|
724
|
-
truncated: clipped.truncated
|
|
725
|
-
};
|
|
726
|
-
})
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
779
|
|
|
730
780
|
// src/sessionMap/mapLive.ts
|
|
781
|
+
var WAITING_ACTIVITY_KINDS = /* @__PURE__ */ new Set([
|
|
782
|
+
"approval.requested",
|
|
783
|
+
"approval.resolved",
|
|
784
|
+
"provider.approval.respond.failed",
|
|
785
|
+
"user-input.requested",
|
|
786
|
+
"user-input.resolved",
|
|
787
|
+
"provider.user-input.respond.failed"
|
|
788
|
+
]);
|
|
731
789
|
function createSessionMapHub(ctx, canvas, options = {}) {
|
|
732
790
|
return new SessionMapHub(ctx, canvas, options.timers !== false);
|
|
733
791
|
}
|
|
@@ -738,14 +796,15 @@ var SessionMapHub = class {
|
|
|
738
796
|
tools = /* @__PURE__ */ new Map();
|
|
739
797
|
toolTitles = /* @__PURE__ */ new Map();
|
|
740
798
|
executions = /* @__PURE__ */ new Map();
|
|
799
|
+
executionGenerations = /* @__PURE__ */ new Map();
|
|
741
800
|
live = /* @__PURE__ */ new Map();
|
|
742
801
|
pendingMessage = /* @__PURE__ */ new Map();
|
|
743
802
|
queued = /* @__PURE__ */ new Set();
|
|
744
|
-
removed = /* @__PURE__ */ new
|
|
745
|
-
emitters = /* @__PURE__ */ new
|
|
746
|
-
|
|
803
|
+
removed = /* @__PURE__ */ new Map();
|
|
804
|
+
emitters = /* @__PURE__ */ new Map();
|
|
805
|
+
pendingFull = /* @__PURE__ */ new Set();
|
|
806
|
+
directory;
|
|
747
807
|
projects = null;
|
|
748
|
-
directoryFlight = null;
|
|
749
808
|
tail = Promise.resolve();
|
|
750
809
|
wantFull = false;
|
|
751
810
|
dueTimer = null;
|
|
@@ -756,13 +815,16 @@ var SessionMapHub = class {
|
|
|
756
815
|
this.ctx = ctx;
|
|
757
816
|
this.canvas = canvas;
|
|
758
817
|
this.timers = timers;
|
|
818
|
+
this.directory = new MapDirectory(ctx);
|
|
759
819
|
}
|
|
760
|
-
subscribe(emit) {
|
|
761
|
-
this.emitters.
|
|
820
|
+
subscribe(emit, projectId = null) {
|
|
821
|
+
this.emitters.set(emit, projectId);
|
|
822
|
+
this.pendingFull.add(emit);
|
|
762
823
|
const deferred = this.gate.takeDeferred();
|
|
763
|
-
this.enqueue(deferred,
|
|
824
|
+
this.enqueue(deferred, false);
|
|
764
825
|
return () => {
|
|
765
826
|
this.emitters.delete(emit);
|
|
827
|
+
this.pendingFull.delete(emit);
|
|
766
828
|
};
|
|
767
829
|
}
|
|
768
830
|
onMessage(threadId, event, now = Date.now()) {
|
|
@@ -780,8 +842,12 @@ var SessionMapHub = class {
|
|
|
780
842
|
const threadId = event.threadId;
|
|
781
843
|
if (progress === null || threadId === null || threadId.length === 0) return;
|
|
782
844
|
const cached = this.executions.get(threadId);
|
|
783
|
-
|
|
784
|
-
|
|
845
|
+
const running = progress.phase !== "ended";
|
|
846
|
+
if (!running || cached === void 0) this.invalidateExecution(threadId);
|
|
847
|
+
else if (cached !== void 0 && !cached.running) {
|
|
848
|
+
this.invalidateExecution(threadId);
|
|
849
|
+
const status = cached.status === "waiting" ? "waiting" : "running";
|
|
850
|
+
this.executions.set(threadId, { ...cached, running, status });
|
|
785
851
|
}
|
|
786
852
|
const known = this.toolTitles.get(threadId);
|
|
787
853
|
this.tools.set(
|
|
@@ -801,28 +867,58 @@ var SessionMapHub = class {
|
|
|
801
867
|
}
|
|
802
868
|
onDirectory(type, threadId) {
|
|
803
869
|
if (type === "thread.deleted") {
|
|
870
|
+
const projectId = this.directory.delete(threadId);
|
|
804
871
|
this.drop(threadId);
|
|
805
|
-
this.removed.
|
|
806
|
-
if (this.shells !== null) {
|
|
807
|
-
this.shells = this.shells.filter((shell) => shell.threadId !== threadId);
|
|
808
|
-
}
|
|
872
|
+
if (projectId !== null) this.removed.set(threadId, projectId);
|
|
809
873
|
if (this.emitters.size > 0) this.enqueue([], false);
|
|
810
874
|
return;
|
|
811
875
|
}
|
|
812
|
-
this.shells = null;
|
|
813
|
-
this.projects = null;
|
|
814
876
|
if (threadId.length > 0) {
|
|
815
877
|
this.transcripts.invalidate(threadId);
|
|
816
|
-
this.
|
|
878
|
+
this.invalidateExecution(threadId);
|
|
817
879
|
}
|
|
818
880
|
if (threadId.length > 0) this.note(threadId, Date.now(), false);
|
|
819
881
|
else if (this.emitters.size > 0) this.enqueue([], true);
|
|
820
882
|
}
|
|
883
|
+
/** 只有审批与提问的发出、答复会改「等你」;别的活动(工具进展等)不重读执行。 */
|
|
884
|
+
onActivity(threadId, payload, now = Date.now()) {
|
|
885
|
+
if (!isRecord(payload) || !isRecord(payload.activity)) return;
|
|
886
|
+
if (WAITING_ACTIVITY_KINDS.has(readString(payload.activity.kind))) this.onStatus(threadId, now);
|
|
887
|
+
}
|
|
888
|
+
/** 审批、提问或会话状态变了:卡片状态要跟着变,执行行重读。 */
|
|
889
|
+
onStatus(threadId, now = Date.now()) {
|
|
890
|
+
if (threadId.length === 0) return;
|
|
891
|
+
this.invalidateExecution(threadId);
|
|
892
|
+
this.note(threadId, now, false);
|
|
893
|
+
}
|
|
894
|
+
/** 屏幕上的卡片只有目录那几样:读它们的正文。读过的不再读。 */
|
|
895
|
+
load(threadIds, now = Date.now()) {
|
|
896
|
+
for (const threadId of threadIds.slice(0, MAX_LOAD_PER_CALL)) {
|
|
897
|
+
if (threadId.length === 0 || this.cards.has(threadId)) continue;
|
|
898
|
+
this.note(threadId, now, true);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
821
901
|
/** 用户命令:立刻读这一条,不管节流窗口。 */
|
|
822
902
|
touch(threadId, now = Date.now()) {
|
|
823
|
-
this.
|
|
903
|
+
this.invalidateExecution(threadId);
|
|
824
904
|
this.note(threadId, now, true);
|
|
825
905
|
}
|
|
906
|
+
async readTurns(threadId, messageIds) {
|
|
907
|
+
const execution = await this.executionFor(threadId);
|
|
908
|
+
if (execution === null) return { threadId, turns: [] };
|
|
909
|
+
const messages = await this.loadMessages(threadId);
|
|
910
|
+
const parent = execution.forkedFrom === null ? null : await this.loadParent(execution.forkedFrom.sourceThreadId);
|
|
911
|
+
const inherited = inheritedMessageCount(parent, execution.forkedFrom?.sourceMessageId ?? null);
|
|
912
|
+
return {
|
|
913
|
+
threadId,
|
|
914
|
+
turns: turnCards(
|
|
915
|
+
messages.slice(inherited),
|
|
916
|
+
messageIds,
|
|
917
|
+
this.tools.get(threadId) ?? [],
|
|
918
|
+
execution.running
|
|
919
|
+
)
|
|
920
|
+
};
|
|
921
|
+
}
|
|
826
922
|
canvasChanged() {
|
|
827
923
|
this.enqueue([], true);
|
|
828
924
|
}
|
|
@@ -876,16 +972,49 @@ var SessionMapHub = class {
|
|
|
876
972
|
const full = this.wantFull;
|
|
877
973
|
this.queued.clear();
|
|
878
974
|
this.wantFull = false;
|
|
975
|
+
for (const scope of new Set(this.emitters.values())) await this.directory.ensure(scope);
|
|
879
976
|
for (const id of ids) await this.projectStored(id);
|
|
880
977
|
if (this.emitters.size === 0) return;
|
|
881
|
-
|
|
882
|
-
|
|
978
|
+
const fresh = /* @__PURE__ */ new Set();
|
|
979
|
+
for (const [emit, scope] of this.emitters) {
|
|
980
|
+
if (!this.directory.isReady(scope)) continue;
|
|
981
|
+
if (full || this.pendingFull.has(emit)) fresh.add(emit);
|
|
982
|
+
this.pendingFull.delete(emit);
|
|
983
|
+
}
|
|
984
|
+
if (fresh.size > 0) await this.emitFull(fresh);
|
|
985
|
+
if (ids.length > 0 || this.removed.size > 0)
|
|
986
|
+
this.emitPatch(ids, /* @__PURE__ */ new Set([...fresh, ...this.pendingFull]));
|
|
883
987
|
}
|
|
884
988
|
async projectStored(threadId) {
|
|
989
|
+
const generation = this.executionGenerations.get(threadId) ?? 0;
|
|
990
|
+
const execution = await this.executionFor(threadId);
|
|
991
|
+
if (generation !== (this.executionGenerations.get(threadId) ?? 0))
|
|
992
|
+
return this.projectStored(threadId);
|
|
993
|
+
if (execution !== null) {
|
|
994
|
+
this.directory.set(
|
|
995
|
+
projectThread({
|
|
996
|
+
execution,
|
|
997
|
+
messages: [],
|
|
998
|
+
parentMessages: null,
|
|
999
|
+
tools: [],
|
|
1000
|
+
textLimit: MAX_CARD_TEXT,
|
|
1001
|
+
loaded: false
|
|
1002
|
+
})
|
|
1003
|
+
);
|
|
1004
|
+
if (![...this.emitters.values()].some(
|
|
1005
|
+
(scope) => scope === null || scope === execution.projectId
|
|
1006
|
+
)) {
|
|
1007
|
+
this.cards.delete(threadId);
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
885
1011
|
const projected = await this.projectOne(threadId);
|
|
1012
|
+
if (generation !== (this.executionGenerations.get(threadId) ?? 0))
|
|
1013
|
+
return this.projectStored(threadId);
|
|
886
1014
|
if (projected === null) {
|
|
1015
|
+
const projectId = this.directory.delete(threadId);
|
|
887
1016
|
this.drop(threadId);
|
|
888
|
-
this.removed.
|
|
1017
|
+
if (projectId !== null) this.removed.set(threadId, projectId);
|
|
889
1018
|
return;
|
|
890
1019
|
}
|
|
891
1020
|
this.cards.set(threadId, projected);
|
|
@@ -924,11 +1053,19 @@ var SessionMapHub = class {
|
|
|
924
1053
|
return applyLiveAnswer(projected, this.transcripts.get(threadId) ?? messages, live, answer);
|
|
925
1054
|
}
|
|
926
1055
|
async executionFor(threadId) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1056
|
+
for (; ; ) {
|
|
1057
|
+
const cached = this.executions.get(threadId);
|
|
1058
|
+
if (cached !== void 0) return cached;
|
|
1059
|
+
const generation = this.executionGenerations.get(threadId) ?? 0;
|
|
1060
|
+
const row = await executionOf(this.ctx, threadId);
|
|
1061
|
+
if (generation !== (this.executionGenerations.get(threadId) ?? 0)) continue;
|
|
1062
|
+
if (row !== null) this.executions.set(threadId, row);
|
|
1063
|
+
return row;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
invalidateExecution(threadId) {
|
|
1067
|
+
this.executions.delete(threadId);
|
|
1068
|
+
this.executionGenerations.set(threadId, (this.executionGenerations.get(threadId) ?? 0) + 1);
|
|
932
1069
|
}
|
|
933
1070
|
async seedTranscript(threadId) {
|
|
934
1071
|
if (this.transcripts.has(threadId)) return;
|
|
@@ -965,62 +1102,55 @@ var SessionMapHub = class {
|
|
|
965
1102
|
}
|
|
966
1103
|
return this.transcripts.get(threadId) ?? [];
|
|
967
1104
|
}
|
|
968
|
-
async emitFull() {
|
|
969
|
-
await this.
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1105
|
+
async emitFull(targets) {
|
|
1106
|
+
const state = await this.canvas.read();
|
|
1107
|
+
if ([...targets].some((emit) => this.emitters.get(emit) === null) && this.projects === null) {
|
|
1108
|
+
this.projects = (await this.ctx.projects.list({ repositoryIdentity: "skip" })).map(
|
|
1109
|
+
(project) => ({
|
|
1110
|
+
projectId: project.id,
|
|
1111
|
+
title: project.title,
|
|
1112
|
+
workspaceRoot: project.workspaceRoot,
|
|
1113
|
+
archived: project.archivedAt != null
|
|
1114
|
+
})
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
for (const emit of targets) {
|
|
1118
|
+
const scope = this.emitters.get(emit);
|
|
1119
|
+
if (scope === void 0) continue;
|
|
1120
|
+
const threads = this.directory.list(scope).map((row) => this.cards.get(row.threadId) ?? row);
|
|
1121
|
+
const ids = new Set(threads.map((thread) => thread.threadId));
|
|
1122
|
+
emit(
|
|
1123
|
+
boardSnapshot({
|
|
1124
|
+
projects: scope === null ? this.projects ?? [] : [],
|
|
1125
|
+
threads,
|
|
1126
|
+
canvas: scope === null ? state : { ...state, hiddenThreadIds: state.hiddenThreadIds.filter((id) => ids.has(id)) }
|
|
1127
|
+
})
|
|
1128
|
+
);
|
|
977
1129
|
}
|
|
978
|
-
const threads = shells.map((shell) => this.cards.get(shell.threadId) ?? threadFromShell(shell));
|
|
979
|
-
const frame = boardSnapshot({
|
|
980
|
-
projects: this.projects ?? [],
|
|
981
|
-
threads,
|
|
982
|
-
canvas: state
|
|
983
|
-
});
|
|
984
|
-
this.removed.clear();
|
|
985
|
-
for (const emit of this.emitters) emit(frame);
|
|
986
1130
|
}
|
|
987
|
-
emitPatch(ids) {
|
|
988
|
-
const
|
|
1131
|
+
emitPatch(ids, skip) {
|
|
1132
|
+
const removed = new Map(this.removed);
|
|
989
1133
|
this.removed.clear();
|
|
990
1134
|
const threads = ids.flatMap((id) => {
|
|
991
1135
|
const card = this.cards.get(id);
|
|
992
|
-
return card === void 0 ? [] : [card];
|
|
1136
|
+
return card === void 0 ? [] : [previewThread(this.directory.decorate(card))];
|
|
993
1137
|
});
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
1003
|
-
return this.directoryFlight;
|
|
1004
|
-
}
|
|
1005
|
-
async loadDirectory() {
|
|
1006
|
-
const shells = await listSessionShells(this.ctx);
|
|
1007
|
-
const projects = (await this.ctx.projects.list({ repositoryIdentity: "skip" })).map(
|
|
1008
|
-
(project) => ({
|
|
1009
|
-
projectId: project.id,
|
|
1010
|
-
title: project.title,
|
|
1011
|
-
workspaceRoot: project.workspaceRoot,
|
|
1012
|
-
archived: project.archivedAt != null
|
|
1013
|
-
})
|
|
1014
|
-
);
|
|
1015
|
-
this.shells = shells;
|
|
1016
|
-
this.projects = projects;
|
|
1138
|
+
for (const [emit, scope] of Array.from(this.emitters)) {
|
|
1139
|
+
if (skip.has(emit) || !this.emitters.has(emit)) continue;
|
|
1140
|
+
const scopedThreads = scope === null ? threads : threads.filter((thread) => thread.projectId === scope);
|
|
1141
|
+
const removedThreadIds = [...removed].flatMap(
|
|
1142
|
+
([id, projectId]) => scope === null || projectId === scope ? [id] : []
|
|
1143
|
+
);
|
|
1144
|
+
if (scopedThreads.length > 0 || removedThreadIds.length > 0)
|
|
1145
|
+
emit({ patch: true, threads: scopedThreads, removedThreadIds });
|
|
1146
|
+
}
|
|
1017
1147
|
}
|
|
1018
1148
|
drop(threadId) {
|
|
1019
1149
|
this.cards.delete(threadId);
|
|
1020
1150
|
this.transcripts.invalidate(threadId);
|
|
1021
1151
|
this.tools.delete(threadId);
|
|
1022
1152
|
this.toolTitles.delete(threadId);
|
|
1023
|
-
this.
|
|
1153
|
+
this.invalidateExecution(threadId);
|
|
1024
1154
|
this.live.delete(threadId);
|
|
1025
1155
|
this.pendingMessage.delete(threadId);
|
|
1026
1156
|
}
|
|
@@ -1049,7 +1179,9 @@ function activateSessionMap(ctx) {
|
|
|
1049
1179
|
"thread.deleted",
|
|
1050
1180
|
"thread.message-sent",
|
|
1051
1181
|
"thread.archived",
|
|
1052
|
-
"thread.unarchived"
|
|
1182
|
+
"thread.unarchived",
|
|
1183
|
+
"thread.activity-appended",
|
|
1184
|
+
"thread.session-set"
|
|
1053
1185
|
],
|
|
1054
1186
|
handle: (event) => {
|
|
1055
1187
|
const threadId = event.aggregateKind === "thread" ? event.aggregateId : "";
|
|
@@ -1057,6 +1189,14 @@ function activateSessionMap(ctx) {
|
|
|
1057
1189
|
hub.onMessage(threadId, event);
|
|
1058
1190
|
return;
|
|
1059
1191
|
}
|
|
1192
|
+
if (event.type === "thread.session-set") {
|
|
1193
|
+
hub.onStatus(threadId);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
if (event.type === "thread.activity-appended") {
|
|
1197
|
+
hub.onActivity(threadId, event.payload);
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1060
1200
|
hub.onDirectory(event.type, threadId);
|
|
1061
1201
|
}
|
|
1062
1202
|
});
|
|
@@ -1077,16 +1217,54 @@ function activateSessionMap(ctx) {
|
|
|
1077
1217
|
return { ok: true };
|
|
1078
1218
|
}
|
|
1079
1219
|
});
|
|
1220
|
+
ctx.command({
|
|
1221
|
+
name: "load",
|
|
1222
|
+
description: "Read the sessions whose cards are on screen.",
|
|
1223
|
+
scope: "read",
|
|
1224
|
+
payloadSchema: {
|
|
1225
|
+
type: "object",
|
|
1226
|
+
properties: { threadIds: { type: "array", items: { type: "string" } } },
|
|
1227
|
+
required: ["threadIds"]
|
|
1228
|
+
},
|
|
1229
|
+
handle: async (payload) => {
|
|
1230
|
+
const ids = payload["threadIds"];
|
|
1231
|
+
hub.load(Array.isArray(ids) ? ids.filter((id) => typeof id === "string") : []);
|
|
1232
|
+
return { ok: true };
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
ctx.command({
|
|
1236
|
+
name: "turns",
|
|
1237
|
+
description: "Read short text for the turn cards on screen.",
|
|
1238
|
+
scope: "read",
|
|
1239
|
+
payloadSchema: {
|
|
1240
|
+
type: "object",
|
|
1241
|
+
properties: {
|
|
1242
|
+
threadId: { type: "string" },
|
|
1243
|
+
messageIds: { type: "array", maxItems: 60, items: { type: "string" } }
|
|
1244
|
+
},
|
|
1245
|
+
required: ["threadId", "messageIds"]
|
|
1246
|
+
},
|
|
1247
|
+
handle: async (payload) => {
|
|
1248
|
+
const ids = payload["messageIds"];
|
|
1249
|
+
return hub.readTurns(
|
|
1250
|
+
readString(payload["threadId"]),
|
|
1251
|
+
Array.isArray(ids) ? ids.filter((id) => typeof id === "string") : []
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1080
1255
|
ctx.command({
|
|
1081
1256
|
name: "snapshot",
|
|
1082
1257
|
description: "Read the session map.",
|
|
1083
1258
|
scope: "read",
|
|
1084
|
-
payloadSchema: { type: "object", properties: {} },
|
|
1085
|
-
handle: async () => {
|
|
1259
|
+
payloadSchema: { type: "object", properties: { projectId: { type: "string" } } },
|
|
1260
|
+
handle: async (payload) => {
|
|
1086
1261
|
const frames = [];
|
|
1087
|
-
const stop = hub.subscribe(
|
|
1088
|
-
|
|
1089
|
-
|
|
1262
|
+
const stop = hub.subscribe(
|
|
1263
|
+
(frame) => {
|
|
1264
|
+
frames.push(frame);
|
|
1265
|
+
},
|
|
1266
|
+
readString(payload["projectId"]) || null
|
|
1267
|
+
);
|
|
1090
1268
|
await hub.settled();
|
|
1091
1269
|
stop();
|
|
1092
1270
|
return frames.at(-1) ?? { projects: [], threads: [] };
|
|
@@ -1097,11 +1275,14 @@ function activateSessionMap(ctx) {
|
|
|
1097
1275
|
description: "Follow the session map.",
|
|
1098
1276
|
scope: "read",
|
|
1099
1277
|
buffer: { latest: 1 },
|
|
1100
|
-
payloadSchema: { type: "object", properties: {} },
|
|
1101
|
-
handle: async (
|
|
1102
|
-
const stop = hub.subscribe(
|
|
1103
|
-
|
|
1104
|
-
|
|
1278
|
+
payloadSchema: { type: "object", properties: { projectId: { type: "string" } } },
|
|
1279
|
+
handle: async (payload, emit, signal) => {
|
|
1280
|
+
const stop = hub.subscribe(
|
|
1281
|
+
(frame) => {
|
|
1282
|
+
emit(frame);
|
|
1283
|
+
},
|
|
1284
|
+
readString(payload["projectId"]) || null
|
|
1285
|
+
);
|
|
1105
1286
|
try {
|
|
1106
1287
|
await abort(signal);
|
|
1107
1288
|
} finally {
|
|
@@ -1109,63 +1290,16 @@ function activateSessionMap(ctx) {
|
|
|
1109
1290
|
}
|
|
1110
1291
|
}
|
|
1111
1292
|
});
|
|
1112
|
-
ctx.command({
|
|
1113
|
-
name: "detail",
|
|
1114
|
-
description: "Read one session for the detail pane.",
|
|
1115
|
-
scope: "read",
|
|
1116
|
-
payloadSchema: {
|
|
1117
|
-
type: "object",
|
|
1118
|
-
properties: { threadId: { type: "string" } },
|
|
1119
|
-
required: ["threadId"]
|
|
1120
|
-
},
|
|
1121
|
-
handle: async (payload) => readDetail(ctx.threads, readString(payload["threadId"]))
|
|
1122
|
-
});
|
|
1123
1293
|
ctx.command({
|
|
1124
1294
|
name: "preferences",
|
|
1125
1295
|
description: "Read or update canvas preferences.",
|
|
1126
1296
|
scope: "operate",
|
|
1127
1297
|
payloadSchema: { type: "object", properties: { patch: {} } },
|
|
1128
1298
|
handle: async (payload) => {
|
|
1129
|
-
const
|
|
1130
|
-
const next =
|
|
1299
|
+
const show = preferencePatch(payload["patch"]);
|
|
1300
|
+
const next = show === null ? await canvas.read() : await canvas.update((state) => showArchived(state, show));
|
|
1131
1301
|
hub.canvasChanged();
|
|
1132
|
-
return next;
|
|
1133
|
-
}
|
|
1134
|
-
});
|
|
1135
|
-
ctx.command({
|
|
1136
|
-
name: "move",
|
|
1137
|
-
description: "Save a card position.",
|
|
1138
|
-
scope: "operate",
|
|
1139
|
-
payloadSchema: {
|
|
1140
|
-
type: "object",
|
|
1141
|
-
properties: {
|
|
1142
|
-
cardId: { type: "string" },
|
|
1143
|
-
x: { type: "number" },
|
|
1144
|
-
y: { type: "number" }
|
|
1145
|
-
},
|
|
1146
|
-
required: ["cardId", "x", "y"]
|
|
1147
|
-
},
|
|
1148
|
-
handle: async (payload) => {
|
|
1149
|
-
const next = await canvas.update(
|
|
1150
|
-
(state) => rememberPosition(state, {
|
|
1151
|
-
cardId: readString(payload["cardId"]),
|
|
1152
|
-
x: typeof payload["x"] === "number" ? payload["x"] : 0,
|
|
1153
|
-
y: typeof payload["y"] === "number" ? payload["y"] : 0
|
|
1154
|
-
})
|
|
1155
|
-
);
|
|
1156
|
-
hub.canvasChanged();
|
|
1157
|
-
return { positions: next.positions };
|
|
1158
|
-
}
|
|
1159
|
-
});
|
|
1160
|
-
ctx.command({
|
|
1161
|
-
name: "tidy",
|
|
1162
|
-
description: "Drop saved card positions so the map uses the fork layout.",
|
|
1163
|
-
scope: "operate",
|
|
1164
|
-
payloadSchema: { type: "object", properties: {} },
|
|
1165
|
-
handle: async () => {
|
|
1166
|
-
await canvas.update(clearPositions);
|
|
1167
|
-
hub.canvasChanged();
|
|
1168
|
-
return { ok: true };
|
|
1302
|
+
return { showArchived: next.showArchived };
|
|
1169
1303
|
}
|
|
1170
1304
|
});
|
|
1171
1305
|
ctx.command({
|
|
@@ -1199,117 +1333,31 @@ function activateSessionMap(ctx) {
|
|
|
1199
1333
|
return { ok: true };
|
|
1200
1334
|
}
|
|
1201
1335
|
});
|
|
1202
|
-
ctx.command({
|
|
1203
|
-
name: "create",
|
|
1204
|
-
description: "Open a session in a project and send the first message.",
|
|
1205
|
-
scope: "operate",
|
|
1206
|
-
payloadSchema: {
|
|
1207
|
-
type: "object",
|
|
1208
|
-
properties: {
|
|
1209
|
-
projectId: { type: "string" },
|
|
1210
|
-
text: { type: "string" }
|
|
1211
|
-
},
|
|
1212
|
-
required: ["projectId", "text"]
|
|
1213
|
-
},
|
|
1214
|
-
handle: async (payload) => createSession(
|
|
1215
|
-
ctx,
|
|
1216
|
-
readString(payload["projectId"]),
|
|
1217
|
-
readString(payload["text"]),
|
|
1218
|
-
(id) => hub.touch(id)
|
|
1219
|
-
)
|
|
1220
|
-
});
|
|
1221
|
-
ctx.command({
|
|
1222
|
-
name: "send",
|
|
1223
|
-
description: "Send a follow-up into an existing session.",
|
|
1224
|
-
scope: "operate",
|
|
1225
|
-
payloadSchema: {
|
|
1226
|
-
type: "object",
|
|
1227
|
-
properties: {
|
|
1228
|
-
threadId: { type: "string" },
|
|
1229
|
-
text: { type: "string" }
|
|
1230
|
-
},
|
|
1231
|
-
required: ["threadId", "text"]
|
|
1232
|
-
},
|
|
1233
|
-
handle: async (payload) => sendFollowUp(
|
|
1234
|
-
ctx,
|
|
1235
|
-
readString(payload["threadId"]),
|
|
1236
|
-
readString(payload["text"]),
|
|
1237
|
-
(id) => hub.touch(id)
|
|
1238
|
-
)
|
|
1239
|
-
});
|
|
1240
1336
|
ctx.command({
|
|
1241
1337
|
name: "fork",
|
|
1242
|
-
description: "Fork at a user message
|
|
1338
|
+
description: "Fork at a user message. The first message is written in the new session.",
|
|
1243
1339
|
scope: "operate",
|
|
1244
1340
|
payloadSchema: {
|
|
1245
1341
|
type: "object",
|
|
1246
1342
|
properties: {
|
|
1247
1343
|
threadId: { type: "string" },
|
|
1248
|
-
messageId: { type: "string" }
|
|
1249
|
-
text: { type: "string" }
|
|
1344
|
+
messageId: { type: "string" }
|
|
1250
1345
|
},
|
|
1251
|
-
required: ["threadId", "messageId"
|
|
1346
|
+
required: ["threadId", "messageId"]
|
|
1252
1347
|
},
|
|
1253
1348
|
handle: async (payload) => forkSession(
|
|
1254
1349
|
ctx,
|
|
1255
1350
|
readString(payload["threadId"]),
|
|
1256
1351
|
readString(payload["messageId"]),
|
|
1257
|
-
readString(payload["text"]),
|
|
1258
1352
|
(id) => hub.touch(id)
|
|
1259
1353
|
)
|
|
1260
1354
|
});
|
|
1261
1355
|
}
|
|
1262
|
-
async function
|
|
1263
|
-
const trimmed = text2.trim();
|
|
1264
|
-
if (trimmed.length === 0) return { ok: false, code: "empty" };
|
|
1265
|
-
const project = await projectOrNull(ctx.projects, projectId);
|
|
1266
|
-
if (project === null) return { ok: false, code: "missing-project" };
|
|
1267
|
-
const selection = await ctx.providers.selection.resolve({ base: project.defaultModelSelection });
|
|
1268
|
-
const threadId = NodeCrypto.randomUUID();
|
|
1269
|
-
const [create, start] = createCommands({
|
|
1270
|
-
projectId,
|
|
1271
|
-
title: titleFromText(trimmed),
|
|
1272
|
-
text: trimmed,
|
|
1273
|
-
modelSelection: selection.modelSelection,
|
|
1274
|
-
runtimeMode: "full-access",
|
|
1275
|
-
interactionMode: "default",
|
|
1276
|
-
threadId,
|
|
1277
|
-
commandId: NodeCrypto.randomUUID(),
|
|
1278
|
-
messageId: NodeCrypto.randomUUID(),
|
|
1279
|
-
turnCommandId: NodeCrypto.randomUUID()
|
|
1280
|
-
});
|
|
1281
|
-
await ctx.threads.create(create);
|
|
1282
|
-
await ctx.threads.turns.start(start);
|
|
1283
|
-
refresh(threadId);
|
|
1284
|
-
return { ok: true, threadId, walkedBack: false };
|
|
1285
|
-
}
|
|
1286
|
-
async function sendFollowUp(ctx, threadId, text2, refresh) {
|
|
1287
|
-
const trimmed = text2.trim();
|
|
1288
|
-
if (trimmed.length === 0) return { ok: false, code: "empty" };
|
|
1289
|
-
const execution = await executionOf(ctx, threadId);
|
|
1290
|
-
if (execution === null) return { ok: false, code: "missing-thread" };
|
|
1291
|
-
await ctx.threads.turns.start(
|
|
1292
|
-
turnCommand({
|
|
1293
|
-
threadId,
|
|
1294
|
-
text: trimmed,
|
|
1295
|
-
messageId: NodeCrypto.randomUUID(),
|
|
1296
|
-
commandId: NodeCrypto.randomUUID(),
|
|
1297
|
-
runtimeMode: execution.runtimeMode,
|
|
1298
|
-
interactionMode: execution.interactionMode,
|
|
1299
|
-
modelSelection: execution.modelSelection
|
|
1300
|
-
})
|
|
1301
|
-
);
|
|
1302
|
-
refresh(threadId);
|
|
1303
|
-
return { ok: true, threadId, walkedBack: false };
|
|
1304
|
-
}
|
|
1305
|
-
async function forkSession(ctx, threadId, messageId, text2, refresh) {
|
|
1306
|
-
const trimmed = text2.trim();
|
|
1307
|
-
if (trimmed.length === 0) return { ok: false, code: "empty" };
|
|
1356
|
+
async function forkSession(ctx, threadId, messageId, refresh) {
|
|
1308
1357
|
const transcript = await readTranscript(ctx.threads, threadId);
|
|
1309
1358
|
if (transcript === null) return { ok: false, code: "missing-thread" };
|
|
1310
1359
|
const anchor = resolveForkAnchor(transcript, messageId);
|
|
1311
1360
|
if (anchor === null) return { ok: false, code: "no-user-message" };
|
|
1312
|
-
const execution = await executionOf(ctx, threadId);
|
|
1313
1361
|
const childId = NodeCrypto.randomUUID();
|
|
1314
1362
|
await ctx.threads.fork(
|
|
1315
1363
|
forkCommand({
|
|
@@ -1319,33 +1367,13 @@ async function forkSession(ctx, threadId, messageId, text2, refresh) {
|
|
|
1319
1367
|
commandId: NodeCrypto.randomUUID()
|
|
1320
1368
|
})
|
|
1321
1369
|
);
|
|
1322
|
-
await ctx.threads.turns.start(
|
|
1323
|
-
turnCommand({
|
|
1324
|
-
threadId: childId,
|
|
1325
|
-
text: trimmed,
|
|
1326
|
-
messageId: NodeCrypto.randomUUID(),
|
|
1327
|
-
commandId: NodeCrypto.randomUUID(),
|
|
1328
|
-
runtimeMode: execution?.runtimeMode ?? "full-access",
|
|
1329
|
-
interactionMode: execution?.interactionMode ?? "default",
|
|
1330
|
-
modelSelection: execution === null ? null : execution.modelSelection
|
|
1331
|
-
})
|
|
1332
|
-
);
|
|
1333
1370
|
refresh(threadId);
|
|
1334
1371
|
refresh(childId);
|
|
1335
1372
|
return { ok: true, threadId: childId, walkedBack: anchor.walkedBack };
|
|
1336
1373
|
}
|
|
1337
1374
|
function preferencePatch(value) {
|
|
1338
|
-
if (!isRecord(value)) return
|
|
1339
|
-
|
|
1340
|
-
threadId: readString(value.anchor.threadId),
|
|
1341
|
-
cardId: readString(value.anchor.cardId)
|
|
1342
|
-
} : void 0;
|
|
1343
|
-
return {
|
|
1344
|
-
...typeof value.autoProjection === "boolean" ? { autoProjection: value.autoProjection } : {},
|
|
1345
|
-
...typeof value.workspaceTitle === "string" ? { workspaceTitle: value.workspaceTitle } : {},
|
|
1346
|
-
...typeof value.showArchived === "boolean" ? { showArchived: value.showArchived } : {},
|
|
1347
|
-
...anchor === void 0 ? {} : { anchor }
|
|
1348
|
-
};
|
|
1375
|
+
if (!isRecord(value) || typeof value.showArchived !== "boolean") return null;
|
|
1376
|
+
return value.showArchived;
|
|
1349
1377
|
}
|
|
1350
1378
|
function abort(signal) {
|
|
1351
1379
|
return new Promise((resolve) => {
|
|
@@ -1360,7 +1388,7 @@ function abort(signal) {
|
|
|
1360
1388
|
// src/index.ts
|
|
1361
1389
|
var index_default = definePlugin({
|
|
1362
1390
|
id: "conversation",
|
|
1363
|
-
apiVersion:
|
|
1391
|
+
apiVersion: 28,
|
|
1364
1392
|
activate(ctx) {
|
|
1365
1393
|
activateSessionMap(ctx);
|
|
1366
1394
|
}
|