@jskit-ai/assistant-runtime 0.2.23 → 0.2.24

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 CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - The assistant surface pattern's icon is `$smartToy`, and the fixtures render with `@jskit-ai/material-web`.
5
6
  - Changing an assistant's instructions needs `assistant.settings.update` on a settings surface that requires neither a workspace nor the console owner (it needed only a signed-in user), still `workspace.settings.update` on a workspace surface and the console owner on a console-owner surface. `assistantServer.<surface>.settingsUpdatePermission` names another permission, or `{ require: "authenticated" }` to keep the old behaviour. A refusal is `assistant.runtime.settings.updateForbidden` (403).
6
7
 
7
8
  ### New message keys (1)
@@ -3,8 +3,9 @@ import { createRouter, createWebHistory } from "vue-router";
3
3
  import { createVuetify } from "vuetify";
4
4
  import * as components from "vuetify/components";
5
5
  import * as directives from "vuetify/directives";
6
- import { aliases, mdi } from "vuetify/iconsets/mdi-svg";
7
6
  import "vuetify/styles";
7
+ import "@jskit-ai/material-web/client/material.css";
8
+ import { createMaterialVuetifyOptions } from "@jskit-ai/material-web/client";
8
9
  import App from "./App.vue";
9
10
  import { createWebPlacementRuntime } from "../../../shell-web/src/client/placement/runtime.js";
10
11
  import { createFixtureSocket } from "./socket.js";
