@poncho-ai/cli 0.29.0 → 0.30.0

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/cli@0.29.0 build /home/runner/work/poncho-ai/poncho-ai/packages/cli
2
+ > @poncho-ai/cli@0.30.0 build /home/runner/work/poncho-ai/poncho-ai/packages/cli
3
3
  > tsup src/index.ts src/cli.ts --format esm --dts
4
4
 
5
5
  CLI Building entry: src/cli.ts, src/index.ts
@@ -9,10 +9,10 @@
9
9
  ESM Build start
10
10
  ESM dist/cli.js 94.00 B
11
11
  ESM dist/index.js 857.00 B
12
- ESM dist/run-interactive-ink-V4KWIUHB.js 56.74 KB
13
- ESM dist/chunk-3WODYQAB.js 465.31 KB
14
- ESM ⚡️ Build success in 65ms
12
+ ESM dist/run-interactive-ink-EMTC7MK7.js 56.86 KB
13
+ ESM dist/chunk-5OLH7U3C.js 467.76 KB
14
+ ESM ⚡️ Build success in 69ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 4068ms
16
+ DTS ⚡️ Build success in 4145ms
17
17
  DTS dist/cli.d.ts 20.00 B
18
18
  DTS dist/index.d.ts 3.70 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @poncho-ai/cli
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`c0ca56b`](https://github.com/cesr/poncho-ai/commit/c0ca56b54bb877d96ba8088537d6f1c7461d2a55) Thanks [@cesr](https://github.com/cesr)! - Add built-in `web_search` and `web_fetch` tools so agents can search the web and fetch page content without a browser or API keys. Remove the scaffolded `fetch-page` skill (superseded by `web_fetch`). Fix `browser_open` crash when agent projects have an older `@poncho-ai/browser` installed.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`c0ca56b`](https://github.com/cesr/poncho-ai/commit/c0ca56b54bb877d96ba8088537d6f1c7461d2a55)]:
12
+ - @poncho-ai/harness@0.28.0
13
+
3
14
  ## 0.29.0
4
15
 
5
16
  ### Minor Changes
@@ -1592,6 +1592,15 @@ var WEB_UI_STYLES = `
1592
1592
  .subagent-link:hover {
1593
1593
  text-decoration: underline;
1594
1594
  }
1595
+ .tool-link {
1596
+ color: var(--accent);
1597
+ text-decoration: none;
1598
+ font-size: 11px;
1599
+ margin-left: 4px;
1600
+ }
1601
+ .tool-link:hover {
1602
+ text-decoration: underline;
1603
+ }
1595
1604
  .subagent-callback-wrap {
1596
1605
  padding: 0;
1597
1606
  }
@@ -2112,14 +2121,24 @@ var getWebUiClientScript = (markedSource2) => `
2112
2121
  return "";
2113
2122
  }
2114
2123
  const subagentLinkRe = /\\s*\\[subagent:([^\\]]+)\\]$/;
