@jskit-ai/assistant-core 0.1.213 → 0.1.214
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/examples/conversation/package.json +5 -5
- package/fixtures/responsive-assistant/ConversationFixture.vue +7 -1
- package/package.json +5 -5
- package/src/client/conversation/AssistantConversationElement.vue +5 -1
- package/src/client/conversation/AssistantTranscript.vue +10 -1
- package/src/server/conversation/agentRun.js +38 -11
- package/src/server/conversation/claudeHistory.js +40 -5
- package/src/server/conversation/claudeProcess.js +16 -2
- package/src/server/conversation/claudeTurn.js +28 -8
- package/src/server/conversation/codexConfiguration.js +1 -1
- package/src/server/conversation/codexHistoryAdapter.js +11 -4
- package/src/server/conversation/codexNotificationQueue.js +11 -2
- package/src/server/conversation/codexObservationControl.js +8 -8
- package/src/server/conversation/codexOutput.js +20 -15
- package/src/server/conversation/codexProvider.js +40 -3
- package/src/server/conversation/codexProviderOwner.js +110 -1
- package/src/server/conversation/codexTurn.js +3 -0
- package/src/server/conversation/continuity.js +24 -6
- package/src/server/conversation/index.js +1 -1
- package/src/server/conversation/nativeHistoryExport.js +97 -0
- package/src/server/conversation/openCodeClient.js +47 -1
- package/src/server/conversation/openCodeRuntime.js +132 -24
- package/src/server/conversation/openCodeTurn.js +65 -24
- package/src/server/conversation/providers/api.js +7 -0
- package/src/server/conversation/providers/claudeDriver.js +52 -7
- package/src/server/conversation/providers/codex.js +19 -2
- package/src/server/conversation/providers/codexDriver.js +92 -14
- package/src/server/conversation/providers/opencodeDriver.js +31 -9
- package/src/server/conversation/runtime.js +418 -64
- package/src/server/conversation/runtimeStateUpgrade.js +77 -2
- package/src/server/conversation/streams.js +25 -5
- package/src/server/conversation/tools.js +2 -2
- package/src/server/conversation/transcript.js +23 -8
- package/src/server/hosts/claudeHistory.js +1 -0
- package/src/server/hosts/claudeProcess.js +1 -0
- package/src/server/hosts/codexProvider.js +3 -0
- package/src/server/hosts/nativeHistory.js +1 -0
- package/src/server/hosts/openCodeClient.js +1 -0
- package/src/server/hosts/openCodeTurn.js +1 -0
- package/src/server/index.js +1 -1
- package/src/server/lib/assistantToolLoop.js +99 -2
- package/test/aiConnectionClient.test.js +57 -0
- package/test/assistantBoundedToolLoop.test.js +332 -0
- package/test/assistantScroll.browser.test.js +151 -0
- package/test/claudeConversationSession.test.js +1329 -3
- package/test/claudeProcess.test.js +101 -0
- package/test/claudeTurn.test.js +31 -0
- package/test/codexConversationSession.test.js +1257 -12
- package/test/codexHistoryAdapter.test.js +74 -1
- package/test/codexTurn.test.js +282 -1
- package/test/conversation.test.js +83 -0
- package/test/conversationAgentRun.test.js +181 -0
- package/test/conversationRuntime.test.js +95 -0
- package/test/conversationRuntimeStateUpgrade.test.js +89 -0
- package/test/conversationStreaming.test.js +85 -0
- package/test/conversationTools.test.js +112 -1
- package/test/openCodeClient.test.js +186 -0
- package/test/openCodeRuntime.test.js +78 -0
- package/test/opencodeConversationSession.test.js +637 -5
|
@@ -276,3 +276,104 @@ test("Claude handshake failure retains acquired execution custody when cleanup i
|
|
|
276
276
|
});
|
|
277
277
|
}
|
|
278
278
|
});
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
test("Claude initialization rejects confirmed execution exit before half-open protocol EOF", async () => {
|
|
282
|
+
const { PassThrough, Writable } = await import("node:stream");
|
|
283
|
+
for (const cleanup of ["verified", "unconfirmed", "rejected"]) {
|
|
284
|
+
const requested = Promise.withResolvers();
|
|
285
|
+
const exited = Promise.withResolvers();
|
|
286
|
+
const stdout = new PassThrough();
|
|
287
|
+
const stdin = new Writable({ write(chunk, _encoding, done) {
|
|
288
|
+
requested.resolve(JSON.parse(chunk.toString()));
|
|
289
|
+
done();
|
|
290
|
+
} });
|
|
291
|
+
const cleanupError = new Error("Owned cleanup refused");
|
|
292
|
+
const stops = [];
|
|
293
|
+
const execution = {
|
|
294
|
+
async start() { return { id: "half-open-startup", stdin, stdout, exited: exited.promise }; },
|
|
295
|
+
async stop(id) {
|
|
296
|
+
stops.push(id);
|
|
297
|
+
if (cleanup === "rejected") throw cleanupError;
|
|
298
|
+
return { scopeEmpty: cleanup === "verified" };
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
const pending = createClaudeCodeProcess({ execution });
|
|
302
|
+
let failure;
|
|
303
|
+
void pending.catch(error => { failure = error; });
|
|
304
|
+
try {
|
|
305
|
+
assert.equal((await requested.promise).request.subtype, "initialize");
|
|
306
|
+
assert.equal(stdout.readableEnded, false);
|
|
307
|
+
exited.resolve({ code: 1, signal: null });
|
|
308
|
+
// The supplied process receipt must settle startup without a protocol EOF
|
|
309
|
+
// or advancement of the original 30-second control deadline.
|
|
310
|
+
for (let index = 0; index < 5; index += 1) await new Promise(resolve => setImmediate(resolve));
|
|
311
|
+
assert.ok(failure, "Confirmed native exit settles initialization promptly");
|
|
312
|
+
assert.equal(failure.code, "assistant_claude_startup_exited");
|
|
313
|
+
assert.deepEqual(failure.processExit, { code: 1, signal: null });
|
|
314
|
+
assert.equal(failure.executionId, "half-open-startup");
|
|
315
|
+
assert.deepEqual(stops, ["half-open-startup"]);
|
|
316
|
+
assert.equal(failure.stopProof.scopeEmpty, cleanup === "verified");
|
|
317
|
+
assert.equal(failure.cleanupFailed === true, cleanup !== "verified");
|
|
318
|
+
if (cleanup === "rejected") assert.equal(failure.cleanupError, cleanupError);
|
|
319
|
+
} finally {
|
|
320
|
+
stdout.destroy();
|
|
321
|
+
stdin.destroy();
|
|
322
|
+
await pending.catch(() => {});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("Claude initialization preserves rejected execution observation and exact cleanup custody", async () => {
|
|
328
|
+
const { PassThrough, Writable } = await import("node:stream");
|
|
329
|
+
const requested = Promise.withResolvers();
|
|
330
|
+
const exited = Promise.withResolvers();
|
|
331
|
+
const stdout = new PassThrough();
|
|
332
|
+
const stdin = new Writable({ write(_chunk, _encoding, done) { requested.resolve(); done(); } });
|
|
333
|
+
const failure = new Error("Execution observation failed");
|
|
334
|
+
const stops = [];
|
|
335
|
+
const pending = createClaudeCodeProcess({ execution: {
|
|
336
|
+
async start() { return { id: "observed-startup", stdin, stdout, exited: exited.promise }; },
|
|
337
|
+
async stop(id) { stops.push(id); return { scopeEmpty: true }; }
|
|
338
|
+
} });
|
|
339
|
+
const rejected = assert.rejects(pending, error => {
|
|
340
|
+
assert.equal(error, failure);
|
|
341
|
+
assert.equal(error.executionId, "observed-startup");
|
|
342
|
+
assert.deepEqual(error.stopProof, { scopeEmpty: true, exited: true });
|
|
343
|
+
assert.equal(error.cleanupFailed, undefined);
|
|
344
|
+
return true;
|
|
345
|
+
});
|
|
346
|
+
try {
|
|
347
|
+
await requested.promise;
|
|
348
|
+
exited.reject(failure);
|
|
349
|
+
await rejected;
|
|
350
|
+
assert.deepEqual(stops, ["observed-startup"]);
|
|
351
|
+
} finally {
|
|
352
|
+
stdout.destroy();
|
|
353
|
+
stdin.destroy();
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
test("Claude supplied execution hosts retain initialization without an optional exit receipt", { skip: process.platform === "win32" }, async t => {
|
|
359
|
+
const setup = await fixture(t, `
|
|
360
|
+
import { createInterface } from "node:readline";
|
|
361
|
+
createInterface({ input: process.stdin }).on("line", line => {
|
|
362
|
+
const frame = JSON.parse(line);
|
|
363
|
+
process.stdout.write(JSON.stringify({ type: "control_response", response: {
|
|
364
|
+
subtype: "success", request_id: frame.request_id, response: { originalHost: true }
|
|
365
|
+
} }) + "\\n");
|
|
366
|
+
});
|
|
367
|
+
`);
|
|
368
|
+
const local = createLocalConversationExecution();
|
|
369
|
+
t.after(() => local.close());
|
|
370
|
+
const native = await createClaudeCodeProcess({ ...setup, execution: {
|
|
371
|
+
async start(input) {
|
|
372
|
+
const { id, stdin, stdout } = await local.start(input);
|
|
373
|
+
return { id, stdin, stdout };
|
|
374
|
+
},
|
|
375
|
+
stop: id => local.stop(id)
|
|
376
|
+
} });
|
|
377
|
+
assert.equal(native.initialization.originalHost, true);
|
|
378
|
+
assert.deepEqual(await native.stop(), { scopeEmpty: true, exited: true });
|
|
379
|
+
});
|
package/test/claudeTurn.test.js
CHANGED
|
@@ -704,3 +704,34 @@ test("Claude admitted old executor failure cannot stop its accepted steering suc
|
|
|
704
704
|
assert.equal(stops, 0);
|
|
705
705
|
assert.equal(f.entry.process, native);
|
|
706
706
|
});
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
test("Claude verified native cleanup retains interrupted checkpoint refusal without leaving saved work active", async t => {
|
|
710
|
+
const f = await publishedClaudeFixture(t);
|
|
711
|
+
await f.send("checkpoint-refused-stop");
|
|
712
|
+
const native = f.entry.process;
|
|
713
|
+
const refusal = new Error("Saved lesson checkpoint refused");
|
|
714
|
+
let stopped = false;
|
|
715
|
+
native.stop = async () => { stopped = true; return { exited: true, scopeEmpty: true }; };
|
|
716
|
+
f.setBeforeState((_entry, event) => {
|
|
717
|
+
if (event.state === "interrupted") {
|
|
718
|
+
assert.equal(stopped, true, "Original native cleanup precedes the host checkpoint");
|
|
719
|
+
throw refusal;
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
const completion = f.entry.completion.promise;
|
|
723
|
+
await assert.rejects(f.entry.nativeTurn.stopProcess("Broken stream"), error => error === refusal);
|
|
724
|
+
assert.equal(f.entry.process, null);
|
|
725
|
+
assert.equal(f.entry.executionId, "");
|
|
726
|
+
assert.deepEqual(f.entry.exitProof, { exited: true, scopeEmpty: true });
|
|
727
|
+
assert.equal(f.entry.turn.active, false);
|
|
728
|
+
assert.equal(f.entry.turn.state, "interrupted");
|
|
729
|
+
assert.equal(f.entry.turn.error, refusal.message);
|
|
730
|
+
const settled = await completion;
|
|
731
|
+
assert.equal(settled.status, "interrupted");
|
|
732
|
+
assert.equal(settled.error, refusal.message);
|
|
733
|
+
assert.equal(f.final(), null, "Refused checkpoint supplies no completed answer receipt");
|
|
734
|
+
await f.reopen();
|
|
735
|
+
assert.equal(f.entry.turn.active, false, "Original supplied store retained the truthful interruption");
|
|
736
|
+
assert.equal(f.entry.turn.state, "interrupted");
|
|
737
|
+
});
|