@jskit-ai/assistant-core 0.1.218 → 0.1.220

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.
@@ -8,14 +8,14 @@
8
8
  "build": "vite build"
9
9
  },
10
10
  "dependencies": {
11
- "@jskit-ai/assistant-core": "0.1.218",
12
- "@jskit-ai/http-runtime": "0.1.238",
13
- "@jskit-ai/kernel": "0.1.240",
11
+ "@jskit-ai/assistant-core": "0.1.220",
12
+ "@jskit-ai/http-runtime": "0.1.240",
13
+ "@jskit-ai/kernel": "0.1.242",
14
14
  "@mdi/js": "^7.4.47",
15
15
  "vue": "^3.5.13",
16
16
  "vuetify": "^4.0.0",
17
- "@jskit-ai/connectors-core": "0.1.57",
18
- "@jskit-ai/connectors-catalog": "0.1.57"
17
+ "@jskit-ai/connectors-core": "0.1.59",
18
+ "@jskit-ai/connectors-catalog": "0.1.59"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@vitejs/plugin-vue": "^6.0.7",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/assistant-core",
3
- "version": "0.1.218",
3
+ "version": "0.1.220",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test --test-concurrency=1"
@@ -47,8 +47,8 @@
47
47
  "@ai-sdk/perplexity": "^4.0.43",
48
48
  "@ai-sdk/togetherai": "^3.0.49",
49
49
  "@ai-sdk/xai": "^4.0.58",
50
- "@jskit-ai/resource-core": "0.1.182",
51
- "@jskit-ai/resource-crud-core": "0.1.182",
50
+ "@jskit-ai/resource-core": "0.1.184",
51
+ "@jskit-ai/resource-crud-core": "0.1.184",
52
52
  "@mdi/js": "^7.4.47",
53
53
  "@openrouter/ai-sdk-provider": "^3.0.0",
54
54
  "ai": "^7.0.101",
@@ -57,8 +57,8 @@
57
57
  "ws": "^8.20.1"
58
58
  },
59
59
  "peerDependencies": {
60
- "@jskit-ai/http-runtime": "0.1.238",
61
- "@jskit-ai/kernel": "0.1.240",
60
+ "@jskit-ai/http-runtime": "0.1.240",
61
+ "@jskit-ai/kernel": "0.1.242",
62
62
  "vue": "^3.5.13",
63
63
  "vuetify": "^4.0.0"
64
64
  },
