@kuralle-syrinx/kuralle 3.1.0 → 4.1.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/package.json +2 -2
- package/src/from-kuralle.test.ts +53 -0
- package/src/from-kuralle.ts +17 -1
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kuralle-syrinx/kuralle",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./src/index.ts",
|
|
8
8
|
"types": "./src/index.ts",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@kuralle-syrinx/core": "
|
|
10
|
+
"@kuralle-syrinx/core": "4.1.0"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"@kuralle-agents/core": ">=0.8.0"
|
package/src/from-kuralle.test.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
reconcileSpokenPrefix,
|
|
9
9
|
rewriteLastAssistant,
|
|
10
10
|
type KuralleMessageLike,
|
|
11
|
+
type KuralleRunOptions,
|
|
11
12
|
type KuralleRuntimeLike,
|
|
12
13
|
type KuralleSessionStoreLike,
|
|
13
14
|
type KuralleStoredSession,
|
|
@@ -74,6 +75,58 @@ async function collectParts(reasoner: Reasoner, turn: ReasonerTurn): Promise<Rea
|
|
|
74
75
|
return collected;
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
describe("fromKuralleRuntime G4 resume-by-seed (historyDelta)", () => {
|
|
79
|
+
const turnWithContext = (): ReasonerTurn => ({
|
|
80
|
+
userText: "Second question",
|
|
81
|
+
messages: [
|
|
82
|
+
{ role: "system", content: "sys" },
|
|
83
|
+
{ role: "user", content: "First question" },
|
|
84
|
+
{ role: "assistant", content: "First answer" },
|
|
85
|
+
],
|
|
86
|
+
signal: new AbortController().signal,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("seeds prior turn.messages into an EMPTY kuralle session (fresh isolate resume)", async () => {
|
|
90
|
+
const spy: { runOpts?: KuralleRunOptions } = {};
|
|
91
|
+
const runtime: KuralleRuntimeLike = {
|
|
92
|
+
run(opts) {
|
|
93
|
+
spy.runOpts = opts;
|
|
94
|
+
return { events: partsToEvents([textDelta("ok"), done()]) };
|
|
95
|
+
},
|
|
96
|
+
getSession: async () => null,
|
|
97
|
+
};
|
|
98
|
+
const reasoner = fromKuralleRuntime(runtime, { sessionId: "sess-1" });
|
|
99
|
+
|
|
100
|
+
await collectParts(reasoner, turnWithContext());
|
|
101
|
+
|
|
102
|
+
expect(spy.runOpts?.input).toBe("Second question");
|
|
103
|
+
expect(spy.runOpts?.historyDelta).toEqual([
|
|
104
|
+
{ role: "user", content: "First question" },
|
|
105
|
+
{ role: "assistant", content: "First answer" },
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("does NOT seed into a non-empty session (no double-applied history, R6)", async () => {
|
|
110
|
+
const spy: { runOpts?: KuralleRunOptions } = {};
|
|
111
|
+
const runtime: KuralleRuntimeLike = {
|
|
112
|
+
run(opts) {
|
|
113
|
+
spy.runOpts = opts;
|
|
114
|
+
return { events: partsToEvents([textDelta("ok"), done()]) };
|
|
115
|
+
},
|
|
116
|
+
getSession: async () => ({
|
|
117
|
+
id: "sess-1",
|
|
118
|
+
messages: [{ role: "user", content: "already there" }],
|
|
119
|
+
}),
|
|
120
|
+
};
|
|
121
|
+
const reasoner = fromKuralleRuntime(runtime, { sessionId: "sess-1" });
|
|
122
|
+
|
|
123
|
+
await collectParts(reasoner, turnWithContext());
|
|
124
|
+
|
|
125
|
+
expect(spy.runOpts?.input).toBe("Second question");
|
|
126
|
+
expect(spy.runOpts?.historyDelta).toBeUndefined();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
77
130
|
describe("fromKuralleRuntime", () => {
|
|
78
131
|
it("maps happy path: text deltas and done to finish:stop", async () => {
|
|
79
132
|
const reasoner = fromKuralleRuntime(
|
package/src/from-kuralle.ts
CHANGED
|
@@ -87,7 +87,23 @@ async function buildKuralleRunOptions(
|
|
|
87
87
|
const appended = await appendFlowResumeUserMessage(runtime, opts.sessionId, turn.userText);
|
|
88
88
|
if (appended) return base;
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
// G4 resume-by-seed: the BRIDGE owns history (reasoner seam §4.5). When the kuralle
|
|
91
|
+
// session is empty (fresh isolate / post-eviction) and the turn carries prior context,
|
|
92
|
+
// seed it once via historyDelta — kuralle appends it to the (empty) session, so the
|
|
93
|
+
// runtime resumes with the same context instead of restarting amnesiac. Never seeded
|
|
94
|
+
// into a non-empty session (that would double-apply history).
|
|
95
|
+
return { ...base, input: turn.userText, ...seedHistoryDelta(session, turn.messages) };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function seedHistoryDelta(
|
|
99
|
+
session: KuralleStoredSession | null,
|
|
100
|
+
messages: ReasonerTurn["messages"],
|
|
101
|
+
): Pick<KuralleRunOptions, "historyDelta"> | Record<string, never> {
|
|
102
|
+
if (session && session.messages.length > 0) return {};
|
|
103
|
+
const delta = messages
|
|
104
|
+
.filter((message) => message.role === "user" || message.role === "assistant")
|
|
105
|
+
.map((message) => ({ role: message.role, content: message.content }));
|
|
106
|
+
return delta.length > 0 ? { historyDelta: delta } : {};
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
export async function buildKuralleTurnRunOptions(
|