@@ -69,7 +70,6 @@ if (new URLSearchParams(window.location.search).has("applicationDefaults")) {
69
70
  app.provide("jskit.shell-web.runtime.web-placement.client", placement);
70
71
  app.provide("jskit.realtime.runtime.client.socket", socket);
71
72
  app.provide("fixture", { placement, socket });
72
- app.use(router).use(createVuetify({ theme: { defaultTheme: "light" }, components, directives,
73
- icons: { defaultSet: "mdi", aliases, sets: { mdi } } }));
73
+ app.use(router).use(createVuetify({ components, directives, ...createMaterialVuetifyOptions() }));
74
74
  await router.isReady();
75
75
  app.mount("#app");
@@ -4,8 +4,9 @@ import { VueQueryPlugin } from "@tanstack/vue-query";
4
4
  import { createVuetify } from "vuetify";
5
5
  import * as components from "vuetify/components";
6
6
  import * as directives from "vuetify/directives";
7
- import { aliases, mdi } from "vuetify/iconsets/mdi-svg";
8
7
  import "vuetify/styles";
8
+ import "@jskit-ai/material-web/client/material.css";
9
+ import { createMaterialVuetifyOptions } from "@jskit-ai/material-web/client";
9
10
  import App from "./App.vue";
10
11
  import { createWebPlacementRuntime } from "../../../shell-web/src/client/placement/runtime.js";
11
12
  import { silentLogger } from "@jskit-ai/kernel/shared/support/silentLogger";
@@ -27,6 +28,6 @@ placement.setContext({ user: { id: "1" }, surfaceConfig: {
27
28
  app.provide("jskit.shell-web.runtime.web-placement.client", placement);
28
29
  app.provide("jskit.workspaces.web.scope-support", { readRouteScope: ({ route }) => ({ workspaceSlug: route.query.workspace || "alpha" }) });
29
30
  app.use(router).use(VueQueryPlugin, { queryClientConfig: { defaultOptions: { queries: { retry: false } } } });
30
- app.use(createVuetify({ theme: { defaultTheme: "light" }, components, directives, icons: { defaultSet: "mdi", aliases, sets: { mdi } } }));
31
+ app.use(createVuetify({ components, directives, ...createMaterialVuetifyOptions() }));
31
32
  await router.isReady();
32
33
  app.mount("#app");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/assistant-runtime",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test --test-concurrency=1"
@@ -14,19 +14,19 @@
14
14
  "./locales/en.json": "./locales/en.json"
15
15
  },
16
16
  "dependencies": {
17
- "@jskit-ai/assistant-core": "0.2.21",
17
+ "@jskit-ai/assistant-core": "0.2.22",
18
18
  "json-rest-schema": "^1.0.17"
19
19
  },
20
20
  "peerDependencies": {
21
- "@jskit-ai/http-runtime": "0.2.9",
22
- "@jskit-ai/http-web": "0.2.15",
23
- "@jskit-ai/kernel": "0.2.9",
24
- "@jskit-ai/realtime": "0.2.13",
25
- "@jskit-ai/shell-web": "0.2.10",
21
+ "@jskit-ai/http-runtime": "0.2.10",
22
+ "@jskit-ai/http-web": "0.2.16",
23
+ "@jskit-ai/kernel": "0.2.10",
24
+ "@jskit-ai/realtime": "0.2.14",
25
+ "@jskit-ai/shell-web": "0.2.11",
26
26
  "@tanstack/vue-query": "^5.90.5",
27
27
  "vue": "^3.5.13",
28
28
  "vuetify": "^4.0.0",
29
- "@jskit-ai/database-runtime": "0.1.258"
29
+ "@jskit-ai/database-runtime": "0.1.259"
30
30
  },
31
31
  "description": "Capability-based assistant runtime with per-surface chat, transcripts, settings, and action tools.",
32
32
  "jskit": {
@@ -126,5 +126,8 @@
126
126
  "@jskit-ai/database-runtime": {
127
127
  "optional": true
128
128
  }
129
+ },
130
+ "devDependencies": {
131
+ "@jskit-ai/material-web": "0.1.0"
129
132
  }
130
133
  }
@@ -11,7 +11,7 @@ addPlacement({
11
11
  order: 140,
12
12
  props: {
13
13
  label: "Assistant",
14
- icon: "mdi-creation-outline",
14
+ icon: "$smartToy",
15
15
  surface: "admin",
16
16
  scopedSuffix: "/assistant",
17
17
  unscopedSuffix: "/assistant"
@@ -146,10 +146,15 @@ test("optional goal controls follow declared command semantics and budgets witho
146
146
  await f.notify("chat:1");
147
147
  await f.primary.getByRole("button", { name: "Set goal", exact: true }).click();
148
148
  const objective = budgets ? "Finish with a bounded budget" : "Finish without a budget";
149
- await f.page.getByRole("textbox", { name: "Goal objective" }).fill(objective);
149
+ const objectiveField = f.page.getByRole("textbox", { name: "Goal objective" });
150
150
  const budget = f.page.getByRole("spinbutton", { name: "Token budget (optional)" });
151
- if (budgets) await budget.fill("1000");
151
+ // The second round's Set goal button is already there, so wait until the notified commands reach the form (its
152
+ // budget field follows them) before typing: a reread landing mid-typing remounts the form.
153
+ await expect(objectiveField).toBeVisible();
154
+ if (budgets) await expect(budget).toBeVisible();
152
155
  else await expect(budget).toHaveCount(0);
156
+ await objectiveField.fill(objective);
157
+ if (budgets) await budget.fill("1000");
153
158
  const start = f.page.getByRole("button", { name: "Start goal", exact: true });
154
159
  await expect(start).toBeEnabled();
155
160
  await start.click();
@@ -199,7 +204,11 @@ test("a paused goal stays cancellable while a message is sending or its delivery
199
204
  await f.primary.getByRole("button", { name: "Set goal", exact: true }).click();
200
205
  await f.page.getByRole("textbox", { name: "Goal objective" }).fill(objective);
201
206
  await f.page.getByRole("button", { name: "Start goal", exact: true }).click();
202
- await f.primary.getByRole("button", { name: "Goal running", exact: true }).click();
207
+ // As in the commands test: the running goal can show before Start has closed its menu, so wait for it to close.
208
+ const running = f.primary.getByRole("button", { name: "Goal running", exact: true });
209
+ await expect(running).toBeVisible();
210
+ await expect(running).toHaveAttribute("aria-expanded", "false");
211
+ await running.click();
203
212
  await f.page.getByRole("button", { name: "Pause goal", exact: true }).click();
204
213
  await expect(f.primary.getByRole("button", { name: "Goal paused", exact: true })).toBeVisible();
205
214
  await f.page.keyboard.press("Escape");
@@ -459,7 +468,9 @@ test("a Start accepted behind the running turn stays Starting… until its goal
459
468
  // A Start the conversation finishes without says so and keeps its objective.
460
469
  await control("Goal running").click();
461
470
  await f.page.getByRole("button", { name: "Cancel goal", exact: true }).click();
462
- await expect(control("Set goal")).toBeVisible();
471
+ // A successful Cancel closes the menu, as Start does; Set goal opens it again.
472
+ await expect(control("Set goal")).toHaveAttribute("aria-expanded", "false");
473
+ await control("Set goal").click();
463
474
  await objective.fill("Never takes effect");
464
475
  await start.click();
465
476
  await expect.poll(async () => (await starts()).length).toBe(2);
@@ -1653,6 +1664,10 @@ test("canonical model controls use the supplied reactive owner and disappear wit
1653
1664
  assert.deepEqual(await f.page.evaluate(() => window.conversationFixture.modelState()), {
1654
1665
  selected: "large", applied: "large", saving: false, selections: ["large"]
1655
1666
  });
1667
+ // A finished Apply closes the menu. Reopen it once it has finished closing: Vuetify 4.3 ignores a click on the
1668
+ // activator while the menu's close transition still runs.
1669
+ await expect(choose).toHaveAttribute("aria-expanded", "false");
1670
+ await expect(f.page.locator(".assistant-model-control")).toBeHidden();
1656
1671
  await choose.click();
1657
1672
  await f.page.evaluate(() => window.conversationFixture.modelError("Reconnect the application catalogue."));
1658
1673
  await expect(f.page.getByText("Reconnect the application catalogue.", { exact: true })).toBeVisible();