@@ -1634,7 +1634,9 @@ export function createOpenCodeSharedRuntime({ onStop = () => {}, scope } = {}) {
1634
1634
 
1635
1635
  async function interruptTurn(key, target, { threadId = "", writeRun } = {}) {
1636
1636
  const turn = turns.get(key);
1637
- if (!target) {
1637
+ // Helpers can acquire the shared process before a main conversation exists.
1638
+ // Their cleanup is owned by closeSession, not a native main-turn interrupt.
1639
+ if (!target || (!text(target.upstreamSessionId) && !turn?.active)) {
1638
1640
  return {
1639
1641
  interrupted: false,
1640
1642
  ok: true,
@@ -241,7 +241,8 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
241
241
  await emit(entry, { type: "message", turnId: turn.turnId, messageId: message.messageId,
242
242
  ...(message.outputId ? { outputId: message.outputId } : {}),
243
243
  ...(["user", "application"].includes(turn.metadata?.runtime?.origin) ? { origin: turn.metadata.runtime.origin } : {}),
244
- role: message.role, text: message.text, status: "complete", streaming: payload.conversationStream || streams.read(entry.id) });
244
+ role: message.role, text: message.text, at: message.at, status: "complete", persisted: true,
245
+ streaming: payload.conversationStream || streams.read(entry.id) });
245
246
  }
246
247
  }
247
248
  if (payload.conversationStream) {
@@ -847,7 +847,9 @@ async function select(conversation, engine, operationId) {
847
847
  }
848
848
 
849
849
  async function fixture(t, options = {}) {
850
- const directory = await mkdtemp(path.join(tmpdir(), "jskit-codex-conversation-"));
850
+ // Keep enough room for the runtime's nested Unix socket path even when the
851
+ // host supplies a comparatively long temporary-directory prefix.
852
+ const directory = await mkdtemp(path.join(tmpdir(), "jc-"));
851
853
  const command = path.join(directory, "codex.mjs");
852
854
  const account = path.join(directory, "account.txt");
853
855
  const trace = path.join(directory, "trace.jsonl");
@@ -3106,6 +3108,8 @@ test("default Codex commentary publishes its saved output identity rather than a
3106
3108
  assert.equal(completed.every(event => event.messageId === progress[0].messageId), true,
3107
3109
  "The existing canonical publication owns this exact output; its native stream must not complete a second identity");
3108
3110
  assert.equal(completed.every(event => event.outputId === progress[0].outputId && event.text === progress[0].text), true);
3111
+ assert.equal(completed.every(event => event.persisted === true && event.at === progress[0].at), true,
3112
+ "Saved output notifications retain their timestamp and cannot masquerade as transient output outside a loaded history page");
3109
3113
  assert.equal((await f.conversation.read()).streaming.completedMessages, undefined);
3110
3114
  assert.equal((await f.trace()).filter(row => row.method === "turn/start").length, 1);
3111
3115
  });
@@ -4,6 +4,7 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import test from "node:test";
6
6
  import { createOpenCodeSharedRuntime, ensureOpenCodeSession, openCodeServerForDirectory, readOpenCodeEnvironments } from "../src/server/conversation/openCodeRuntime.js";
7
+ import { createOpenCodeServerClient } from "../src/server/conversation/openCodeClient.js";
7
8
 
8
9
  function fixture(options = {}) {
9
10
  const runtime = createOpenCodeSharedRuntime();
@@ -186,6 +187,54 @@ test("OpenCode shares one lazy server across open sessions and stops it after th
186
187
  assert.equal(f.stops.length, 2);
187
188
  });
188
189
 
190
+ test("an acquired OpenCode process without a main conversation can close without interrupting a peer", async () => {
191
+ const interrupted = [];
192
+ const f = fixture({ interrupt: id => { interrupted.push(id); return true; } });
193
+ const target = await f.acquire("empty");
194
+ target.sessionId = "empty";
195
+ const peer = await f.acquire("peer");
196
+ peer.upstreamSessionId = "peer-native";
197
+ const nativeClient = createOpenCodeServerClient({ baseUrl: "http://127.0.0.1:1",
198
+ fetchImpl: async () => assert.fail("No native main conversation exists to interrupt.") });
199
+ target.server = { ...target.server, client: nativeClient };
200
+ const stopped = await f.runtime.interruptPreparedTurn({
201
+ key: "empty", threadId: "", failure: error => { throw error; }
202
+ });
203
+ assert.equal(stopped.ok, true);
204
+ assert.equal(stopped.interrupted, false);
205
+ const calls = [];
206
+ const closed = await f.runtime.closeSession("empty", {}, {
207
+ closeTerminals: async () => { calls.push("terminals"); return { ok: true, closed: 0 }; },
208
+ beforeRelease: async () => { calls.push("helpers"); },
209
+ onRemoved: async () => { calls.push("removed"); },
210
+ afterRelease: async () => { calls.push("history"); },
211
+ failure: error => { throw error; }
212
+ });
213
+ assert.equal(closed.ok, true);
214
+ assert.equal(closed.closed, 1);
215
+ assert.deepEqual(calls, ["terminals", "helpers", "removed", "history"]);
216
+ assert.equal(target.abortController.signal.aborted, true);
217
+ assert.equal(peer.abortController.signal.aborted, false);
218
+ assert.equal(f.stops.length, 0);
219
+ assert.deepEqual(interrupted, []);
220
+ await f.runtime.interruptTurn("peer", peer);
221
+ assert.deepEqual(interrupted, ["peer-native"]);
222
+ await f.runtime.release(peer);
223
+ assert.equal(f.stops.length, 1);
224
+ });
225
+
226
+ test("an active OpenCode turn with missing native identity still fails instead of claiming Stop", async () => {
227
+ const runtime = createOpenCodeSharedRuntime();
228
+ const client = createOpenCodeServerClient({ baseUrl: "http://127.0.0.1:1",
229
+ fetchImpl: async () => assert.fail("Invalid identity must fail before transport.") });
230
+ const turn = { active: true, state: "inProgress" };
231
+ runtime.turns.set("invalid", turn);
232
+ await assert.rejects(runtime.interruptTurn("invalid", { upstreamSessionId: "", server: { client } }),
233
+ /OpenCode session requests require a session id/u);
234
+ assert.equal(turn.active, true);
235
+ assert.equal(turn.interruptRequested, false);
236
+ });
237
+
189
238
  test("a pending OpenCode session start retains the shared server while another session closes", async () => {
190
239
  const f = fixture();
191
240
  const first = await f.acquire("session-1");