2124
+ const toolLinkRe = /\\s*\\[link:(https?:\\/\\/[^\\]]+)\\]$/;
2115
2125
  const renderActivityItem = (item) => {
2116
- const match = item.match(subagentLinkRe);
2117
- if (match) {
2126
+ const subMatch = item.match(subagentLinkRe);
2127
+ if (subMatch) {
2118
2128
  const cleaned = escapeHtml(item.replace(subagentLinkRe, ""));
2119
- const subId = escapeHtml(match[1]);
2129
+ const subId = escapeHtml(subMatch[1]);
2120
2130
  return '<div class="tool-activity-item">' + cleaned +
2121
2131
  ' <a class="subagent-link" href="javascript:void(0)" data-subagent-id="' + subId + '">View subagent</a></div>';
2122
2132
  }
2133
+ const linkMatch = item.match(toolLinkRe);
2134
+ if (linkMatch) {
2135
+ const cleaned = escapeHtml(item.replace(toolLinkRe, ""));
2136
+ const href = escapeHtml(linkMatch[1]);
2137
+ var displayUrl = linkMatch[1].replace(/^https?:\\/\\//, "");
2138
+ if (displayUrl.length > 55) displayUrl = displayUrl.slice(0, 52) + "...";
2139
+ return '<div class="tool-activity-item">' + cleaned +
2140
+ ' <a class="tool-link" href="' + href + '" target="_blank" rel="noopener">' + escapeHtml(displayUrl) + '</a></div>';
2141
+ }
2123
2142
  return '<div class="tool-activity-item">' + escapeHtml(item) + "</div>";
2124
2143
  };
2125
2144
  const chips = hasItems
@@ -3365,10 +3384,17 @@ var getWebUiClientScript = (markedSource2) => `
3365
3384
  );
3366
3385
  const duration =
3367
3386
  typeof payload.duration === "number" ? payload.duration : null;
3368
- const detail =
3387
+ var detail =
3369
3388
  activeActivity && typeof activeActivity.detail === "string"
3370
3389
  ? activeActivity.detail.trim()
3371
3390
  : "";
3391
+ const out = payload.output && typeof payload.output === "object" ? payload.output : {};
3392
+ if (!detail && toolName === "web_search" && typeof out.query === "string") {
3393
+ detail = "\\x22" + (out.query.length > 60 ? out.query.slice(0, 57) + "..." : out.query) + "\\x22";
3394
+ }
3395
+ if (!detail && toolName === "web_fetch" && typeof out.url === "string") {
3396
+ detail = out.url;
3397
+ }
3372
3398
  const meta = [];
3373
3399
  if (duration !== null) meta.push(duration + "ms");
3374
3400
  if (detail) meta.push(detail);
@@ -3380,6 +3406,12 @@ var getWebUiClientScript = (markedSource2) => `
3380
3406
  if (toolName === "spawn_subagent" && payload.output && typeof payload.output === "object" && payload.output.subagentId) {
3381
3407
  toolText += " [subagent:" + payload.output.subagentId + "]";
3382
3408
  }
3409
+ if (toolName === "web_fetch" && typeof out.url === "string") {
3410
+ toolText += " [link:" + out.url + "]";
3411
+ }
3412
+ if (toolName === "web_search" && Array.isArray(out.results)) {
3413
+ toolText += " \\u2014 " + out.results.length + " result" + (out.results.length !== 1 ? "s" : "");
3414
+ }
3383
3415
  assistantMessage._currentTools.push(toolText);
3384
3416
  assistantMessage.metadata.toolActivity.push(toolText);
3385
3417
  if (typeof payload.outputTokenEstimate === "number" && payload.outputTokenEstimate > 0 && state.contextWindow > 0) {
@@ -3819,6 +3851,28 @@ var getWebUiClientScript = (markedSource2) => `
3819
3851
  };
3820
3852
  }
3821
3853
 
3854
+ if (toolName === "web_search") {
3855
+ const query = getStringInputField(input, "query");
3856
+ const short = query && query.length > 60 ? query.slice(0, 57) + "..." : query;
3857
+ return {
3858
+ kind: "tool",
3859
+ tool: toolName,
3860
+ label: "Searching" + (short ? " \\x22" + short + "\\x22" : ""),
3861
+ detail: short ? "\\x22" + short + "\\x22" : "",
3862
+ };
3863
+ }
3864
+
3865
+ if (toolName === "web_fetch") {
3866
+ const url = getStringInputField(input, "url");
3867
+ const short = url && url.length > 60 ? url.slice(0, 57) + "..." : url;
3868
+ return {
3869
+ kind: "tool",
3870
+ tool: toolName,
3871
+ label: "Fetching " + (short || "page"),
3872
+ detail: url || "",
3873
+ };
3874
+ }
3875
+
3822
3876
  return {
3823
3877
  kind: "tool",
3824
3878
  tool: toolName,
@@ -4206,10 +4260,17 @@ var getWebUiClientScript = (markedSource2) => `
4206
4260
  toolName,
4207
4261
  );
4208
4262
  const duration = typeof payload.duration === "number" ? payload.duration : null;
4209
- const detail =
4263
+ var detail =
4210
4264
  activeActivity && typeof activeActivity.detail === "string"
4211
4265
  ? activeActivity.detail.trim()
4212
4266
  : "";
4267
+ const out = payload.output && typeof payload.output === "object" ? payload.output : {};
4268
+ if (!detail && toolName === "web_search" && typeof out.query === "string") {
4269
+ detail = "\\x22" + (out.query.length > 60 ? out.query.slice(0, 57) + "..." : out.query) + "\\x22";
4270
+ }
4271
+ if (!detail && toolName === "web_fetch" && typeof out.url === "string") {
4272
+ detail = out.url;
4273
+ }
4213
4274
  const meta = [];
4214
4275
  if (duration !== null) meta.push(duration + "ms");
4215
4276
  if (detail) meta.push(detail);
@@ -4218,6 +4279,12 @@ var getWebUiClientScript = (markedSource2) => `
4218
4279
  if (toolName === "spawn_subagent" && payload.output && typeof payload.output === "object" && payload.output.subagentId) {
4219
4280
  toolText += " [subagent:" + payload.output.subagentId + "]";
4220
4281
  }
4282
+ if (toolName === "web_fetch" && typeof out.url === "string") {
4283
+ toolText += " [link:" + out.url + "]";
4284
+ }
4285
+ if (toolName === "web_search" && Array.isArray(out.results)) {
4286
+ toolText += " \\u2014 " + out.results.length + " result" + (out.results.length !== 1 ? "s" : "");
4287
+ }
4221
4288
  assistantMessage._currentTools.push(toolText);
4222
4289
  if (!assistantMessage.metadata) assistantMessage.metadata = {};
4223
4290
  if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
@@ -7291,14 +7358,10 @@ ${name}/
7291
7358
  \u251C\u2500\u2500 tests/
7292
7359
  \u2502 \u2514\u2500\u2500 basic.yaml # Test suite
7293
7360
  \u2514\u2500\u2500 skills/
7294
- \u251C\u2500\u2500 starter/
7295
- \u2502 \u251C\u2500\u2500 SKILL.md
7296
- \u2502 \u2514\u2500\u2500 scripts/
7297
- \u2502 \u2514\u2500\u2500 starter-echo.ts
7298
- \u2514\u2500\u2500 fetch-page/
7361
+ \u2514\u2500\u2500 starter/
7299
7362
  \u251C\u2500\u2500 SKILL.md
7300
7363
  \u2514\u2500\u2500 scripts/
7301
- \u2514\u2500\u2500 fetch-page.ts
7364
+ \u2514\u2500\u2500 starter-echo.ts
7302
7365
  \`\`\`
7303
7366
 
7304
7367
  ## Cron Jobs
@@ -7476,67 +7539,6 @@ var SKILL_TOOL_TEMPLATE = `export default async function run(input) {
7476
7539
  return { echoed: message };
7477
7540
  }
7478
7541
  `;
7479
- var FETCH_PAGE_SKILL_TEMPLATE = `---
7480
- name: fetch-page
7481
- description: Fetch a web page and return its text content
7482
- allowed-tools:
7483
- - ./scripts/fetch-page.ts
7484
- ---
7485
-
7486
- # Fetch Page
7487
-
7488
- Fetches a URL and returns the page body as plain text (HTML tags stripped).
7489
-
7490
- ## Usage
7491
-
7492
- Call \`run_skill_script\` with:
7493
- - **skill**: \`fetch-page\`
7494
- - **script**: \`./scripts/fetch-page.ts\`
7495
- - **input**: \`{ "url": "https://example.com" }\`
7496
-
7497
- The script returns \`{ url, status, content }\` where \`content\` is the
7498
- text-only body (capped at ~32 000 chars to stay context-friendly).
7499
- `;
7500
- var FETCH_PAGE_SCRIPT_TEMPLATE = `export default async function run(input) {
7501
- const url = typeof input?.url === "string" ? input.url.trim() : "";
7502
- if (!url) {
7503
- return { error: "A \\"url\\" string is required." };
7504
- }
7505
-
7506
- const MAX_LENGTH = 32_000;
7507
-
7508
- const response = await fetch(url, {
7509
- headers: { "User-Agent": "poncho-fetch-page/1.0" },
7510
- redirect: "follow",
7511
- });
7512
-
7513
- if (!response.ok) {
7514
- return { url, status: response.status, error: response.statusText };
7515
- }
7516
-
7517
- const html = await response.text();
7518
-
7519
- // Lightweight HTML-to-text: strip tags, collapse whitespace.
7520
- const text = html
7521
- .replace(/<script[\\s\\S]*?<\\/script>/gi, "")
7522
- .replace(/<style[\\s\\S]*?<\\/style>/gi, "")
7523
- .replace(/<[^>]+>/g, " ")
7524
- .replace(/&nbsp;/gi, " ")
7525
- .replace(/&amp;/gi, "&")
7526
- .replace(/&lt;/gi, "<")
7527
- .replace(/&gt;/gi, ">")
7528
- .replace(/&quot;/gi, '"')
7529
- .replace(/&#39;/gi, "'")
7530
- .replace(/\\s+/g, " ")
7531
- .trim();
7532
-
7533
- const content = text.length > MAX_LENGTH
7534
- ? text.slice(0, MAX_LENGTH) + "\u2026 (truncated)"
7535
- : text;
7536
-
7537
- return { url, status: response.status, content };
7538
- }
7539
- `;
7540
7542
  var ensureFile = async (path, content) => {
7541
7543
  await mkdir3(dirname4(path), { recursive: true });
7542
7544
  await writeFile3(path, content, { encoding: "utf8", flag: "wx" });
@@ -8003,9 +8005,7 @@ var initProject = async (projectName, options) => {
8003
8005
  { path: ".gitignore", content: GITIGNORE_TEMPLATE },
8004
8006
  { path: "tests/basic.yaml", content: TEST_TEMPLATE },
8005
8007
  { path: "skills/starter/SKILL.md", content: SKILL_TEMPLATE },
8006
- { path: "skills/starter/scripts/starter-echo.ts", content: SKILL_TOOL_TEMPLATE },
8007
- { path: "skills/fetch-page/SKILL.md", content: FETCH_PAGE_SKILL_TEMPLATE },
8008
- { path: "skills/fetch-page/scripts/fetch-page.ts", content: FETCH_PAGE_SCRIPT_TEMPLATE }
8008
+ { path: "skills/starter/scripts/starter-echo.ts", content: SKILL_TOOL_TEMPLATE }
8009
8009
  ];
8010
8010
  if (onboarding.envFile) {
8011
8011
  scaffoldFiles.push({ path: ".env", content: onboarding.envFile });
@@ -8363,6 +8363,9 @@ data: ${JSON.stringify(statusPayload)}
8363
8363
  if (currentTools.length > 0) {
8364
8364
  sections.push({ type: "tools", content: currentTools });
8365
8365
  currentTools = [];
8366
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
8367
+ assistantResponse += " ";
8368
+ }
8366
8369
  }
8367
8370
  assistantResponse += event.content;
8368
8371
  currentText += event.content;
@@ -8478,6 +8481,9 @@ data: ${JSON.stringify(statusPayload)}
8478
8481
  if (currentTools.length > 0) {
8479
8482
  sections.push({ type: "tools", content: currentTools });
8480
8483
  currentTools = [];
8484
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
8485
+ assistantResponse += " ";
8486
+ }
8481
8487
  }
8482
8488
  assistantResponse += resumeEvent.content;
8483
8489
  currentText += resumeEvent.content;
@@ -8725,6 +8731,9 @@ ${resultBody}`,
8725
8731
  if (currentTools.length > 0) {
8726
8732
  sections.push({ type: "tools", content: currentTools });
8727
8733
  currentTools = [];
8734
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
8735
+ assistantResponse += " ";
8736
+ }
8728
8737
  }
8729
8738
  assistantResponse += event.content;
8730
8739
  currentText += event.content;
@@ -9011,6 +9020,9 @@ ${resultBody}`,
9011
9020
  if (currentTools.length > 0) {
9012
9021
  sections.push({ type: "tools", content: currentTools });
9013
9022
  currentTools = [];
9023
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
9024
+ assistantResponse += " ";
9025
+ }
9014
9026
  }
9015
9027
  assistantResponse += event.content;
9016
9028
  currentText += event.content;
@@ -9294,6 +9306,9 @@ ${resultBody}`,
9294
9306
  if (currentTools.length > 0) {
9295
9307
  sections.push({ type: "tools", content: currentTools });
9296
9308
  currentTools = [];
9309
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
9310
+ assistantResponse += " ";
9311
+ }
9297
9312
  }
9298
9313
  assistantResponse += event.content;
9299
9314
  currentText += event.content;
@@ -10821,6 +10836,9 @@ data: ${JSON.stringify(frame)}
10821
10836
  if (currentTools.length > 0) {
10822
10837
  sections.push({ type: "tools", content: currentTools });
10823
10838
  currentTools = [];
10839
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
10840
+ assistantResponse += " ";
10841
+ }
10824
10842
  }
10825
10843
  assistantResponse += event.content;
10826
10844
  currentText += event.content;
@@ -11218,6 +11236,9 @@ ${cronJob.task}`;
11218
11236
  if (currentTools.length > 0) {
11219
11237
  sections.push({ type: "tools", content: currentTools });
11220
11238
  currentTools = [];
11239
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
11240
+ assistantResponse += " ";
11241
+ }
11221
11242
  }
