-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchild-session-forwarding-resolver.ts
More file actions
121 lines (106 loc) · 3.72 KB
/
child-session-forwarding-resolver.ts
File metadata and controls
121 lines (106 loc) · 3.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import type { PendingForwardRequest } from "./session-registry";
export type SessionMessage = {
info: { role: string; id: string };
parts: Array<{ type: string; text: string | null; ignored: boolean | null }>;
};
export type ForwardableAssistantMessage = {
assistantMessageID: string;
text: string;
};
export class ChildSessionForwardingResolver {
public normalizeMessages(raw: Array<any>): Array<SessionMessage> {
return raw
.map((msg) => {
const info = msg?.info ?? {};
const parts = Array.isArray(msg?.parts) ? msg.parts : [];
return {
info: {
role: typeof info?.role === "string" ? info.role : "",
id: typeof info?.id === "string" ? info.id : "",
},
parts: parts
.filter((p: any) => p && typeof p.type === "string")
.map((p: any) => ({
type: String(p.type),
text: typeof p.text === "string" ? p.text : null,
ignored: typeof p.ignored === "boolean" ? p.ignored : null,
})),
};
})
.filter((m) => m.info.id.length > 0);
}
public findForwardableAssistantMessage(
messages: Array<SessionMessage>,
request: PendingForwardRequest
): ForwardableAssistantMessage | null {
const startIndex = this.resolveStartIndex(messages, request);
const token = request.forwardToken;
let found: ForwardableAssistantMessage | null = null;
for (let i = startIndex; i < messages.length; i += 1) {
const msg = messages[i];
if (!msg) continue;
if (msg.info.role !== "assistant") continue;
const rawText = this.extractTextFromParts(msg.parts);
if (!rawText.trim().length) continue;
if (!this.containsToken(rawText, token)) continue;
const cleaned = this.stripToken(rawText, token).trim();
if (!cleaned.length) continue;
found = { assistantMessageID: msg.info.id, text: cleaned };
}
return found;
}
public createTriggerMarker(messages: Array<SessionMessage>): {
afterMessageCount: number;
afterAssistantMessageID: string | null;
} {
const afterMessageCount = messages.length;
const afterAssistantMessageID = this.findLatestAssistantMessageID(messages);
return { afterMessageCount, afterAssistantMessageID };
}
private resolveStartIndex(
messages: Array<SessionMessage>,
request: PendingForwardRequest
): number {
const count = request.afterMessageCount;
if (count !== null && count >= 0) {
if (count <= messages.length) return count;
return 0;
}
const id = request.afterAssistantMessageID;
if (id !== null && id.length) {
const idx = messages.findIndex((m) => m?.info?.id === id);
if (idx >= 0) return idx + 1;
}
return 0;
}
private findLatestAssistantMessageID(messages: Array<SessionMessage>): string | null {
for (let i = messages.length - 1; i >= 0; i -= 1) {
const msg = messages[i];
if (!msg) continue;
if (msg.info.role !== "assistant") continue;
return msg.info.id;
}
return null;
}
private extractTextFromParts(
parts: Array<{ type: string; text: string | null; ignored: boolean | null }>
): string {
return parts
.filter((part) => part.type === "text" && part.ignored !== true)
.map((part) => part.text ?? "")
.join("\n");
}
private containsToken(text: string, token: string): boolean {
return text.includes(this.buildTokenLine(token));
}
private stripToken(text: string, token: string): string {
const line = this.buildTokenLine(token);
return text
.split("\n")
.filter((l) => l.trim() !== line)
.join("\n");
}
private buildTokenLine(token: string): string {
return `opencode_cc_forward_token: ${token}`;
}
}