@agent-native/core 0.84.29 → 0.84.30

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/corpus/README.md CHANGED
@@ -28,4 +28,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
  ## Generated Counts
29
29
 
30
30
  - core files: 2043
31
- - template files: 4972
31
+ - template files: 4973
@@ -1,5 +1,12 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.84.30
4
+
5
+ ### Patch Changes
6
+
7
+ - 80e618a: Fix hosted Google Analytics / Tag Manager injection by baking the measurement id into Nitro server bundles and merging the required GA/GTM script, connect, and image hosts into existing stricter document CSPs.
8
+ - 80e618a: Improve chat tool-preparation UX by hiding zero-byte progress, using clearer preparation/writing copy, and showing a delayed long-running update hint.
9
+
3
10
  ## 0.84.29
4
11
 
5
12
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.84.29",
3
+ "version": "0.84.30",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -70,6 +70,43 @@ export const ApprovalContext = React.createContext<ApprovalContextValue | null>(
70
70
  null,
71
71
  );
72
72
 
73
+ export const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45_000;
74
+
75
+ function ToolLongRunningHintShell({
76
+ toolName,
77
+ isRunning,
78
+ children,
79
+ }: {
80
+ toolName: string;
81
+ isRunning: boolean;
82
+ children: React.ReactNode;
83
+ }) {
84
+ const [showLongRunningHint, setShowLongRunningHint] = useState(false);
85
+
86
+ useEffect(() => {
87
+ if (!isRunning) {
88
+ setShowLongRunningHint(false);
89
+ return;
90
+ }
91
+ setShowLongRunningHint(false);
92
+ const timeout = window.setTimeout(() => {
93
+ setShowLongRunningHint(true);
94
+ }, TOOL_LONG_RUNNING_HINT_DELAY_MS);
95
+ return () => window.clearTimeout(timeout);
96
+ }, [isRunning, toolName]);
97
+
98
+ return (
99
+ <>
100
+ {children}
101
+ {isRunning && showLongRunningHint && (
102
+ <div className="mt-0.5 px-2.5 text-[11px] leading-snug text-muted-foreground/80">
103
+ Still working. Large updates can take a minute or two.
104
+ </div>
105
+ )}
106
+ </>
107
+ );
108
+ }
109
+
73
110
  // ─── Tool-payload formatting ──────────────────────────────────────────────────
74
111
 
75
112
  type ToolDetailSection = "input" | "result";
