@jskit-ai/assistant-core 0.2.16 → 0.2.18
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 +1 -0
- package/examples/conversation/package.json +5 -5
- package/package.json +6 -6
- package/src/server/conversation/claudeNativeTurn.js +9 -1
- package/src/server/conversation/claudeSessionCleanup.js +9 -1
- package/src/server/conversation/claudeTurn.js +1 -0
- package/src/server/conversation/codexConversations.js +65 -6
- package/src/server/conversation/codexRunJournal.js +33 -1
- package/src/server/conversation/codexRuntimeLifecycle.js +20 -1
- package/src/server/conversation/codexTurn.js +41 -4
- package/src/server/conversation/codexTurnEndings.js +9 -6
- package/src/server/conversation/nativeConversationLifecycle.js +22 -0
- package/src/server/conversation/openCodeProcessOwner.js +11 -5
- package/src/server/conversation/openCodeRuntime.js +1 -0
- package/src/server/conversation/openCodeTurn.js +10 -5
- package/src/server/conversation/openCodeTurnMonitor.js +4 -2
- package/src/server/conversation/providers/claudeDriver.js +5 -0
- package/src/server/conversation/providers/codexDriver.js +5 -0
- package/src/server/conversation/providers/opencodeDriver.js +5 -0
- package/src/server/conversation/runtime.js +1 -0
- package/test/apiConversationDriver.test.js +8 -4
- package/test/claudeLifecycle.test.js +28 -0
- package/test/codexConversationsCheckpoint.test.js +2 -0
- package/test/codexLifecycleIsolation.test.js +2 -0
- package/test/codexShutdownStop.test.js +168 -0
- package/test/codexTemporaryProviderRelease.test.js +2 -0
- package/test/codexTurn.test.js +57 -0
- package/test/conversationApiFacilities.test.js +4 -3
- package/test/conversationAttachments.test.js +4 -4
- package/test/conversationReplacement.test.js +1 -1
- package/test/conversationSession.test.js +40 -10
- package/test/conversationTools.test.js +2 -2
- package/test/openCodeLifecycle.test.js +55 -3
|
@@ -87,7 +87,7 @@ test("split internal tags and tool arguments never become common answer deltas",
|
|
|
87
87
|
await f.conversation.send(message);
|
|
88
88
|
const result = await f.conversation.wait();
|
|
89
89
|
assert.deepEqual(f.observed.executions, [{ left: 2, right: 3 }]);
|
|
90
|
-
assert.equal(f.observed.requests[1].messages.at(-
|
|
90
|
+
assert.equal(f.observed.requests[1].messages.at(-2).content, checkTexts("the app").markup, "The check comes last, before the engine's state note");
|
|
91
91
|
assert.deepEqual(f.observed.events.filter(event => event.type === "message" && event.status !== "complete")
|
|
92
92
|
.map(event => event.text), ["The search is complete."]);
|
|
93
93
|
assert.equal(result.conversationLog[0].assistant.text, "The search is complete.");
|
|
@@ -101,7 +101,7 @@ test("text-encoded calls never run or reach the person, even when written again
|
|
|
101
101
|
const result = await f.conversation.wait();
|
|
102
102
|
assert.deepEqual(f.observed.executions, []);
|
|
103
103
|
assert.equal(f.observed.requests.length, 2, "The check sends the reply back once");
|
|
104
|
-
assert.equal(f.observed.requests[1].messages.at(-
|
|
104
|
+
assert.equal(f.observed.requests[1].messages.at(-2).content, checkTexts("the app").markup, "The check comes last, before the engine's state note");
|
|
105
105
|
assert.deepEqual(result.conversationLog[0].metadata.applicationTools, []);
|
|
106
106
|
assert.equal(f.observed.events.some(event => event.type === "message" && /invoke|function_calls/u.test(event.text)), false);
|
|
107
107
|
});
|
|
@@ -13,8 +13,8 @@ const ABORTED = "The operation was aborted.";
|
|
|
13
13
|
const PROVIDER_FAILURE = "The provider refused this request.";
|
|
14
14
|
|
|
15
15
|
// OpenCode's native history outlives each server that runs it. The turn's reply
|
|
16
|
-
// is still being written until an interrupt ends it as aborted,
|
|
17
|
-
// fails it.
|
|
16
|
+
// is still being written until an interrupt ends it as aborted, its provider
|
|
17
|
+
// fails it, or it completes on its own.
|
|
18
18
|
function nativeOpenCode() {
|
|
19
19
|
const rows = [
|
|
20
20
|
{ id: INPUT_ID, type: "user", time: { created: 1 } },
|
|
@@ -46,7 +46,11 @@ function nativeOpenCode() {
|
|
|
46
46
|
rows[1] = { ...rows[1], error: { name: "APIError", data: { message: PROVIDER_FAILURE } }, time: { created: 2, completed: 3 } };
|
|
47
47
|
busy = false;
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
function complete() {
|
|
50
|
+
rows[1] = { ...rows[1], text: "Done.", finish: "stop", time: { created: 2, completed: 3 } };
|
|
51
|
+
busy = false;
|
|
52
|
+
}
|
|
53
|
+
return { calls, create, fail, complete };
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
// One server with Vibe64's run rules: the session record keeps the OpenCode run
|
|
@@ -108,6 +112,8 @@ function server(native, saved, { turnEndCheckpoint = async () => {} } = {}) {
|
|
|
108
112
|
return {
|
|
109
113
|
runtime, calls, release,
|
|
110
114
|
shutdown: () => runtime.invalidateRuntimes({ reason: "server-shutdown" }, { release }),
|
|
115
|
+
// The server's shutdown begins: nothing stops yet.
|
|
116
|
+
begin: () => runtime.beginShutdown(),
|
|
111
117
|
async running() {
|
|
112
118
|
const target = await runtime.acquirePrepared(processPreparation);
|
|
113
119
|
void runtime.beginMessageMonitor(KEY, target, { id: INPUT_ID, eventStartedAt: 1, startedAt: "2026-11-05T00:00:00.000Z" }, {}, monitor);
|
|
@@ -284,3 +290,49 @@ test("OpenCode shutdown ends a temporary conversation's turn as interrupted, and
|
|
|
284
290
|
await closed.entry.completion.catch(() => null);
|
|
285
291
|
assert.deepEqual(closed.closes, [{ stopped: true, outcome: "failed", stoppedByShutdown: false }]);
|
|
286
292
|
});
|
|
293
|
+
|
|
294
|
+
test("once OpenCode's shutdown has begun, a main turn that ends on its own is left to the next server, which records it with one checkpoint", { timeout: 5_000 }, async t => {
|
|
295
|
+
const native = nativeOpenCode();
|
|
296
|
+
const saved = { run: null };
|
|
297
|
+
const first = server(native, saved);
|
|
298
|
+
t.after(() => first.shutdown());
|
|
299
|
+
await first.running();
|
|
300
|
+
first.begin();
|
|
301
|
+
assert.deepEqual({ interrupts: native.calls.interrupts, stops: native.calls.stops }, { interrupts: [], stops: 0 },
|
|
302
|
+
"beginning the shutdown stopped nothing");
|
|
303
|
+
native.complete();
|
|
304
|
+
await first.runtime.monitors.get(KEY);
|
|
305
|
+
assert.deepEqual(first.calls.checkpoints, [], "the turn's end made no checkpoint");
|
|
306
|
+
assert.deepEqual(runOf(saved), { active: true, state: "active", error: "", stoppedByShutdown: true, turnId: INPUT_ID });
|
|
307
|
+
assert.equal((await first.shutdown()).ok, true);
|
|
308
|
+
assert.equal(native.calls.stops, 1, "the full stop still proved the OpenCode process exit");
|
|
309
|
+
assert.deepEqual(first.calls.checkpoints, []);
|
|
310
|
+
|
|
311
|
+
const next = server(native, saved);
|
|
312
|
+
t.after(() => next.shutdown());
|
|
313
|
+
assert.equal((await next.ready()).ok, true);
|
|
314
|
+
assert.equal((await next.ready()).ok, true);
|
|
315
|
+
assert.deepEqual(next.calls.checkpoints, [`${INPUT_ID}:interrupted`], "the next server made the checkpoint once");
|
|
316
|
+
assert.deepEqual(runOf(saved), { active: false, state: "interrupted", error: "", stoppedByShutdown: true, turnId: INPUT_ID });
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("once OpenCode's shutdown has begun, a temporary conversation's turn that ends on its own leaves its checkpoint to the next server", { timeout: 5_000 }, async t => {
|
|
320
|
+
const native = nativeOpenCode();
|
|
321
|
+
const first = server(native, { run: null });
|
|
322
|
+
t.after(() => first.shutdown());
|
|
323
|
+
const { entry, closes } = await first.temporary("ses_temp");
|
|
324
|
+
first.begin();
|
|
325
|
+
native.complete();
|
|
326
|
+
assert.deepEqual(await entry.completion, { interrupted: true });
|
|
327
|
+
assert.deepEqual(native.calls.interrupts, [], "nothing interrupted the native turn");
|
|
328
|
+
assert.deepEqual(closes, [{ stopped: true, outcome: "interrupted", stoppedByShutdown: true }],
|
|
329
|
+
"the application is told to leave the turn's checkpoint to the next server");
|
|
330
|
+
assert.equal((await first.shutdown()).ok, true);
|
|
331
|
+
|
|
332
|
+
const next = server(native, { run: null });
|
|
333
|
+
t.after(() => next.shutdown());
|
|
334
|
+
const ended = [];
|
|
335
|
+
const read = await next.readTemporary("ses_temp", { runId: INPUT_ID, async ended() { ended.push(INPUT_ID); } });
|
|
336
|
+
assert.deepEqual({ status: read.status, error: read.error }, { status: "interrupted", error: "" });
|
|
337
|
+
assert.deepEqual(ended, [INPUT_ID], "recording the run lets the application make its checkpoint");
|
|
338
|
+
});
|