assistant-ui 0.0.126 → 0.0.128

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.
Files changed (86) hide show
  1. package/README.md +18 -2
  2. package/dist/codemods/utils/createTransformer.js +1 -2
  3. package/dist/codemods/utils/resolveBinding.js +1 -2
  4. package/dist/codemods/v0-11/content-part-to-message-part.js +1 -2
  5. package/dist/codemods/v0-12/assistant-api-to-aui.js +1 -2
  6. package/dist/codemods/v0-12/event-names-to-camelcase.js +1 -2
  7. package/dist/codemods/v0-12/primitive-if-to-aui-if.js +1 -2
  8. package/dist/codemods/v0-15/aui-accessor-calls-to-properties.js +1 -2
  9. package/dist/codemods/v0-8/ui-package-split.js +1 -2
  10. package/dist/codemods/v0-9/edge-package-split.js +1 -2
  11. package/dist/commands/add.js +1 -2
  12. package/dist/commands/agent.js +1 -2
  13. package/dist/commands/cloud.d.ts.map +1 -1
  14. package/dist/commands/cloud.js +70 -31
  15. package/dist/commands/cloud.js.map +1 -1
  16. package/dist/commands/create.js +1 -2
  17. package/dist/commands/doctor.js +1 -2
  18. package/dist/commands/info.js +1 -2
  19. package/dist/commands/init.js +1 -2
  20. package/dist/commands/mcp.js +1 -2
  21. package/dist/commands/update.js +1 -2
  22. package/dist/commands/upgrade.js +1 -2
  23. package/dist/index.js +1 -2
  24. package/dist/lib/agent-skill.d.ts +2 -2
  25. package/dist/lib/agent-skill.js +2 -3
  26. package/dist/lib/agent-skill.js.map +1 -1
  27. package/dist/lib/cloud-abort.js +1 -2
  28. package/dist/lib/cloud-auth.d.ts +1 -0
  29. package/dist/lib/cloud-auth.d.ts.map +1 -1
  30. package/dist/lib/cloud-auth.js +2 -3
  31. package/dist/lib/cloud-auth.js.map +1 -1
  32. package/dist/lib/cloud-config.d.ts +11 -1
  33. package/dist/lib/cloud-config.d.ts.map +1 -1
  34. package/dist/lib/cloud-config.js +52 -16
  35. package/dist/lib/cloud-config.js.map +1 -1
  36. package/dist/lib/cloud-provision.d.ts.map +1 -1
  37. package/dist/lib/cloud-provision.js +7 -3
  38. package/dist/lib/cloud-provision.js.map +1 -1
  39. package/dist/lib/cloud-remote.d.ts +16 -1
  40. package/dist/lib/cloud-remote.d.ts.map +1 -1
  41. package/dist/lib/cloud-remote.js +53 -9
  42. package/dist/lib/cloud-remote.js.map +1 -1
  43. package/dist/lib/cloud-setup-login.js +1 -2
  44. package/dist/lib/cloud-url.d.ts.map +1 -1
  45. package/dist/lib/cloud-url.js +3 -7
  46. package/dist/lib/cloud-url.js.map +1 -1
  47. package/dist/lib/create-project.js +1 -2
  48. package/dist/lib/handle-cli-error.js +1 -2
  49. package/dist/lib/install-ai-sdk-lib.js +1 -2
  50. package/dist/lib/install-edge-lib.js +1 -2
  51. package/dist/lib/run-spawn.js +1 -2
  52. package/dist/lib/safe-project-file.d.ts +2 -0
  53. package/dist/lib/safe-project-file.d.ts.map +1 -0
  54. package/dist/lib/safe-project-file.js +25 -0
  55. package/dist/lib/safe-project-file.js.map +1 -0
  56. package/dist/lib/transform.js +1 -2
  57. package/dist/lib/upgrade.js +1 -2
  58. package/dist/lib/utils/config.js +1 -2
  59. package/dist/lib/utils/download.js +1 -2
  60. package/dist/lib/utils/file-scanner.js +1 -2
  61. package/dist/lib/utils/logger.js +1 -2
  62. package/dist/lib/utils/package-installer.js +1 -2
  63. package/dist/lib/utils/package-json.js +1 -2
  64. package/dist/lib/utils/package-manager.js +1 -2
  65. package/dist/lib/utils/registry.js +1 -2
  66. package/dist/lib/utils/workspace.js +1 -2
  67. package/dist/program.js +1 -2
  68. package/dist/run.js +1 -2
  69. package/package.json +3 -3
  70. package/src/commands/cloud.test.ts +156 -0
  71. package/src/commands/cloud.ts +93 -41
  72. package/src/commands/mcp.test.ts +123 -0
  73. package/src/lib/agent-skill.ts +1 -1
  74. package/src/lib/cloud-auth.test.ts +7 -0
  75. package/src/lib/cloud-auth.ts +1 -1
  76. package/src/lib/cloud-config.test.ts +139 -1
  77. package/src/lib/cloud-config.ts +59 -8
  78. package/src/lib/cloud-provision.test.ts +26 -1
  79. package/src/lib/cloud-provision.ts +3 -0
  80. package/src/lib/cloud-remote.test.ts +99 -3
  81. package/src/lib/cloud-remote.ts +93 -7
  82. package/src/lib/cloud-url.ts +6 -4
  83. package/src/lib/safe-project-file.ts +31 -0
  84. package/templates/cloud-harness/components/assistant-ui/elements/thread.aui.tsx +1 -1
  85. package/templates/cloud-harness/components/assistant-ui/elements/tool-fallback.aui.tsx +7 -1
  86. package/templates/cloud-harness/package.json +7 -7
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import { readOptionalFile, writeCloudFile } from "./cloud-config";
4
+ import { assertSafeProjectFile } from "./safe-project-file";
4
5
  import { validateCloudUrl } from "./cloud-auth";