@@ -435,38 +472,43 @@ export function ToolCallDisplay({
435
472
  // These must be separate components so hook order in ToolCallDisplayGeneric
436
473
  // is always stable (no conditional hook calls).
437
474
  const toolKind = structuredMeta?.toolKind as string | undefined;
475
+ const wrapToolDisplay = (children: React.ReactNode) => (
476
+ <ToolLongRunningHintShell toolName={toolName} isRunning={isRunning}>
477
+ {children}
478
+ </ToolLongRunningHintShell>
479
+ );
438
480
  if (toolKind === "bash") {
439
- return (
481
+ return wrapToolDisplay(
440
482
  <BashCell
441
483
  meta={
442
484
  structuredMeta as unknown as Parameters<typeof BashCell>[0]["meta"]
443
485
  }
444
486
  output={result}
445
487
  isRunning={isRunning}
446
- />
488
+ />,
447
489
  );
448
490
  }
449
491
  if (toolKind === "edit") {
450
- return (
492
+ return wrapToolDisplay(
451
493
  <EditCell
452
494
  meta={
453
495
  structuredMeta as unknown as Parameters<typeof EditCell>[0]["meta"]
454
496
  }
455
497
  isRunning={isRunning}
456
- />
498
+ />,
457
499
  );
458
500
  }
459
501
  if (toolKind === "write") {
460
- return (
502
+ return wrapToolDisplay(
461
503
  <WriteCell
462
504
  meta={
463
505
  structuredMeta as unknown as Parameters<typeof WriteCell>[0]["meta"]
464
506
  }
465
507
  isRunning={isRunning}
466
- />
508
+ />,
467
509
  );
468
510
  }
469
- return (
511
+ return wrapToolDisplay(
470
512
  <ToolCallDisplayGeneric
471
513
  toolName={toolName}
472
514
  argsText={argsText}
@@ -477,7 +519,7 @@ export function ToolCallDisplay({
477
519
  isRunning={isRunning}
478
520
  approval={approval}
479
521
  repeatCount={repeatCount}
480
- />
522
+ />,
481
523
  );
482
524
  }
483
525
 
@@ -205,13 +205,26 @@ function baseActivityLabel(ev: SSEEvent, tool?: string): string {
205
205
  return humanizeToolLabelText(ev.label ?? "Working", tool);
206
206
  }
207
207
 
208
+ function preparationActivityLabel(
209
+ tool: string | undefined,
210
+ progressBytes: number | undefined,
211
+ ): string {
212
+ const action = humanizeToolName(tool);
213
+ if (progressBytes === undefined) {
214
+ return `Starting ${action}...`;
215
+ }
216
+ if (progressBytes <= 0) {
217
+ return `Preparing ${action}...`;
218
+ }
219
+ return `Writing ${action}... (${formatProgressBytes(progressBytes)} prepared)`;
220
+ }
221
+
208
222
  function visibleActivityLabel(ev: SSEEvent, tool?: string): string {
209
- const label = baseActivityLabel(ev, tool);
210
223
  const progressBytes = activityProgressBytes(ev);
211
- if (progressBytes === undefined || !isPreparingActionActivity(ev)) {
212
- return label;
224
+ if (isPreparingActionActivity(ev)) {
225
+ return preparationActivityLabel(tool, progressBytes);
213
226
  }
214
- return `${label} (${formatProgressBytes(progressBytes)} streamed)`;
227
+ return baseActivityLabel(ev, tool);
215
228
  }
216
229
 
217
230
  function findPendingToolCallIndex(
@@ -277,6 +277,59 @@ type CspDirective = {
277
277
  tokens: string[];
278
278
  };
279
279
 
280
+ const CSP_DIRECTIVES_WITH_VALUE_TOKENS = new Set([
281
+ "base-uri",
282
+ "block-all-mixed-content",
283
+ "child-src",
284
+ "connect-src",
285
+ "default-src",
286
+ "fenced-frame-src",
287
+ "font-src",
288
+ "form-action",
289
+ "frame-ancestors",
290
+ "frame-src",
291
+ "img-src",
292
+ "manifest-src",
293
+ "media-src",
294
+ "navigate-to",
295
+ "object-src",
296
+ "plugin-types",
297
+ "prefetch-src",
298
+ "referrer",
299
+ "reflected-xss",
300
+ "require-sri-for",
301
+ "require-trusted-types-for",
302
+ "report-to",
303
+ "report-uri",
304
+ "sandbox",
305
+ "script-src",
306
+ "script-src-attr",
307
+ "script-src-elem",
308
+ "style-src",
309
+ "style-src-attr",
310
+ "style-src-elem",
311
+ "trusted-types",
312
+ "upgrade-insecure-requests",
313
+ "webrtc",
314
+ "worker-src",
315
+ ]);
316
+
317
+ function hasCommaJoinedCspPolicies(policy: string): boolean {
318
+ let commaIndex = policy.indexOf(",");
319
+ while (commaIndex !== -1) {
320
+ const afterComma = policy.slice(commaIndex + 1);
321
+ const directive = /^\s+([a-z][a-z0-9-]*)(?=\s|;|$)/i.exec(afterComma)?.[1];
322
+ if (
323
+ directive &&
324
+ CSP_DIRECTIVES_WITH_VALUE_TOKENS.has(directive.toLowerCase())
325
+ ) {
326
+ return true;
327
+ }
328
+ commaIndex = policy.indexOf(",", commaIndex + 1);
329
+ }
330
+ return false;
331
+ }
332
+
280
333
  function parseCsp(policy: string): CspDirective[] {
281
334
  return policy
282
335
  .split(";")
@@ -349,13 +402,96 @@ function appendToExistingCspDirective(
349
402
  existing.tokens = appendCspTokens(existing.tokens, additions);
350
403
  }
351
404
 
352
- function augmentExistingCspForFrameworkScripts(
405
+ function hasStrictNonceScriptPolicy(tokens: readonly string[]): boolean {
406
+ return tokens.some(
407
+ (token) => token === "'strict-dynamic'" || token.startsWith("'nonce-"),
408
+ );
409
+ }
410
+
411
+ function appendToScriptCspDirective(
412
+ directives: CspDirective[],
413
+ name: string,
414
+ additions: readonly string[],
415
+ ): boolean {
416
+ const existing = findCspDirective(directives, name);
417
+ if (existing) {
418
+ if (hasStrictNonceScriptPolicy(existing.tokens)) return false;
419
+ existing.tokens = appendCspTokens(existing.tokens, additions);
420
+ return true;
421
+ }
422
+
423
+ const defaultSrc = findCspDirective(directives, "default-src");
424
+ if (!defaultSrc || hasStrictNonceScriptPolicy(defaultSrc.tokens)) {
425
+ return false;
426
+ }
427
+ directives.push({
428
+ name,
429
+ tokens: appendCspTokens([...defaultSrc.tokens], additions),
430
+ });
431
+ return true;
432
+ }
433
+
434
+ function appendToEffectiveScriptElementCspDirective(
435
+ directives: CspDirective[],
436
+ additions: readonly string[],
437
+ ): boolean {
438
+ const scriptSrcElem = findCspDirective(directives, "script-src-elem");
439
+ if (scriptSrcElem) {
440
+ if (hasStrictNonceScriptPolicy(scriptSrcElem.tokens)) return false;
441
+ scriptSrcElem.tokens = appendCspTokens(scriptSrcElem.tokens, additions);
442
+ return true;
443
+ }
444
+
445
+ return appendToScriptCspDirective(directives, "script-src", additions);
446
+ }
447
+
448
+ function augmentExistingEnforcedCspForFrameworkScripts(
449
+ policy: string,
450
+ options: {
451
+ gaScriptSrcTokens: readonly string[];
452
+ gaEnabled: boolean;
453
+ },
454
+ ): string {
455
+ // Multiple CSP headers are surfaced by Headers.get() as one comma-joined
456
+ // string. CSP is not a comma-list header, so serializing a parsed combined
457
+ // value would turn two policies into one invalid policy. Leave those headers
458
+ // app-owned; a comma inside a source/report URL is still safe to parse.
459
+ if (hasCommaJoinedCspPolicies(policy)) return policy;
460
+
461
+ const directives = parseCsp(policy);
462
+ if (!directives.length) return policy;
463
+
464
+ if (options.gaEnabled) {
465
+ const addedScriptElement = appendToEffectiveScriptElementCspDirective(
466
+ directives,
467
+ options.gaScriptSrcTokens,
468
+ );
469
+ if (addedScriptElement) {
470
+ appendToExistingOrDefaultCspDirective(
471
+ directives,
472
+ "connect-src",
473
+ GA_CSP_CONNECT_HOSTS,
474
+ );
475
+ appendToExistingOrDefaultCspDirective(
476
+ directives,
477
+ "img-src",
478
+ GA_CSP_IMG_HOSTS,
479
+ );
480
+ }
481
+ }
482
+
483
+ return serializeCsp(directives);
484
+ }
485
+
486
+ function augmentExistingReportOnlyCspForFrameworkScripts(
353
487
  policy: string,
354
488
  options: {
355
489
  scriptSrcTokens: readonly string[];
356
490
  gaEnabled: boolean;
357
491
  },
358
492
  ): string {
493
+ if (hasCommaJoinedCspPolicies(policy)) return policy;
494
+
359
495
  const directives = parseCsp(policy);
360
496
  if (!directives.length) return policy;
361
497
 
@@ -364,7 +500,6 @@ function augmentExistingCspForFrameworkScripts(
364
500
  "script-src",
365
501
  options.scriptSrcTokens,
366
502
  );
367
- // `script-src-elem` overrides `script-src` for script tags when present.
368
503
  appendToExistingCspDirective(
369
504
  directives,
370
505
  "script-src-elem",
@@ -409,10 +544,11 @@ function augmentExistingCspForFrameworkScripts(
409
544
  * instead of reporting a violation on every page load.
410
545
  *
411
546
  * If an app or host already sends an enforced CSP with `script-src`,
412
- * `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge the
413
- * framework's GA/GTM allowances into the existing directive. That keeps
414
- * stricter deployments working without adding a new enforced script policy to
415
- * routes that only declare unrelated directives such as `frame-ancestors`.
547
+ * `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only
548
+ * GA-specific allowances into existing host/hash policies. Strict nonce or
549
+ * `strict-dynamic` script policies stay app-owned because blindly appending
550
+ * hashes or hosts would widen the policy without reliably loading our injected
551
+ * scripts.
416
552
  *
417
553
  * Templates additionally render a theme-init inline script whose exact content
418
554
  * varies by template (default theme param, custom docs variant, etc.) and which
@@ -440,6 +576,7 @@ function applyDocumentCsp(headers: Headers, sentryScript: string | null): void {
440
576
  const gaInlineBody = getGaInlineConfigScriptBody();
441
577
  const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;
442
578
  const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];
579
+ const gaScriptSrcTokens = [...(gaHash ? [gaHash] : []), ...gaHosts];
443
580
  const scriptSrcTokens = [
444
581
  "'self'",
445
582
  ...(sentryHash ? [sentryHash] : []),
@@ -449,6 +586,7 @@ function applyDocumentCsp(headers: Headers, sentryScript: string | null): void {
449
586
 
450
587
  const cspAugmentOptions = {
451
588
  scriptSrcTokens,
589
+ gaScriptSrcTokens,
452
590
  gaEnabled: Boolean(gaInlineBody),
453
591
  };
454
592
  const existing = headers.get("content-security-policy") ?? "";
@@ -460,7 +598,10 @@ function applyDocumentCsp(headers: Headers, sentryScript: string | null): void {
460
598
  } else {
461
599
  headers.set(
462
600
  "content-security-policy",
463
- augmentExistingCspForFrameworkScripts(existing, cspAugmentOptions),
601
+ augmentExistingEnforcedCspForFrameworkScripts(
602
+ existing,
603
+ cspAugmentOptions,
604
+ ),
464
605
  );
465
606
  }
466
607
 
@@ -471,7 +612,10 @@ function applyDocumentCsp(headers: Headers, sentryScript: string | null): void {
471
612
  } else {
472
613
  headers.set(
473
614
  "content-security-policy-report-only",
474
- augmentExistingCspForFrameworkScripts(existingRo, cspAugmentOptions),
615
+ augmentExistingReportOnlyCspForFrameworkScripts(
616
+ existingRo,
617
+ cspAugmentOptions,
618
+ ),
475
619
  );
476
620
  }
477
621
  }
@@ -67,7 +67,10 @@ import {
67
67
  type TweakSelections,
68
68
  } from "@shared/resolve-tweaks";
69
69
  import { utilityStem, widthToPrefix } from "@shared/responsive-classes";
70
- import { normalizeDesignSourceType } from "@shared/source-mode";
70
+ import {
71
+ normalizeDesignSourceType,
72
+ type DesignSourceType,
73
+ } from "@shared/source-mode";
71
74
  import {
72
75
  IconArrowLeft,
73
76
  IconArrowUpRight,
@@ -347,6 +350,28 @@ function getContentSignature(content: string): string {
347
350
  return `${content.length}:${hash.toString(36)}`;
348
351
  }
349
352
 
353
+ export function getOverviewScreenRuntimeReplacementKey({
354
+ screenId,
355
+ updatedAt,
356
+ content,
357
+ }: {
358
+ screenId: string;
359
+ updatedAt?: string | null;
360
+ content: string;
361
+ }) {
362
+ return [screenId, updatedAt ?? "", getContentSignature(content)].join(":");
363
+ }
364
+
365
+ export function shouldUseOverviewRuntimeReplacement({
366
+ sourceType,
367
+ externalSnapshotHtml,
368
+ }: {
369
+ sourceType?: DesignSourceType | null;
370
+ externalSnapshotHtml?: string | null;
371
+ }) {
372
+ return sourceType === "inline" && !externalSnapshotHtml;
373
+ }
374
+
350
375
  function dedupeStringIds(ids: string[]): string[] {
351
376
  return Array.from(new Set(ids.filter(Boolean)));
352
377
  }
@@ -17258,12 +17283,26 @@ ${serializedHtml}
17258
17283
  const screenBridgeUrl = screen.bridgeUrl;
17259
17284
  const screenSnapshot =
17260
17285
  liveScreenSnapshotsById[screen.id]?.html;
17286
+ const screenContentSignature =
17287
+ getContentSignature(screenContent);
17288
+ const useRuntimeReplacement =
17289
+ shouldUseOverviewRuntimeReplacement({
17290
+ sourceType: screenSourceType,
17291
+ externalSnapshotHtml: screenSnapshot,
17292
+ });
17293
+ const runtimeReplacementKey = useRuntimeReplacement
17294
+ ? getOverviewScreenRuntimeReplacementKey({
17295
+ screenId: screen.id,
17296
+ updatedAt: screen.updatedAt,
17297
+ content: screenContent,
17298
+ })
17299
+ : undefined;
17261
17300
  const screenContentKey = screenIsActive
17262
17301
  ? [screen.id, contentRenderRevision].join(":")
17263
17302
  : [
17264
17303
  screen.id,
17265
17304
  screen.updatedAt ?? "",
17266
- getContentSignature(screenContent),
17305
+ screenContentSignature,
17267
17306
  0,
17268
17307
  ].join(":");
17269
17308
 
@@ -17271,6 +17310,10 @@ ${serializedHtml}
17271
17310
  <DesignCanvas
17272
17311
  content={screenContent}
17273
17312
  contentKey={screenContentKey}
17313
+ runtimeReplacementContent={
17314
+ useRuntimeReplacement ? screenContent : undefined
17315
+ }
17316
+ runtimeReplacementKey={runtimeReplacementKey}
17274
17317
  screenId={screen.id}
17275
17318
  zoom={100}
17276
17319
  deviceFrame="none"
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-01
4
+ ---
5
+
6
+ Design previews refresh immediately after agent screen edits.
@@ -16,6 +16,7 @@ export type ApprovalContextValue = {
16
16
  onApprove: (approvalKey: string) => void;
17
17
  };
18
18
  export declare const ApprovalContext: React.Context<ApprovalContextValue | null>;
19
+ export declare const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45000;
19
20
  type ToolDetailSection = "input" | "result";
20
21
  export type ToolDetailPayload = {
21
22
  section: ToolDetailSection;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-call-display.d.ts","sourceRoot":"","sources":["../../../src/client/chat/tool-call-display.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAcpE,OAAO,KAMN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAKzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAIL,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAmBhC,eAAO,MAAM,kBAAkB,wBAA6B,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,8DAA8D;IAC9D,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AACF,eAAO,MAAM,eAAe,4CAE3B,CAAC;AAIF,KAAK,iBAAiB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAiEF,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,iBAAiB,GAAG,IAAI,CAyB1B;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,iBAAiB,GAAG,IAAI,CAU1B;AA8ND,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,MAAM,EACN,MAAM,EACN,SAAS,EACT,cAAc,EACd,QAAQ,EACR,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,qBAiDA;AAwND,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,GAAG,IAAI,EACR,EAAE,wBAAwB,GAAG;IAC5B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,qBAwBA;AAMD,wBAAgB,sBAAsB,CAAC,EACrC,OAAO,GACR,EAAE;IACD,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,qBA8CA;AAKD,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"tool-call-display.d.ts","sourceRoot":"","sources":["../../../src/client/chat/tool-call-display.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAcpE,OAAO,KAMN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAKzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAIL,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAmBhC,eAAO,MAAM,kBAAkB,wBAA6B,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,8DAA8D;IAC9D,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AACF,eAAO,MAAM,eAAe,4CAE3B,CAAC;AAEF,eAAO,MAAM,+BAA+B,QAAS,CAAC;AAuCtD,KAAK,iBAAiB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAiEF,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,iBAAiB,GAAG,IAAI,CAyB1B;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,iBAAiB,GAAG,IAAI,CAU1B;AA8ND,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,MAAM,EACN,MAAM,EACN,SAAS,EACT,cAAc,EACd,QAAQ,EACR,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,qBAsDA;AAwND,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,GAAG,IAAI,EACR,EAAE,wBAAwB,GAAG;IAC5B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,qBAwBA;AAMD,wBAAgB,sBAAsB,CAAC,EACrC,OAAO,GACR,EAAE;IACD,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,qBA8CA;AAKD,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { IconLoader2, IconCircleX, IconCheck, IconSquareFilled, IconChevronDown, IconCopy, IconSearch, IconArrowsMaximize, IconArrowsMinimize, IconShieldCheck, IconX, } from "@tabler/icons-react";
3
3
  import React, { useState, useEffect, useCallback, useMemo, useRef, } from "react";
4
4
  import { AgentTaskCard } from "../AgentTaskCard.js";
@@ -14,6 +14,22 @@ import { isBuiltinDataWidgetActionRenderer, resolveBuiltinActionChatRenderer, re
14
14
  // Exported so AssistantChatInner can provide a context value.
15
15
  export const ChatRunningContext = React.createContext(false);
16
16
  export const ApprovalContext = React.createContext(null);
17
+ export const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45_000;
18
+ function ToolLongRunningHintShell({ toolName, isRunning, children, }) {
19
+ const [showLongRunningHint, setShowLongRunningHint] = useState(false);
20
+ useEffect(() => {
21
+ if (!isRunning) {
22
+ setShowLongRunningHint(false);
23
+ return;
24
+ }
25
+ setShowLongRunningHint(false);
26
+ const timeout = window.setTimeout(() => {
27
+ setShowLongRunningHint(true);
28
+ }, TOOL_LONG_RUNNING_HINT_DELAY_MS);
29
+ return () => window.clearTimeout(timeout);
30
+ }, [isRunning, toolName]);
31
+ return (_jsxs(_Fragment, { children: [children, isRunning && showLongRunningHint && (_jsx("div", { className: "mt-0.5 px-2.5 text-[11px] leading-snug text-muted-foreground/80", children: "Still working. Large updates can take a minute or two." }))] }));
32
+ }
17
33
  function stringifyToolValue(value, pretty = false) {
18
34
  if (typeof value === "string")
19
35
  return value;
@@ -192,16 +208,17 @@ export function ToolCallDisplay({ toolName, argsText, args, result, mcpApp, chat
192
208
  // These must be separate components so hook order in ToolCallDisplayGeneric
193
209
  // is always stable (no conditional hook calls).
194
210
  const toolKind = structuredMeta?.toolKind;
211
+ const wrapToolDisplay = (children) => (_jsx(ToolLongRunningHintShell, { toolName: toolName, isRunning: isRunning, children: children }));
195
212
  if (toolKind === "bash") {
196
- return (_jsx(BashCell, { meta: structuredMeta, output: result, isRunning: isRunning }));
213
+ return wrapToolDisplay(_jsx(BashCell, { meta: structuredMeta, output: result, isRunning: isRunning }));
197
214
  }
198
215
  if (toolKind === "edit") {
199
- return (_jsx(EditCell, { meta: structuredMeta, isRunning: isRunning }));
216
+ return wrapToolDisplay(_jsx(EditCell, { meta: structuredMeta, isRunning: isRunning }));
200
217
  }
201
218
  if (toolKind === "write") {
202
- return (_jsx(WriteCell, { meta: structuredMeta, isRunning: isRunning }));
219
+ return wrapToolDisplay(_jsx(WriteCell, { meta: structuredMeta, isRunning: isRunning }));
203
220
  }
204
- return (_jsx(ToolCallDisplayGeneric, { toolName: toolName, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: isRunning, approval: approval, repeatCount: repeatCount }));
221
+ return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: isRunning, approval: approval, repeatCount: repeatCount }));
205
222
  }
206
223
  function ToolCallDisplayGeneric({ toolName, argsText, args, result, mcpApp, chatUI, isRunning, approval, repeatCount, }) {
207
224
  const streamRef = useRef(null);