@jskit-ai/assistant-runtime 0.1.208 → 0.1.209
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.
|
@@ -155,6 +155,7 @@ export default defineConfig({
|
|
|
155
155
|
return json({ ok: true, delivered: true, messageId: input.messageId }, 202);
|
|
156
156
|
}
|
|
157
157
|
if (mode === "rejected") return json({ error: "The message was rejected before admission.", code: "ACTION_VALIDATION_FAILED" }, 400);
|
|
158
|
+
if (mode === "unadmitted") return json({ error: "Request failed with status 503." }, 503);
|
|
158
159
|
if (mode === "uncertain") {
|
|
159
160
|
state.status = "unconfirmed";
|
|
160
161
|
state.pendingRequest = { messageId: input.messageId, text: input.text, origin: "user",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/assistant-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.209",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test --test-concurrency=1"
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
"./server": "./src/server/index.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@jskit-ai/assistant-core": "0.1.
|
|
15
|
+
"@jskit-ai/assistant-core": "0.1.216",
|
|
16
16
|
"json-rest-schema": "^1.0.17"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
20
|
-
"@jskit-ai/http-web": "0.1.
|
|
21
|
-
"@jskit-ai/kernel": "0.1.
|
|
22
|
-
"@jskit-ai/realtime": "0.1.
|
|
23
|
-
"@jskit-ai/shell-web": "0.1.
|
|
19
|
+
"@jskit-ai/http-runtime": "0.1.236",
|
|
20
|
+
"@jskit-ai/http-web": "0.1.83",
|
|
21
|
+
"@jskit-ai/kernel": "0.1.238",
|
|
22
|
+
"@jskit-ai/realtime": "0.1.235",
|
|
23
|
+
"@jskit-ai/shell-web": "0.1.242",
|
|
24
24
|
"@tanstack/vue-query": "^5.90.5",
|
|
25
25
|
"vue": "^3.5.13",
|
|
26
26
|
"vuetify": "^4.0.0",
|
|
27
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
27
|
+
"@jskit-ai/database-runtime": "0.1.238"
|
|
28
28
|
},
|
|
29
29
|
"description": "Capability-based assistant runtime with per-surface chat, transcripts, settings, and action tools.",
|
|
30
30
|
"jskit": {
|
|
@@ -117,7 +117,8 @@ function createConversation(identity, { api, socket, actorKey, placement, reader
|
|
|
117
117
|
const queueing = computed(() => queueWhileSending !== false && (steerable.value || workingPolicy.value.deferWhileWorking || workingPolicy.value.admitWhileWorking));
|
|
118
118
|
const canSubmit = computed(() => available.value && (snapshot.value.status === "ready" || steerable.value || (workingPolicy.value.deferWhileWorking || workingPolicy.value.admitWhileWorking) && snapshot.value.status === "working") &&
|
|
119
119
|
(queueWhileSending !== false || !delivery.state.sending) &&
|
|
120
|
-
!delivery.state.messages.some(message => message.status === "uncertain"
|
|
120
|
+
!delivery.state.messages.some(message => message.status === "uncertain" &&
|
|
121
|
+
(snapshot.value.status !== "ready" || !message.payload || text(message.payload.draft ?? message.payload.message) === text(draft))));
|
|
121
122
|
const savedDraft = toValue(draftStorage);
|
|
122
123
|
if (savedDraft && (typeof savedDraft.key !== "string" || !savedDraft.key ||
|
|
123
124
|
!["getItem", "setItem", "removeItem"].every(name => typeof savedDraft.storage?.[name] === "function"))) {
|
|
@@ -340,8 +341,6 @@ function createConversation(identity, { api, socket, actorKey, placement, reader
|
|
|
340
341
|
for (const pending of pendingMessages.values()) pending.controller.abort();
|
|
341
342
|
pendingMessages.clear();
|
|
342
343
|
goalReadController?.abort();
|
|
343
|
-
globalThis.removeEventListener?.("focus", refreshVisibleGoal);
|
|
344
|
-
globalThis.document?.removeEventListener("visibilitychange", refreshVisibleGoal);
|
|
345
344
|
delivery.reset();
|
|
346
345
|
});
|
|
347
346
|
|
|
@@ -350,6 +349,8 @@ function createConversation(identity, { api, socket, actorKey, placement, reader
|
|
|
350
349
|
if (retry?.status === "failed" && retry.payload.goalRequest) return changeGoal(retry.payload.goalRequest.action, {}, { messageId });
|
|
351
350
|
if (!canSubmit.value) return false;
|
|
352
351
|
if (retry && retry.status !== "failed") return false;
|
|
352
|
+
if (delivery.state.messages.some(message => message.status === "uncertain" &&
|
|
353
|
+
text(message.payload?.message) === text((retry?.payload || payload).message))) return false;
|
|
353
354
|
const request = retry?.payload?.request || payload.request;
|
|
354
355
|
const requestedSteering = request?.steer === true;
|
|
355
356
|
if (requestedSteering && snapshot.value.capabilities?.steering !== true) return false;
|
|
@@ -572,11 +573,6 @@ function createConversation(identity, { api, socket, actorKey, placement, reader
|
|
|
572
573
|
goalRead = job;
|
|
573
574
|
return job;
|
|
574
575
|
}
|
|
575
|
-
function refreshVisibleGoal() {
|
|
576
|
-
if (!globalThis.document?.hidden) void refreshGoal();
|
|
577
|
-
}
|
|
578
|
-
globalThis.addEventListener?.("focus", refreshVisibleGoal);
|
|
579
|
-
globalThis.document?.addEventListener("visibilitychange", refreshVisibleGoal);
|
|
580
576
|
async function changeGoal(action, input = {}, { messageId = crypto.randomUUID() } = {}) {
|
|
581
577
|
const view = goalView.value;
|
|
582
578
|
const target = view?.target;
|
|
@@ -665,9 +661,16 @@ function createConversation(identity, { api, socket, actorKey, placement, reader
|
|
|
665
661
|
if (!current.value) return false;
|
|
666
662
|
if (receipt.status === "accepted") delivery.accept(messageId);
|
|
667
663
|
else if (receipt.status === "not-sent" && receipt.messageId === messageId) delivery.rejectUnsent(messageId, receipt.error);
|
|
664
|
+
else if (receipt.status === "unknown" && message?.status === "uncertain") {
|
|
665
|
+
message.error = "Delivery is still unknown. You can send a different message when the conversation is ready. Check delivery before resending this one.";
|
|
666
|
+
}
|
|
668
667
|
subscription?.reload();
|
|
669
668
|
return receipt;
|
|
670
|
-
} catch (failure) {
|
|
669
|
+
} catch (failure) {
|
|
670
|
+
if (current.value && message?.status === "uncertain") message.error = failure.message || "Delivery could not be checked.";
|
|
671
|
+
receiveError(failure);
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
671
674
|
finally { if (message) message.checking = false; }
|
|
672
675
|
}
|
|
673
676
|
|
|
@@ -62,7 +62,7 @@ test("inspecting a restored no-admission request releases Send while preserving
|
|
|
62
62
|
await attach();
|
|
63
63
|
await expect(f.input).toHaveValue("A separate newer draft");
|
|
64
64
|
const send = f.primary.getByRole("button", { name: "Send message", exact: true });
|
|
65
|
-
await expect(send).
|
|
65
|
+
await expect(send).toBeEnabled();
|
|
66
66
|
await f.page.evaluate(() => window.conversationFixture.attachments(true));
|
|
67
67
|
await f.primary.locator("input[type=file]").setInputFiles({ name: "newer.txt", mimeType: "text/plain", buffer: Buffer.from("Keep this upload") });
|
|
68
68
|
const uploads = () => f.page.evaluate(() => window.conversationFixture.attachmentState());
|
|
@@ -288,7 +288,9 @@ test("a supplied goal view keeps its pinned target and refreshes through the ret
|
|
|
288
288
|
await expect.poll(() => reads).toBeGreaterThan(beforeReconnect);
|
|
289
289
|
const beforeFocus = reads;
|
|
290
290
|
await f.page.evaluate(() => window.dispatchEvent(new Event("focus")));
|
|
291
|
-
await
|
|
291
|
+
await f.page.evaluate(() => document.dispatchEvent(new Event("visibilitychange")));
|
|
292
|
+
await f.page.waitForTimeout(100);
|
|
293
|
+
assert.equal(reads, beforeFocus, "Focus and visibility do not request goal state");
|
|
292
294
|
await expect.poll(() => f.page.evaluate(() => window.conversationFixture.current().goalState.value.pending)).toBe(false);
|
|
293
295
|
failedRead = true;
|
|
294
296
|
await f.page.evaluate(() => window.conversationFixture.current().refreshGoal());
|
|
@@ -2892,3 +2894,45 @@ test("changing the observed native segment blocks cached goal controls until the
|
|
|
2892
2894
|
releaseRead?.();
|
|
2893
2895
|
}
|
|
2894
2896
|
});
|
|
2897
|
+
|
|
2898
|
+
|
|
2899
|
+
test("unknown delivery reports its current result and allows different idle input without replaying the old message", options, async t => {
|
|
2900
|
+
const f = await fixture(t, 390);
|
|
2901
|
+
await f.command("mode", { mode: "unadmitted" });
|
|
2902
|
+
await f.input.fill("Original uncertain request");
|
|
2903
|
+
await f.input.press("Enter");
|
|
2904
|
+
const check = f.primary.getByRole("button", { name: "Check delivery", exact: true });
|
|
2905
|
+
const send = f.primary.getByRole("button", { name: "Send message", exact: true });
|
|
2906
|
+
await expect(check).toBeVisible();
|
|
2907
|
+
const bubble = f.primary.locator(".assistant-transcript__message--user").filter({ hasText: "Original uncertain request" });
|
|
2908
|
+
await expect(bubble.getByText("Delivery unconfirmed", { exact: true })).toBeVisible();
|
|
2909
|
+
await expect(bubble.getByRole("button", { name: "Check delivery", exact: true })).toHaveCount(1);
|
|
2910
|
+
await expect(bubble).toHaveClass(/assistant-transcript__message--unconfirmed/u);
|
|
2911
|
+
await expect(f.primary.locator(".assistant-transcript__system")).toHaveCount(0);
|
|
2912
|
+
await expect(bubble.getByText("Request failed with status 503.", { exact: true })).toBeVisible();
|
|
2913
|
+
await f.input.fill("Original uncertain request");
|
|
2914
|
+
await expect(send).toBeDisabled();
|
|
2915
|
+
await f.input.fill("A different message");
|
|
2916
|
+
await expect(send).toBeEnabled();
|
|
2917
|
+
await check.click();
|
|
2918
|
+
await expect(f.primary.getByText("Delivery is still unknown.", { exact: false })).toBeVisible();
|
|
2919
|
+
await expect(f.input).toHaveValue("A different message");
|
|
2920
|
+
await expect(bubble.getByText("Request failed with status 503.", { exact: true })).toHaveCount(0);
|
|
2921
|
+
const screenshots = process.env.JSKIT_ASSISTANT_SCREENSHOTS;
|
|
2922
|
+
for (const width of [390, 1280]) {
|
|
2923
|
+
await f.page.setViewportSize({ width, height: 900 });
|
|
2924
|
+
await expect(bubble.getByRole("button", { name: "Check delivery", exact: true })).toBeVisible();
|
|
2925
|
+
assert.ok(await f.page.evaluate(() => document.documentElement.scrollWidth <= innerWidth));
|
|
2926
|
+
if (screenshots) await f.page.screenshot({ path: `${screenshots}/delivery-attached-${width}.png` });
|
|
2927
|
+
}
|
|
2928
|
+
assert.equal((await f.sent()).length, 1);
|
|
2929
|
+
await f.command("mode", { mode: "product-accepted" });
|
|
2930
|
+
await send.click();
|
|
2931
|
+
await expect.poll(async () => (await f.sent()).length).toBe(2);
|
|
2932
|
+
const [old, next] = await f.sent();
|
|
2933
|
+
assert.notEqual(old.input.messageId, next.input.messageId);
|
|
2934
|
+
assert.equal(next.input.text, "A different message");
|
|
2935
|
+
await expect(check).toBeVisible();
|
|
2936
|
+
assert.equal(await f.page.evaluate(id => window.conversationFixture.current().delivery.find(id).status,
|
|
2937
|
+
old.input.messageId), "uncertain");
|
|
2938
|
+
});
|