5
6
 
6
7
  export type CloudProvisionRequest = {
@@ -28,6 +29,7 @@ export const resolveProvisionRequest = async (
28
29
  accessCode?: string,
29
30
  ): Promise<CloudProvisionRequest> => {
30
31
  const file = path.join(directory, ".assistant-ui", "provision.json");
32
+ assertSafeProjectFile(file, directory);
31
33
  const previous = await readOptionalFile(file);
32
34
  let request: CloudProvisionRequest;
33
35
  if (previous) {
@@ -42,6 +44,7 @@ export const resolveProvisionRequest = async (
42
44
  request = { ...input, idempotency_key: randomUUID() };
43
45
  await writeCloudFile(file, `${JSON.stringify(request, null, 2)}\n`, {
44
46
  exclusive: true,
47
+ projectRoot: directory,
45
48
  });
46
49
  }
47
50
  return { ...request, ...(accessCode ? { access_code: accessCode } : {}) };
@@ -11,14 +11,19 @@ afterEach(() => {
11
11
  vi.unstubAllGlobals();
12
12
  });
13
13
 
14
- const harness = (reply: CloudCliResponse, token: string | null = "token") => {
14
+ const harness = (
15
+ reply: CloudCliResponse,
16
+ token: string | null = "token",
17
+ extra: Partial<Parameters<typeof runCloudCommand>[1]> = {},
18
+ ) => {
15
19
  const output = { stdout: "", stderr: "" };
16
- const send = vi.fn(async () => reply);
20
+ const send = vi.fn(async (..._args: unknown[]) => reply);
17
21
  return {
18
22
  output,
19
23
  send,
20
24
  run: (args: string[]) =>
21
25
  runCloudCommand(args, {
26
+ ...extra,
22
27
  cloudUrl: "https://cloud.example.com",
23
28
  accessToken: async () => token,
24
29
  stdout: (text) => {
@@ -136,7 +141,7 @@ describe("cloud remote commands", () => {
136
141
  expect(init.headers).toMatchObject({ Authorization: "Bearer tok" });
137
142
  expect(JSON.parse(init.body as string)).toMatchObject({
138
143
  command: "help 'a b'",
139
- client: { name: "assistant-ui" },
144
+ client: { name: "assistant-ui", actions: ["print_url", "open_url"] },
140
145
  });
141
146
  });
142
147
 
@@ -153,4 +158,95 @@ describe("cloud remote commands", () => {
153
158
  )[1];
154
159
  expect(init.headers).not.toHaveProperty("Authorization");
155
160
  });
161
+
162
+ it("prints a print_url link on stderr and never opens it", async () => {
163
+ const openUrl = vi.fn();
164
+ const { run, output } = harness(
165
+ {
166
+ stdout: "Deleting needs a confirmation.\n",
167
+ exitCode: 0,
168
+ actions: [
169
+ {
170
+ type: "print_url",
171
+ url: "https://cloud.example.com/o/acme/settings",
172
+ label: "Confirm in the dashboard",
173
+ },
174
+ ],
175
+ },
176
+ "token",
177
+ { interactive: true, openUrl },
178
+ );
179
+ await expect(run(["projects", "delete", "web"])).resolves.toBe(0);
180
+ expect(output).toEqual({
181
+ stdout: "Deleting needs a confirmation.\n",
182
+ stderr:
183
+ "Confirm in the dashboard: https://cloud.example.com/o/acme/settings\n",
184
+ });
185
+ expect(openUrl).not.toHaveBeenCalled();
186
+ });
187
+
188
+ it("opens an open_url link only when interactive, without --no-open, on the same Cloud", async () => {
189
+ const reply = (url: string): CloudCliResponse => ({
190
+ exitCode: 0,
191
+ actions: [{ type: "open_url", url }],
192
+ });
193
+ const dashboard = "https://cloud.example.com/o/acme";
194
+ const cases: [boolean, boolean, string, boolean][] = [
195
+ [true, false, dashboard, true],
196
+ [true, true, dashboard, false],
197
+ [false, false, dashboard, false],
198
+ [true, false, "https://evil.example.org/", false],
199
+ ];
200
+ for (const [interactive, noOpen, url, opens] of cases) {
201
+ const openUrl = vi.fn();
202
+ const { run, output } = harness(reply(url), "token", {
203
+ interactive,
204
+ noOpen,
205
+ openUrl,
206
+ });
207
+ await expect(run(["x"])).resolves.toBe(0);
208
+ expect(output.stderr).toBe(`Open: ${url}\n`);
209
+ expect(openUrl).toHaveBeenCalledTimes(opens ? 1 : 0);
210
+ }
211
+ });
212
+
213
+ it("fails a malformed link action before printing", async () => {
214
+ for (const url of [undefined, 3, "javascript:alert(1)", "not a url"]) {
215
+ const { run, output } = harness({
216
+ stdout: "x\n",
217
+ exitCode: 0,
218
+ actions: [{ type: "open_url", url }],
219
+ });
220
+ await expect(run(["x"])).resolves.toBe(1);
221
+ expect(output.stdout).toBe("");
222
+ expect(output.stderr).toMatch(/malformed "open_url"/);
223
+ }
224
+ });
225
+
226
+ it("passes the directory's default org and project to the server", async () => {
227
+ const { run, send } = harness({ exitCode: 0 }, "token", {
228
+ defaults: async () => ({ org: "org_1", project: "proj_1" }),
229
+ });
230
+ await run(["projects", "show"]);
231
+ expect(send).toHaveBeenCalledWith(
232
+ "https://cloud.example.com",
233
+ ["projects", "show"],
234
+ "token",
235
+ { org: "org_1", project: "proj_1" },
236
+ );
237
+ });
238
+
239
+ it("sends defaults in the request body only when there are some", async () => {
240
+ const fetchMock = vi.fn(async () => Response.json({ exitCode: 0 }));
241
+ vi.stubGlobal("fetch", fetchMock);
242
+ await sendCloudCommand("https://cloud.example.com", ["x"], null, {
243
+ org: "org_1",
244
+ });
245
+ await sendCloudCommand("https://cloud.example.com", ["x"], null, {});
246
+ const bodies = fetchMock.mock.calls.map((call) =>
247
+ JSON.parse((call as unknown as [string, RequestInit])[1].body as string),
248
+ );
249
+ expect(bodies[0].defaults).toEqual({ org: "org_1" });
250
+ expect(bodies[1]).not.toHaveProperty("defaults");
251
+ });
156
252
  });
@@ -4,6 +4,12 @@ export const CLOUD_CLI_RUN_PATH = "/api/cli/run";
4
4
 
5
5
  export type CloudCliAction = { type: string; [key: string]: unknown };
6
6
 
7
+ /**
8
+ * The organization and project a command uses when it is given no --org or
9
+ * --project, read from the `.assistant-ui/cloud.json` that setup writes.
10
+ */
11
+ export type CloudCliDefaults = { org?: string; project?: string };
12
+
7
13
  export type CloudCliResponse = {
8
14
  stdout?: string;
9
15
  stderr?: string;
@@ -62,6 +68,7 @@ export const sendCloudCommand = async (
62
68
  cloudUrl: string,
63
69
  args: readonly string[],
64
70
  accessToken: string | null,
71
+ defaults?: CloudCliDefaults | null,
65
72
  ): Promise<CloudCliResponse> => {
66
73
  const url = `${validateCloudUrl(cloudUrl)}${CLOUD_CLI_RUN_PATH}`;
67
74
  let response: Response;
@@ -78,7 +85,10 @@ export const sendCloudCommand = async (
78
85
  name: "assistant-ui",
79
86
  version: cliVersion,
80
87
  platform: `${process.platform}-${process.arch}`,
88
+ // The server sends only the actions listed here.
89
+ actions: Object.keys(ACTION_HANDLERS),
81
90
  },
91
+ ...(defaults && (defaults.org || defaults.project) ? { defaults } : {}),
82
92
  }),
83
93
  redirect: "error",
84
94
  signal: AbortSignal.timeout(60_000),
@@ -98,12 +108,62 @@ export const sendCloudCommand = async (
98
108
  return body;
99
109
  };
100
110
 
101
- // A response naming an action this client lacks fails before printing, so the
102
- // server can roll a new action out behind the client version it is sent.
103
- const ACTION_HANDLERS: Record<
104
- string,
105
- (action: CloudCliAction) => Promise<number>
106
- > = {};
111
+ type ActionContext = {
112
+ cloudUrl: string;
113
+ /** False when the user passed --no-open or no one is at the terminal. */
114
+ open: boolean;
115
+ openUrl: (url: string) => void;
116
+ stderr: (text: string) => void;
117
+ };
118
+
119
+ type ActionHandler = {
120
+ /** Checks the action's fields; a malformed action fails before printing. */
121
+ valid(action: CloudCliAction): boolean;
122
+ run(action: CloudCliAction, context: ActionContext): Promise<number> | number;
123
+ };
124
+
125
+ const httpUrl = (value: unknown): URL | null => {
126
+ if (typeof value !== "string") return null;
127
+ try {
128
+ const url = new URL(value);
129
+ return url.protocol === "https:" || url.protocol === "http:" ? url : null;
130
+ } catch {
131
+ return null;
132
+ }
133
+ };
134
+
135
+ const urlAction = (action: CloudCliAction): boolean =>
136
+ !!httpUrl(action["url"]) &&
137
+ (action["label"] === undefined || typeof action["label"] === "string");
138
+
139
+ const printUrl = (action: CloudCliAction, context: ActionContext): void => {
140
+ const label = (action["label"] as string | undefined) ?? "Open";
141
+ context.stderr(`${label}: ${action["url"] as string}\n`);
142
+ };
143
+
144
+ // The request lists these (`client.actions`) and the server sends no others;
145
+ // a response naming one this client lacks still fails before printing.
146
+ const ACTION_HANDLERS: Record<string, ActionHandler> = {
147
+ print_url: {
148
+ valid: urlAction,
149
+ run: (action, context) => {
150
+ printUrl(action, context);
151
+ return 0;
152
+ },
153
+ },
154
+ // Prints the link, then opens it in a browser when someone is at the
155
+ // terminal. Only links on the Cloud the command ran against are opened.
156
+ open_url: {
157
+ valid: urlAction,
158
+ run: (action, context) => {
159
+ printUrl(action, context);
160
+ const url = httpUrl(action["url"])!;
161
+ if (context.open && url.origin === new URL(context.cloudUrl).origin)
162
+ context.openUrl(url.href);
163
+ return 0;
164
+ },
165
+ },
166
+ };
107
167
 
108
168
  export const runCloudCommand = async (
109
169
  args: readonly string[],
@@ -113,10 +173,20 @@ export const runCloudCommand = async (
113
173
  stdout: (text: string) => void;
114
174
  stderr: (text: string) => void;
115
175
  send?: typeof sendCloudCommand;
176
+ /** The default org/project for this directory, if setup configured one. */
177
+ defaults?: () => Promise<CloudCliDefaults | null>;
178
+ /** Whether someone is at the terminal to see a browser open. */
179
+ interactive?: boolean;
180
+ /** --no-open: print links without opening them. */
181
+ noOpen?: boolean;
182
+ openUrl?: (url: string) => void;
116
183
  },
117
184
  ): Promise<number> => {
118
185
  let response: CloudCliResponse;
119
186
  try {
187
+ const defaults = deps.defaults
188
+ ? await deps.defaults().catch(() => null)
189
+ : null;
120
190
  response = await (deps.send ?? sendCloudCommand)(
121
191
  deps.cloudUrl,
122
192
  args,
@@ -126,6 +196,7 @@ export const runCloudCommand = async (
126
196
  );
127
197
  return null;
128
198
  }),
199
+ ...(defaults ? [defaults] : []),
129
200
  );
130
201
  } catch (error) {
131
202
  deps.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
@@ -140,10 +211,25 @@ export const runCloudCommand = async (
140
211
  );
141
212
  return 1;
142
213
  }
214
+ const malformed = response.actions?.find(
215
+ (action) => !ACTION_HANDLERS[action.type]!.valid(action),
216
+ );
217
+ if (malformed) {
218
+ deps.stderr(
219
+ `Unexpected response from the server (malformed "${malformed.type}" action).\n`,
220
+ );
221
+ return 1;
222
+ }
143
223
  if (response.stdout) deps.stdout(response.stdout);
144
224
  if (response.stderr) deps.stderr(response.stderr);
225
+ const context: ActionContext = {
226
+ cloudUrl: deps.cloudUrl,
227
+ open: !deps.noOpen && !!deps.interactive && !!deps.openUrl,
228
+ openUrl: deps.openUrl ?? (() => {}),
229
+ stderr: deps.stderr,
230
+ };
145
231
  for (const action of response.actions ?? []) {
146
- const code = await ACTION_HANDLERS[action.type]!(action);
232
+ const code = await ACTION_HANDLERS[action.type]!.run(action, context);
147
233
  if (code !== 0) return code;
148
234
  }
149
235
  return response.exitCode;
@@ -1,3 +1,8 @@
1
+ const isLoopbackHostname = (hostname: string): boolean =>
2
+ hostname === "localhost" ||
3
+ hostname === "[::1]" ||
4
+ /^127(?:\.\d{1,3}){3}$/.test(hostname);
5
+
1
6
  export const validateCloudUrl = (value: string): string => {
2
7
  const url = new URL(value);
3
8
  if (
@@ -6,10 +11,7 @@ export const validateCloudUrl = (value: string): string => {
6
11
  url.search ||
7
12
  url.hash ||
8
13
  (url.protocol !== "https:" &&
9
- !(
10
- url.protocol === "http:" &&
11
- ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)
12
- ))
14
+ !(url.protocol === "http:" && isLoopbackHostname(url.hostname)))
13
15
  ) {
14
16
  throw new Error(
15
17
  "Cloud, accounts and setup URLs must use HTTPS or HTTP localhost for development.",
@@ -0,0 +1,31 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ export const assertSafeProjectFile = (file: string, projectRoot: string) => {
5
+ try {
6
+ if (fs.lstatSync(file).isSymbolicLink()) {
7
+ throw new Error(`${file} is a symbolic link`);
8
+ }
9
+ } catch (error) {
10
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
11
+ }
12
+
13
+ const root = fs.realpathSync(projectRoot);
14
+ let parent = path.dirname(file);
15
+ const missing: string[] = [];
16
+ while (!fs.existsSync(parent)) {
17
+ const next = path.dirname(parent);
18
+ if (next === parent) break;
19
+ missing.unshift(path.basename(parent));
20
+ parent = next;
21
+ }
22
+ const resolvedParent = path.join(fs.realpathSync(parent), ...missing);
23
+ const relative = path.relative(root, resolvedParent);
24
+ if (
25
+ relative === ".." ||
26
+ relative.startsWith(`..${path.sep}`) ||
27
+ path.isAbsolute(relative)
28
+ ) {
29
+ throw new Error(`${file} has a parent directory outside the project`);
30
+ }
31
+ };
@@ -409,7 +409,7 @@ const AssistantActionBar: FC = () => {
409
409
  className="aui-action-bar-more-content bg-popover/95 text-popover-foreground data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-xl border p-1.5 shadow-lg backdrop-blur-sm"
410
410
  >
411
411
  <ActionBarPrimitive.ExportMarkdown asChild>
412
- <ActionBarMorePrimitive.Item className="aui-action-bar-more-item hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-sm outline-none select-none">
412
+ <ActionBarMorePrimitive.Item className="aui-action-bar-more-item hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground flex items-center gap-2 rounded-lg px-2.5 py-1.5 text-sm outline-none select-none">
413
413
  <DownloadIcon className="size-4" />
414
414
  Export as Markdown
415
415
  </ActionBarMorePrimitive.Item>
@@ -141,15 +141,19 @@ function ToolFallbackDuration({
141
141
  function ToolFallbackTrigger({
142
142
  toolName,
143
143
  status,
144
+ isError,
144
145
  responseDeliveryStatus,
145
146
  className,
146
147
  ...props
147
148
  }: React.ComponentProps<typeof CollapsibleTrigger> & {
148
149
  toolName: string;
149
150
  status?: ToolCallMessagePartStatus;
151
+ isError?: ToolCallMessagePart["isError"];
150
152
  responseDeliveryStatus?: ToolCallMessagePart["responseDeliveryStatus"];
151
153
  }) {
152
- const statusType = status?.type ?? "complete";
154
+ const reportedType = status?.type ?? "complete";
155
+ const statusType =
156
+ reportedType === "complete" && isError ? "incomplete" : reportedType;
153
157
  const isRunning = statusType === "running";
154
158
  const isCancelled =
155
159
  status?.type === "incomplete" && status.reason === "cancelled";
@@ -1198,6 +1202,7 @@ const ToolFallbackImpl: ToolCallMessagePartComponent = ({
1198
1202
  toolName,
1199
1203
  argsText,
1200
1204
  result,
1205
+ isError,
1201
1206
  status,
1202
1207
  responseDeliveryStatus,
1203
1208
  addResult,
@@ -1251,6 +1256,7 @@ const ToolFallbackImpl: ToolCallMessagePartComponent = ({
1251
1256
  <ToolFallbackTrigger
1252
1257
  toolName={toolName}
1253
1258
  status={status}
1259
+ isError={isError}
1254
1260
  responseDeliveryStatus={responseDeliveryStatus}
1255
1261
  />
1256
1262
  <ToolFallbackContent>
@@ -13,7 +13,7 @@
13
13
  "typecheck": "tsc --noEmit"
14
14
  },
15
15
  "dependencies": {
16
- "@ai-sdk/openai": "^4.0.96",
16
+ "@ai-sdk/openai": "^4.0.102",
17
17
  "@assistant-ui/ai-sdk": "workspace:*",
18
18
  "@assistant-ui/core": "workspace:*",
19
19
  "@assistant-ui/react": "workspace:*",
@@ -22,20 +22,20 @@
22
22
  "@assistant-ui/store": "workspace:*",
23
23
  "@assistant-ui/tap": "workspace:*",
24
24
  "@base-ui/react": "^1.9.0",
25
- "ai": "^7.0.143",
25
+ "ai": "^7.0.153",
26
26
  "assistant-cloud": "workspace:*",
27
27
  "class-variance-authority": "^0.7.1",
28
28
  "cn": "^0.5.0",
29
29
  "harness-sdk": "file:vendor/harness-sdk-0.3.1.tgz",
30
- "lucide-react": "^1.59.0",
31
- "next": "^16.4.2",
30
+ "lucide-react": "^1.61.0",
31
+ "next": "^16.4.5",
32
32
  "react": "^19.3.1",
33
33
  "react-dom": "^19.3.1",
34
34
  "remark-gfm": "^4.0.1",
35
35
  "statewire": "file:vendor/statewire-0.19.3.tgz",
36
36
  "tw-animate-css": "^1.4.0",
37
37
  "tw-shimmer": "^0.4.14",
38
- "zustand": "^5.0.15"
38
+ "zustand": "^5.0.16"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@assistant-ui/next": "workspace:*",
@@ -43,8 +43,8 @@
43
43
  "@types/node": "^26.6.5",
44
44
  "@types/react": "^19.3.0",
45
45
  "@types/react-dom": "^19.3.0",
46
- "oxfmt": "^0.73.0",
47
- "oxlint": "^1.88.0",
46
+ "oxfmt": "^0.74.0",
47
+ "oxlint": "^1.89.0",
48
48
  "tailwindcss": "^4.3.3",
49
49
  "typescript": "^7.0.2"
50
50
  }