11222
11243
  assistantResponse += event.content;
11223
11244
  currentText += event.content;
@@ -11388,6 +11409,9 @@ var startDevServer = async (port, options) => {
11388
11409
  if (currentTools.length > 0) {
11389
11410
  sections.push({ type: "tools", content: currentTools });
11390
11411
  currentTools = [];
11412
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
11413
+ assistantResponse += " ";
11414
+ }
11391
11415
  }
11392
11416
  assistantResponse += event.content;
11393
11417
  currentText += event.content;
@@ -11652,7 +11676,7 @@ var runInteractive = async (workingDir, params) => {
11652
11676
  await harness.initialize();
11653
11677
  const identity = await ensureAgentIdentity2(workingDir);
11654
11678
  try {
11655
- const { runInteractiveInk } = await import("./run-interactive-ink-V4KWIUHB.js");
11679
+ const { runInteractiveInk } = await import("./run-interactive-ink-EMTC7MK7.js");
11656
11680
  await runInteractiveInk({
11657
11681
  harness,
11658
11682
  params,
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-3WODYQAB.js";
4
+ } from "./chunk-5OLH7U3C.js";
5
5
 
6
6
  // src/cli.ts
7
7
  void main();
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  runTests,
24
24
  startDevServer,
25
25
  updateAgentGuidance
26
- } from "./chunk-3WODYQAB.js";
26
+ } from "./chunk-5OLH7U3C.js";
27
27
  export {
28
28
  addSkill,
29
29
  buildCli,
@@ -2,7 +2,7 @@ import {
2
2
  consumeFirstRunIntro,
3
3
  inferConversationTitle,
4
4
  resolveHarnessEnvironment
5
- } from "./chunk-3WODYQAB.js";
5
+ } from "./chunk-5OLH7U3C.js";
6
6
 
7
7
  // src/run-interactive-ink.ts
8
8
  import * as readline from "readline";
@@ -1882,6 +1882,9 @@ var runInteractiveInk = async ({
1882
1882
  if (currentTools.length > 0) {
1883
1883
  sections.push({ type: "tools", content: currentTools });
1884
1884
  currentTools = [];
1885
+ if (responseText.length > 0 && !/\s$/.test(responseText)) {
1886
+ responseText += " ";
1887
+ }
1885
1888
  }
1886
1889
  responseText += event.content;
1887
1890
  streamedText += event.content;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/cli",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "CLI for building and deploying AI agents",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  "react": "^19.2.4",
28
28
  "react-devtools-core": "^6.1.5",
29
29
  "yaml": "^2.8.1",
30
- "@poncho-ai/harness": "0.27.0",
30
+ "@poncho-ai/harness": "0.28.0",
31
31
  "@poncho-ai/messaging": "0.7.1",
32
32
  "@poncho-ai/sdk": "1.6.0"
33
33
  },
package/src/index.ts CHANGED
@@ -586,14 +586,10 @@ ${name}/
586
586
  ├── tests/
587
587
  │ └── basic.yaml # Test suite
588
588
  └── skills/
589
- ├── starter/
590
- │ ├── SKILL.md
591
- │ └── scripts/
592
- │ └── starter-echo.ts
593
- └── fetch-page/
589
+ └── starter/
594
590
  ├── SKILL.md
595
591
  └── scripts/
596
- └── fetch-page.ts
592
+ └── starter-echo.ts
597
593
  \`\`\`
598
594
 
599
595
  ## Cron Jobs
@@ -776,69 +772,6 @@ const SKILL_TOOL_TEMPLATE = `export default async function run(input) {
776
772
  }
777
773
  `;
778
774
 
779
- const FETCH_PAGE_SKILL_TEMPLATE = `---
780
- name: fetch-page
781
- description: Fetch a web page and return its text content
782
- allowed-tools:
783
- - ./scripts/fetch-page.ts
784
- ---
785
-
786
- # Fetch Page
787
-
788
- Fetches a URL and returns the page body as plain text (HTML tags stripped).
789
-
790
- ## Usage
791
-
792
- Call \`run_skill_script\` with:
793
- - **skill**: \`fetch-page\`
794
- - **script**: \`./scripts/fetch-page.ts\`
795
- - **input**: \`{ "url": "https://example.com" }\`
796
-
797
- The script returns \`{ url, status, content }\` where \`content\` is the
798
- text-only body (capped at ~32 000 chars to stay context-friendly).
799
- `;
800
-
801
- const FETCH_PAGE_SCRIPT_TEMPLATE = `export default async function run(input) {
802
- const url = typeof input?.url === "string" ? input.url.trim() : "";
803
- if (!url) {
804
- return { error: "A \\"url\\" string is required." };
805
- }
806
-
807
- const MAX_LENGTH = 32_000;
808
-
809
- const response = await fetch(url, {
810
- headers: { "User-Agent": "poncho-fetch-page/1.0" },
811
- redirect: "follow",
812
- });
813
-
814
- if (!response.ok) {
815
- return { url, status: response.status, error: response.statusText };
816
- }
817
-
818
- const html = await response.text();
819
-
820
- // Lightweight HTML-to-text: strip tags, collapse whitespace.
821
- const text = html
822
- .replace(/<script[\\s\\S]*?<\\/script>/gi, "")
823
- .replace(/<style[\\s\\S]*?<\\/style>/gi, "")
824
- .replace(/<[^>]+>/g, " ")
825
- .replace(/&nbsp;/gi, " ")
826
- .replace(/&amp;/gi, "&")
827
- .replace(/&lt;/gi, "<")
828
- .replace(/&gt;/gi, ">")
829
- .replace(/&quot;/gi, '"')
830
- .replace(/&#39;/gi, "'")
831
- .replace(/\\s+/g, " ")
832
- .trim();
833
-
834
- const content = text.length > MAX_LENGTH
835
- ? text.slice(0, MAX_LENGTH) + "… (truncated)"
836
- : text;
837
-
838
- return { url, status: response.status, content };
839
- }
840
- `;
841
-
842
775
  const ensureFile = async (path: string, content: string): Promise<void> => {
843
776
  await mkdir(dirname(path), { recursive: true });
844
777
  await writeFile(path, content, { encoding: "utf8", flag: "wx" });
@@ -1375,8 +1308,6 @@ export const initProject = async (
1375
1308
  { path: "tests/basic.yaml", content: TEST_TEMPLATE },
1376
1309
  { path: "skills/starter/SKILL.md", content: SKILL_TEMPLATE },
1377
1310
  { path: "skills/starter/scripts/starter-echo.ts", content: SKILL_TOOL_TEMPLATE },
1378
- { path: "skills/fetch-page/SKILL.md", content: FETCH_PAGE_SKILL_TEMPLATE },
1379
- { path: "skills/fetch-page/scripts/fetch-page.ts", content: FETCH_PAGE_SCRIPT_TEMPLATE },
1380
1311
  ];
1381
1312
  if (onboarding.envFile) {
1382
1313
  scaffoldFiles.push({ path: ".env", content: onboarding.envFile });
@@ -1808,6 +1739,9 @@ export const createRequestHandler = async (options?: {
1808
1739
  if (currentTools.length > 0) {
1809
1740
  sections.push({ type: "tools", content: currentTools });
1810
1741
  currentTools = [];
1742
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
1743
+ assistantResponse += " ";
1744
+ }
1811
1745
  }
1812
1746
  assistantResponse += event.content;
1813
1747
  currentText += event.content;
@@ -1930,6 +1864,9 @@ export const createRequestHandler = async (options?: {
1930
1864
  if (currentTools.length > 0) {
1931
1865
  sections.push({ type: "tools", content: currentTools });
1932
1866
  currentTools = [];
1867
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
1868
+ assistantResponse += " ";
1869
+ }
1933
1870
  }
1934
1871
  assistantResponse += resumeEvent.content;
1935
1872
  currentText += resumeEvent.content;
@@ -2203,6 +2140,9 @@ export const createRequestHandler = async (options?: {
2203
2140
  if (currentTools.length > 0) {
2204
2141
  sections.push({ type: "tools", content: currentTools });
2205
2142
  currentTools = [];
2143
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
2144
+ assistantResponse += " ";
2145
+ }
2206
2146
  }
2207
2147
  assistantResponse += event.content;
2208
2148
  currentText += event.content;
@@ -2534,6 +2474,9 @@ export const createRequestHandler = async (options?: {
2534
2474
  if (currentTools.length > 0) {
2535
2475
  sections.push({ type: "tools", content: currentTools });
2536
2476
  currentTools = [];
2477
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
2478
+ assistantResponse += " ";
2479
+ }
2537
2480
  }
2538
2481
  assistantResponse += event.content;
2539
2482
  currentText += event.content;
@@ -2853,6 +2796,9 @@ export const createRequestHandler = async (options?: {
2853
2796
  if (currentTools.length > 0) {
2854
2797
  sections.push({ type: "tools", content: currentTools });
2855
2798
  currentTools = [];
2799
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
2800
+ assistantResponse += " ";
2801
+ }
2856
2802
  }
2857
2803
  assistantResponse += event.content;
2858
2804
  currentText += event.content;
@@ -4587,6 +4533,9 @@ export const createRequestHandler = async (options?: {
4587
4533
  if (currentTools.length > 0) {
4588
4534
  sections.push({ type: "tools", content: currentTools });
4589
4535
  currentTools = [];
4536
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
4537
+ assistantResponse += " ";
4538
+ }
4590
4539
  }
4591
4540
  assistantResponse += event.content;
4592
4541
  currentText += event.content;
@@ -5030,6 +4979,9 @@ export const createRequestHandler = async (options?: {
5030
4979
  if (currentTools.length > 0) {
5031
4980
  sections.push({ type: "tools", content: currentTools });
5032
4981
  currentTools = [];
4982
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
4983
+ assistantResponse += " ";
4984
+ }
5033
4985
  }
5034
4986
  assistantResponse += event.content;
5035
4987
  currentText += event.content;
@@ -5241,6 +5193,9 @@ export const startDevServer = async (
5241
5193
  if (currentTools.length > 0) {
5242
5194
  sections.push({ type: "tools", content: currentTools });
5243
5195
  currentTools = [];
5196
+ if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
5197
+ assistantResponse += " ";
5198
+ }
5244
5199
  }
5245
5200
  assistantResponse += event.content;
5246
5201
  currentText += event.content;
@@ -567,6 +567,9 @@ export const runInteractiveInk = async ({
567
567
  if (currentTools.length > 0) {
568
568
  sections.push({ type: "tools", content: currentTools });
569
569
  currentTools = [];
570
+ if (responseText.length > 0 && !/\s$/.test(responseText)) {
571
+ responseText += " ";
572
+ }
570
573
  }
571
574
  responseText += event.content;
572
575
  streamedText += event.content;
@@ -342,14 +342,24 @@ export const getWebUiClientScript = (markedSource: string): string => `
342
342
  return "";
343
343
  }
344
344
  const subagentLinkRe = /\\s*\\[subagent:([^\\]]+)\\]$/;
345
+ const toolLinkRe = /\\s*\\[link:(https?:\\/\\/[^\\]]+)\\]$/;
345
346
  const renderActivityItem = (item) => {
346
- const match = item.match(subagentLinkRe);
347
- if (match) {
347
+ const subMatch = item.match(subagentLinkRe);
348
+ if (subMatch) {
348
349
  const cleaned = escapeHtml(item.replace(subagentLinkRe, ""));
349
- const subId = escapeHtml(match[1]);
350
+ const subId = escapeHtml(subMatch[1]);
350
351
  return '<div class="tool-activity-item">' + cleaned +
351
352
  ' <a class="subagent-link" href="javascript:void(0)" data-subagent-id="' + subId + '">View subagent</a></div>';
352
353
  }
354
+ const linkMatch = item.match(toolLinkRe);
355
+ if (linkMatch) {
356
+ const cleaned = escapeHtml(item.replace(toolLinkRe, ""));
357
+ const href = escapeHtml(linkMatch[1]);
358
+ var displayUrl = linkMatch[1].replace(/^https?:\\/\\//, "");
359
+ if (displayUrl.length > 55) displayUrl = displayUrl.slice(0, 52) + "...";
360
+ return '<div class="tool-activity-item">' + cleaned +
361
+ ' <a class="tool-link" href="' + href + '" target="_blank" rel="noopener">' + escapeHtml(displayUrl) + '</a></div>';
362
+ }
353
363
  return '<div class="tool-activity-item">' + escapeHtml(item) + "</div>";
354
364
  };
355
365
  const chips = hasItems
@@ -1595,10 +1605,17 @@ export const getWebUiClientScript = (markedSource: string): string => `
1595
1605
  );
1596
1606
  const duration =
1597
1607
  typeof payload.duration === "number" ? payload.duration : null;
1598
- const detail =
1608
+ var detail =
1599
1609
  activeActivity && typeof activeActivity.detail === "string"
1600
1610
  ? activeActivity.detail.trim()
1601
1611
  : "";
1612
+ const out = payload.output && typeof payload.output === "object" ? payload.output : {};
1613
+ if (!detail && toolName === "web_search" && typeof out.query === "string") {
1614
+ detail = "\\x22" + (out.query.length > 60 ? out.query.slice(0, 57) + "..." : out.query) + "\\x22";
1615
+ }
1616
+ if (!detail && toolName === "web_fetch" && typeof out.url === "string") {
1617
+ detail = out.url;
1618
+ }
1602
1619
  const meta = [];
1603
1620
  if (duration !== null) meta.push(duration + "ms");
1604
1621
  if (detail) meta.push(detail);
@@ -1610,6 +1627,12 @@ export const getWebUiClientScript = (markedSource: string): string => `
1610
1627
  if (toolName === "spawn_subagent" && payload.output && typeof payload.output === "object" && payload.output.subagentId) {
1611
1628
  toolText += " [subagent:" + payload.output.subagentId + "]";
1612
1629
  }
1630
+ if (toolName === "web_fetch" && typeof out.url === "string") {
1631
+ toolText += " [link:" + out.url + "]";
1632
+ }
1633
+ if (toolName === "web_search" && Array.isArray(out.results)) {
1634
+ toolText += " \\u2014 " + out.results.length + " result" + (out.results.length !== 1 ? "s" : "");
1635
+ }
1613
1636
  assistantMessage._currentTools.push(toolText);
1614
1637
  assistantMessage.metadata.toolActivity.push(toolText);
1615
1638
  if (typeof payload.outputTokenEstimate === "number" && payload.outputTokenEstimate > 0 && state.contextWindow > 0) {
@@ -2049,6 +2072,28 @@ export const getWebUiClientScript = (markedSource: string): string => `
2049
2072
  };
2050
2073
  }
2051
2074
 
2075
+ if (toolName === "web_search") {
2076
+ const query = getStringInputField(input, "query");
2077
+ const short = query && query.length > 60 ? query.slice(0, 57) + "..." : query;
2078
+ return {
2079
+ kind: "tool",
2080
+ tool: toolName,
2081
+ label: "Searching" + (short ? " \\x22" + short + "\\x22" : ""),
2082
+ detail: short ? "\\x22" + short + "\\x22" : "",
2083
+ };
2084
+ }
2085
+
2086
+ if (toolName === "web_fetch") {
2087
+ const url = getStringInputField(input, "url");
2088
+ const short = url && url.length > 60 ? url.slice(0, 57) + "..." : url;
2089
+ return {
2090
+ kind: "tool",
2091
+ tool: toolName,
2092
+ label: "Fetching " + (short || "page"),
2093
+ detail: url || "",
2094
+ };
2095
+ }
2096
+
2052
2097
  return {
2053
2098
  kind: "tool",
2054
2099
  tool: toolName,
@@ -2436,10 +2481,17 @@ export const getWebUiClientScript = (markedSource: string): string => `
2436
2481
  toolName,
2437
2482
  );
2438
2483
  const duration = typeof payload.duration === "number" ? payload.duration : null;
2439
- const detail =
2484
+ var detail =
2440
2485
  activeActivity && typeof activeActivity.detail === "string"
2441
2486
  ? activeActivity.detail.trim()
2442
2487
  : "";
2488
+ const out = payload.output && typeof payload.output === "object" ? payload.output : {};
2489
+ if (!detail && toolName === "web_search" && typeof out.query === "string") {
2490
+ detail = "\\x22" + (out.query.length > 60 ? out.query.slice(0, 57) + "..." : out.query) + "\\x22";
2491
+ }
2492
+ if (!detail && toolName === "web_fetch" && typeof out.url === "string") {
2493
+ detail = out.url;
2494
+ }
2443
2495
  const meta = [];
2444
2496
  if (duration !== null) meta.push(duration + "ms");
2445
2497
  if (detail) meta.push(detail);
@@ -2448,6 +2500,12 @@ export const getWebUiClientScript = (markedSource: string): string => `
2448
2500
  if (toolName === "spawn_subagent" && payload.output && typeof payload.output === "object" && payload.output.subagentId) {
2449
2501
  toolText += " [subagent:" + payload.output.subagentId + "]";
2450
2502
  }
2503
+ if (toolName === "web_fetch" && typeof out.url === "string") {
2504
+ toolText += " [link:" + out.url + "]";
2505
+ }
2506
+ if (toolName === "web_search" && Array.isArray(out.results)) {
2507
+ toolText += " \\u2014 " + out.results.length + " result" + (out.results.length !== 1 ? "s" : "");
2508
+ }
2451
2509
  assistantMessage._currentTools.push(toolText);
2452
2510
  if (!assistantMessage.metadata) assistantMessage.metadata = {};
2453
2511
  if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
@@ -1551,6 +1551,15 @@ export const WEB_UI_STYLES = `
1551
1551
  .subagent-link:hover {
1552
1552
  text-decoration: underline;
1553
1553
  }
1554
+ .tool-link {
1555
+ color: var(--accent);
1556
+ text-decoration: none;
1557
+ font-size: 11px;
1558
+ margin-left: 4px;
1559
+ }
1560
+ .tool-link:hover {
1561
+ text-decoration: underline;
1562
+ }
1554
1563
  .subagent-callback-wrap {
1555
1564
  padding: 0;
1556
1565
  }