unbrowse 3.1.0-experiments.f9b4a71 → 3.2.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.
Files changed (110) hide show
  1. package/dist/cli.js +80 -46
  2. package/dist/index.js +2 -6
  3. package/dist/mcp.js +73 -22
  4. package/dist/server.js +25811 -0
  5. package/package.json +1 -2
  6. package/vendor/kuri/darwin-arm64/kuri +0 -0
  7. package/vendor/kuri/darwin-x64/kuri +0 -0
  8. package/vendor/kuri/linux-arm64/kuri +0 -0
  9. package/vendor/kuri/linux-x64/kuri +0 -0
  10. package/vendor/kuri/manifest.json +7 -10
  11. package/runtime-src/agent-outcome.ts +0 -166
  12. package/runtime-src/analytics-session.ts +0 -55
  13. package/runtime-src/api/browse-index.ts +0 -339
  14. package/runtime-src/api/browse-session.ts +0 -572
  15. package/runtime-src/api/browse-submit-prereqs.ts +0 -48
  16. package/runtime-src/api/browse-submit.ts +0 -1184
  17. package/runtime-src/api/routes.ts +0 -1855
  18. package/runtime-src/auth/browser-cookies.ts +0 -423
  19. package/runtime-src/auth/index.ts +0 -535
  20. package/runtime-src/auth/runtime.ts +0 -116
  21. package/runtime-src/browser/index.ts +0 -660
  22. package/runtime-src/browser/types.ts +0 -41
  23. package/runtime-src/build-info.generated.ts +0 -6
  24. package/runtime-src/capture/index.ts +0 -1907
  25. package/runtime-src/capture/prefetch.ts +0 -95
  26. package/runtime-src/capture/rsc.ts +0 -45
  27. package/runtime-src/cli/shortcuts.ts +0 -273
  28. package/runtime-src/cli.ts +0 -1734
  29. package/runtime-src/client/graph-client.ts +0 -100
  30. package/runtime-src/client/index.ts +0 -1425
  31. package/runtime-src/debug-trace.ts +0 -18
  32. package/runtime-src/domain.ts +0 -38
  33. package/runtime-src/execution/index.ts +0 -3407
  34. package/runtime-src/execution/retry.ts +0 -46
  35. package/runtime-src/execution/robots.ts +0 -167
  36. package/runtime-src/execution/search-forms.ts +0 -188
  37. package/runtime-src/execution/token-resolver.ts +0 -122
  38. package/runtime-src/extraction/index.ts +0 -1507
  39. package/runtime-src/foundry/publish-bundle.ts +0 -392
  40. package/runtime-src/graph/agent-augment.ts +0 -315
  41. package/runtime-src/graph/index.ts +0 -1532
  42. package/runtime-src/graph/local-fixtures.ts +0 -393
  43. package/runtime-src/graph/local-harness.ts +0 -646
  44. package/runtime-src/graph/planner.ts +0 -411
  45. package/runtime-src/graph/session.ts +0 -294
  46. package/runtime-src/graph/trace-store.ts +0 -136
  47. package/runtime-src/impact-log.ts +0 -227
  48. package/runtime-src/index.ts +0 -24
  49. package/runtime-src/indexer/index.ts +0 -465
  50. package/runtime-src/intent-match.ts +0 -1515
  51. package/runtime-src/kuri/client.ts +0 -1839
  52. package/runtime-src/logger.ts +0 -30
  53. package/runtime-src/marketplace/index.ts +0 -111
  54. package/runtime-src/mcp.ts +0 -1911
  55. package/runtime-src/orchestrator/browser-agent.ts +0 -375
  56. package/runtime-src/orchestrator/dag-advisor.ts +0 -59
  57. package/runtime-src/orchestrator/dag-feedback.ts +0 -257
  58. package/runtime-src/orchestrator/first-pass-action.ts +0 -403
  59. package/runtime-src/orchestrator/index.ts +0 -4480
  60. package/runtime-src/orchestrator/passive-publish.ts +0 -187
  61. package/runtime-src/orchestrator/timing-economics.ts +0 -80
  62. package/runtime-src/payments/cascade.ts +0 -137
  63. package/runtime-src/payments/index.ts +0 -270
  64. package/runtime-src/payments/lobster-pay.ts +0 -182
  65. package/runtime-src/payments/wallet.ts +0 -98
  66. package/runtime-src/publish/review-context.ts +0 -93
  67. package/runtime-src/publish/sanitize.ts +0 -197
  68. package/runtime-src/publish/schema-review.ts +0 -192
  69. package/runtime-src/publish-admission.ts +0 -388
  70. package/runtime-src/ratelimit/index.ts +0 -23
  71. package/runtime-src/reverse-engineer/bundle-scanner.ts +0 -127
  72. package/runtime-src/reverse-engineer/description-prompt.ts +0 -213
  73. package/runtime-src/reverse-engineer/index.ts +0 -1551
  74. package/runtime-src/reverse-engineer/token-sources.ts +0 -357
  75. package/runtime-src/router.ts +0 -17
  76. package/runtime-src/routing-telemetry.ts +0 -395
  77. package/runtime-src/runtime/browser-access.ts +0 -11
  78. package/runtime-src/runtime/browser-auth.ts +0 -12
  79. package/runtime-src/runtime/browser-host.ts +0 -48
  80. package/runtime-src/runtime/lifecycle.ts +0 -17
  81. package/runtime-src/runtime/local-server.ts +0 -311
  82. package/runtime-src/runtime/paths.ts +0 -99
  83. package/runtime-src/runtime/setup.ts +0 -251
  84. package/runtime-src/runtime/supervisor.ts +0 -69
  85. package/runtime-src/runtime/update-hints.ts +0 -351
  86. package/runtime-src/server.ts +0 -100
  87. package/runtime-src/session-logs.ts +0 -142
  88. package/runtime-src/settings.ts +0 -221
  89. package/runtime-src/single-binary.ts +0 -143
  90. package/runtime-src/site-policy.ts +0 -54
  91. package/runtime-src/stale-cleanup-runner.ts +0 -144
  92. package/runtime-src/stale-cleanup.ts +0 -133
  93. package/runtime-src/telemetry-attribution.ts +0 -120
  94. package/runtime-src/telemetry.ts +0 -253
  95. package/runtime-src/template-params.ts +0 -141
  96. package/runtime-src/transform/drift.ts +0 -60
  97. package/runtime-src/transform/index.ts +0 -277
  98. package/runtime-src/types/index.ts +0 -1
  99. package/runtime-src/types/skill.ts +0 -931
  100. package/runtime-src/vault/index.ts +0 -196
  101. package/runtime-src/verification/auth-gate.ts +0 -8
  102. package/runtime-src/verification/candidates.ts +0 -27
  103. package/runtime-src/verification/index.ts +0 -120
  104. package/runtime-src/verification/matrix.ts +0 -30
  105. package/runtime-src/version.ts +0 -148
  106. package/runtime-src/workflow/artifact.ts +0 -161
  107. package/runtime-src/workflow/compile.ts +0 -808
  108. package/runtime-src/workflow/publish.ts +0 -225
  109. package/runtime-src/workflow/runtime.ts +0 -213
  110. package/vendor/kuri/win-x64/kuri.exe +0 -0
package/dist/cli.js CHANGED
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
31
31
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
32
32
 
33
33
  // ../../src/build-info.generated.ts
34
- var BUILD_RELEASE_VERSION = "3.1.0-experiments.f9b4a71", BUILD_GIT_SHA = "f9b4a71af04a", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuZjliNGE3MSIsImdpdF9zaGEiOiJmOWI0YTcxYWYwNGEiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QGY5YjRhNzFhZjA0YSIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDVUMTM6MjI6NDYuMTM0WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "_s5u-H651GXE5q8GnEVaA7daM2EoV_MS-tJDHAUiJIg", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
34
+ var BUILD_RELEASE_VERSION = "3.2.0", BUILD_GIT_SHA = "c3fc3f822751", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjAiLCJnaXRfc2hhIjoiYzNmYzNmODIyNzUxIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0BjM2ZjM2Y4MjI3NTEiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA2VDA1OjA2OjAxLjIwMFoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "xCBEHEB2UsniVYLfzuTpoXlcomZHL2pXht-7Ii1e7mM", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -586,7 +586,7 @@ var init_logger = __esm(() => {
586
586
 
587
587
  // ../../src/kuri/client.ts
588
588
  import { execFileSync as execFileSync2, spawn as spawn2 } from "node:child_process";
589
- import { existsSync as existsSync8 } from "node:fs";
589
+ import { existsSync as existsSync9 } from "node:fs";
590
590
  import path5 from "node:path";
591
591
  function createBrokerState(port = KURI_DEFAULT_PORT) {
592
592
  return {
@@ -661,7 +661,7 @@ function findKuriBinary() {
661
661
  if (process.env.KURI_BIN)
662
662
  return process.env.KURI_BIN;
663
663
  const candidates = getKuriBinaryCandidates();
664
- return candidates.find((candidate) => existsSync8(candidate)) ?? candidates[0] ?? kuriBinaryName();
664
+ return candidates.find((candidate) => existsSync9(candidate)) ?? candidates[0] ?? kuriBinaryName();
665
665
  }
666
666
  var KURI_DEFAULT_PORT = 7700, defaultBrokerState, brokerClients;
667
667
  var init_client2 = __esm(() => {
@@ -796,6 +796,15 @@ var init_bundle_scanner = __esm(() => {
796
796
  init_logger();
797
797
  });
798
798
 
799
+ // ../../src/reverse-engineer/token-sources.ts
800
+ var init_token_sources = () => {};
801
+
802
+ // ../../src/execution/token-resolver.ts
803
+ var init_token_resolver = __esm(() => {
804
+ init_token_sources();
805
+ init_client2();
806
+ });
807
+
799
808
  // ../../src/vault/index.ts
800
809
  import { join as join7 } from "path";
801
810
  import { homedir as homedir5 } from "os";
@@ -899,18 +908,6 @@ var init_extraction = __esm(() => {
899
908
  CHROME_TAGS = new Set(["nav", "footer", "header"]);
900
909
  });
901
910
 
902
- // ../../src/graph/agent-augment.ts
903
- var DEFAULT_MODEL, ENABLED, AUGMENT_TIMEOUT_MS, MAX_AUGMENT_ENDPOINTS, MAX_AUGMENT_PAYLOAD_CHARS, GENERIC_SEMANTIC_TYPES;
904
- var init_agent_augment = __esm(() => {
905
- init_graph();
906
- DEFAULT_MODEL = process.env.UNBROWSE_AGENT_SEMANTIC_MODEL ?? process.env.UNBROWSE_AGENT_JUDGE_MODEL ?? "gpt-4.1-mini";
907
- ENABLED = process.env.UNBROWSE_AGENT_SEMANTIC_AUGMENT !== "0";
908
- AUGMENT_TIMEOUT_MS = Number(process.env.UNBROWSE_AGENT_SEMANTIC_TIMEOUT_MS ?? 8000);
909
- MAX_AUGMENT_ENDPOINTS = Math.max(1, Number(process.env.UNBROWSE_AGENT_SEMANTIC_MAX_ENDPOINTS ?? 6));
910
- MAX_AUGMENT_PAYLOAD_CHARS = Math.max(4000, Number(process.env.UNBROWSE_AGENT_SEMANTIC_MAX_PAYLOAD_CHARS ?? 24000));
911
- GENERIC_SEMANTIC_TYPES = new Set(["identifier", "input", "resource", "entity", "item"]);
912
- });
913
-
914
911
  // ../../src/execution/search-forms.ts
915
912
  var SEARCH_FIELD_NAMES, LOGIN_FIELD_NAMES, SUPPORTED_INPUT_TYPES;
916
913
  var init_search_forms = __esm(() => {
@@ -1026,6 +1023,7 @@ var init_execution = __esm(async () => {
1026
1023
  init_capture();
1027
1024
  init_reverse_engineer();
1028
1025
  init_bundle_scanner();
1026
+ init_token_resolver();
1029
1027
  init_marketplace();
1030
1028
  init_runtime();
1031
1029
  init_transform();
@@ -1036,7 +1034,6 @@ var init_execution = __esm(async () => {
1036
1034
  init_domain();
1037
1035
  init_extraction();
1038
1036
  init_graph();
1039
- init_agent_augment();
1040
1037
  init_logger();
1041
1038
  init_version();
1042
1039
  init_search_forms();
@@ -1165,7 +1162,7 @@ var init_routing_telemetry = __esm(() => {
1165
1162
  });
1166
1163
  // ../../src/orchestrator/index.ts
1167
1164
  import { nanoid as nanoid9 } from "nanoid";
1168
- import { existsSync as existsSync9, writeFileSync as writeFileSync3, readFileSync as readFileSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "node:fs";
1165
+ import { existsSync as existsSync10, writeFileSync as writeFileSync3, readFileSync as readFileSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "node:fs";
1169
1166
  import { dirname as dirname3, join as join9 } from "node:path";
1170
1167
  var LIVE_CAPTURE_TIMEOUT_MS, capturedDomainCache, captureInFlight, captureDomainLocks, skillRouteCache, ROUTE_CACHE_FILE, SKILL_SNAPSHOT_DIR2, domainSkillCache, DOMAIN_CACHE_FILE, _routeCacheDirty = false, routeCacheFlushTimer, routeResultCache, ROUTE_CACHE_TTL, MARKETPLACE_HYDRATE_LIMIT, MARKETPLACE_GET_SKILL_TIMEOUT_MS, MARKETPLACE_DOMAIN_SEARCH_K, MARKETPLACE_GLOBAL_SEARCH_K, SEARCH_INTENT_STOPWORDS;
1171
1168
  var init_orchestrator = __esm(async () => {
@@ -1201,7 +1198,7 @@ var init_orchestrator = __esm(async () => {
1201
1198
  domainSkillCache = new Map;
1202
1199
  DOMAIN_CACHE_FILE = join9(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
1203
1200
  try {
1204
- if (existsSync9(DOMAIN_CACHE_FILE)) {
1201
+ if (existsSync10(DOMAIN_CACHE_FILE)) {
1205
1202
  const data = JSON.parse(readFileSync6(DOMAIN_CACHE_FILE, "utf-8"));
1206
1203
  for (const [k, v] of Object.entries(data)) {
1207
1204
  const entry = v;
@@ -1218,7 +1215,7 @@ var init_orchestrator = __esm(async () => {
1218
1215
  _routeCacheDirty = false;
1219
1216
  try {
1220
1217
  const dir = dirname3(ROUTE_CACHE_FILE);
1221
- if (!existsSync9(dir))
1218
+ if (!existsSync10(dir))
1222
1219
  mkdirSync5(dir, { recursive: true });
1223
1220
  const entries = Object.fromEntries(skillRouteCache);
1224
1221
  writeFileSync3(ROUTE_CACHE_FILE, JSON.stringify(entries), "utf-8");
@@ -1226,7 +1223,7 @@ var init_orchestrator = __esm(async () => {
1226
1223
  }, 5000);
1227
1224
  routeCacheFlushTimer.unref?.();
1228
1225
  try {
1229
- if (existsSync9(ROUTE_CACHE_FILE)) {
1226
+ if (existsSync10(ROUTE_CACHE_FILE)) {
1230
1227
  const data = JSON.parse(readFileSync6(ROUTE_CACHE_FILE, "utf-8"));
1231
1228
  for (const [k, v] of Object.entries(data)) {
1232
1229
  const entry = v;
@@ -1323,7 +1320,7 @@ __export(exports_wallet, {
1323
1320
  getWalletContext: () => getWalletContext2,
1324
1321
  checkWalletConfigured: () => checkWalletConfigured2
1325
1322
  });
1326
- import { existsSync as existsSync13, readFileSync as readFileSync9 } from "node:fs";
1323
+ import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
1327
1324
  import { homedir as homedir6 } from "node:os";
1328
1325
  import { join as join11 } from "node:path";
1329
1326
  function asNonEmptyString2(value) {
@@ -1331,7 +1328,7 @@ function asNonEmptyString2(value) {
1331
1328
  }
1332
1329
  function getLobsterWalletFromLocalConfig2() {
1333
1330
  const agentsPath = join11(process.env.HOME || homedir6(), ".lobster", "agents.json");
1334
- if (!existsSync13(agentsPath))
1331
+ if (!existsSync14(agentsPath))
1335
1332
  return;
1336
1333
  try {
1337
1334
  const raw = JSON.parse(readFileSync9(agentsPath, "utf8"));
@@ -1382,7 +1379,7 @@ init_wallet();
1382
1379
  init_telemetry_attribution();
1383
1380
  import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync, readdirSync as readdirSync2 } from "fs";
1384
1381
  import { join as join4 } from "path";
1385
- import { homedir as homedir3, hostname } from "os";
1382
+ import { homedir as homedir3, hostname, release as osRelease } from "os";
1386
1383
  import { randomBytes, createHash as createHash2 } from "crypto";
1387
1384
  import { createInterface } from "readline";
1388
1385
  var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
@@ -1575,9 +1572,19 @@ async function recordInstallTelemetryEvent(source, options) {
1575
1572
  skill_version: options?.skillVersion,
1576
1573
  status: options?.status ?? "installed",
1577
1574
  created_at: createdAt,
1578
- properties: mergeTelemetryProperties(options?.properties, getTelemetryAttribution())
1575
+ properties: mergeTelemetryProperties({ ...getRuntimeContext(), ...options?.properties }, getTelemetryAttribution())
1579
1576
  });
1580
1577
  }
1578
+ function getRuntimeContext() {
1579
+ return {
1580
+ cli_version: PACKAGE_VERSION,
1581
+ code_hash: CODE_HASH,
1582
+ node_version: process.version,
1583
+ platform: process.platform,
1584
+ arch: process.arch,
1585
+ os_release: osRelease()
1586
+ };
1587
+ }
1581
1588
  async function recordFunnelTelemetryEvent(name, options) {
1582
1589
  const createdAt = options?.createdAt ?? new Date().toISOString();
1583
1590
  const landingToken = getLandingToken();
@@ -1589,7 +1596,7 @@ async function recordFunnelTelemetryEvent(name, options) {
1589
1596
  source: options?.source ?? "cli",
1590
1597
  host_type: options?.hostType ?? detectTelemetryHostType(),
1591
1598
  created_at: createdAt,
1592
- properties: mergeTelemetryProperties(options?.properties, getTelemetryAttribution())
1599
+ properties: mergeTelemetryProperties({ ...getRuntimeContext(), ...options?.properties }, getTelemetryAttribution())
1593
1600
  });
1594
1601
  }
1595
1602
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/i;
@@ -2300,7 +2307,7 @@ function buildDepsMetadata(pack, taskName) {
2300
2307
  init_paths();
2301
2308
  init_supervisor();
2302
2309
  init_version();
2303
- import { openSync, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
2310
+ import { existsSync as existsSync7, openSync, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
2304
2311
  import path2 from "node:path";
2305
2312
  import { spawn } from "node:child_process";
2306
2313
  function isServerVersionMismatch(runningVersion, installedVersion, runningCodeHash, installedCodeHash) {
@@ -2366,6 +2373,10 @@ function getServerSpawnSpec(metaUrl, entrypoint = resolveSiblingEntrypoint(metaU
2366
2373
  recordedEntrypoint: `${process.execPath} serve`
2367
2374
  };
2368
2375
  }
2376
+ const serverJs = path2.join(path2.dirname(entrypoint), "server.js");
2377
+ if (existsSync7(serverJs) && path2.basename(entrypoint) !== "server.js") {
2378
+ entrypoint = serverJs;
2379
+ }
2369
2380
  return {
2370
2381
  command: process.execPath,
2371
2382
  args: runtimeArgsForEntrypoint(metaUrl, entrypoint),
@@ -2511,7 +2522,7 @@ async function restartServer(baseUrl, metaUrl) {
2511
2522
  }
2512
2523
 
2513
2524
  // ../../src/runtime/paths.ts
2514
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, realpathSync as realpathSync2 } from "node:fs";
2525
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, realpathSync as realpathSync2 } from "node:fs";
2515
2526
  import path3 from "node:path";
2516
2527
  import { createRequire as createRequire3 } from "node:module";
2517
2528
  import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
@@ -2531,7 +2542,7 @@ function runtimeArgsForEntrypoint2(metaUrl, entrypoint) {
2531
2542
  const req = createRequire3(metaUrl);
2532
2543
  const tsxPkg = req.resolve("tsx/package.json");
2533
2544
  const tsxLoader = path3.join(path3.dirname(tsxPkg), "dist", "loader.mjs");
2534
- if (existsSync7(tsxLoader))
2545
+ if (existsSync8(tsxLoader))
2535
2546
  return ["--import", pathToFileURL2(tsxLoader).href, entrypoint];
2536
2547
  } catch {}
2537
2548
  return ["--import", "tsx", entrypoint];
@@ -2600,13 +2611,13 @@ init_client2();
2600
2611
  init_logger();
2601
2612
  init_wallet();
2602
2613
  import { execFileSync as execFileSync3 } from "node:child_process";
2603
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "node:fs";
2614
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "node:fs";
2604
2615
  import os4 from "node:os";
2605
2616
  import path7 from "node:path";
2606
2617
 
2607
2618
  // ../../src/runtime/update-hints.ts
2608
2619
  init_paths();
2609
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2620
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2610
2621
  import os3 from "node:os";
2611
2622
  import path6 from "node:path";
2612
2623
  var DEFAULT_INTERVAL_MS = 12 * 60 * 60 * 1000;
@@ -2620,7 +2631,7 @@ function getConfigDir2() {
2620
2631
  return path6.join(getHomeDir(), ".unbrowse");
2621
2632
  }
2622
2633
  function ensureDir3(dir) {
2623
- if (!existsSync10(dir))
2634
+ if (!existsSync11(dir))
2624
2635
  mkdirSync6(dir, { recursive: true });
2625
2636
  return dir;
2626
2637
  }
@@ -2643,7 +2654,7 @@ function detectRepoRoot(start2) {
2643
2654
  let dir = path6.resolve(start2);
2644
2655
  const root = path6.parse(dir).root;
2645
2656
  while (dir !== root) {
2646
- if (existsSync10(path6.join(dir, ".git")))
2657
+ if (existsSync11(path6.join(dir, ".git")))
2647
2658
  return dir;
2648
2659
  dir = path6.dirname(dir);
2649
2660
  }
@@ -2722,12 +2733,12 @@ codex_hooks = true
2722
2733
  }
2723
2734
  function writeCodexHook(metaUrl) {
2724
2735
  const configPath = getCodexConfigPath();
2725
- if (!existsSync10(path6.dirname(configPath))) {
2736
+ if (!existsSync11(path6.dirname(configPath))) {
2726
2737
  return { host: "codex", action: "not-detected", config_file: configPath };
2727
2738
  }
2728
2739
  try {
2729
2740
  const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
2730
- const fileExistsBefore = existsSync10(configPath);
2741
+ const fileExistsBefore = existsSync11(configPath);
2731
2742
  let content = fileExistsBefore ? readFileSync7(configPath, "utf8") : "";
2732
2743
  const previous = content;
2733
2744
  content = ensureCodexHooksFeature(content);
@@ -2762,13 +2773,13 @@ command = ${JSON.stringify(command)}
2762
2773
  }
2763
2774
  function writeClaudeHook(metaUrl) {
2764
2775
  const settingsPath = getClaudeSettingsPath();
2765
- if (!existsSync10(path6.dirname(settingsPath))) {
2776
+ if (!existsSync11(path6.dirname(settingsPath))) {
2766
2777
  return { host: "claude", action: "not-detected", config_file: settingsPath };
2767
2778
  }
2768
2779
  try {
2769
2780
  const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
2770
2781
  const command = `node "${hookScript}"`;
2771
- const fileExistsBefore = existsSync10(settingsPath);
2782
+ const fileExistsBefore = existsSync11(settingsPath);
2772
2783
  const settings = readJsonFile(settingsPath) ?? {};
2773
2784
  settings.hooks ??= {};
2774
2785
  settings.hooks.SessionStart ??= [];
@@ -2833,7 +2844,7 @@ function getOpenCodeProjectCommandsDir(cwd) {
2833
2844
  return path7.join(cwd, ".opencode", "commands");
2834
2845
  }
2835
2846
  function detectOpenCode(cwd) {
2836
- return hasBinary("opencode") || existsSync11(path7.join(resolveConfigHome(), "opencode")) || existsSync11(path7.join(cwd, ".opencode"));
2847
+ return hasBinary("opencode") || existsSync12(path7.join(resolveConfigHome(), "opencode")) || existsSync12(path7.join(cwd, ".opencode"));
2837
2848
  }
2838
2849
  function renderOpenCodeCommand() {
2839
2850
  return `---
@@ -2861,11 +2872,11 @@ function writeOpenCodeCommand(scope, cwd) {
2861
2872
  if (scope === "auto" && !detected) {
2862
2873
  return { detected: false, action: "not-detected", scope: "off" };
2863
2874
  }
2864
- const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync11(path7.join(cwd, ".opencode")) ? "project" : "global";
2875
+ const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync12(path7.join(cwd, ".opencode")) ? "project" : "global";
2865
2876
  const commandsDir = resolvedScope === "project" ? getOpenCodeProjectCommandsDir(cwd) : getOpenCodeGlobalCommandsDir();
2866
2877
  const commandFile = path7.join(ensureDir2(commandsDir), "unbrowse.md");
2867
2878
  const content = renderOpenCodeCommand();
2868
- const action2 = existsSync11(commandFile) ? "updated" : "installed";
2879
+ const action2 = existsSync12(commandFile) ? "updated" : "installed";
2869
2880
  mkdirSync7(path7.dirname(commandFile), { recursive: true });
2870
2881
  writeFileSync5(commandFile, content);
2871
2882
  return {
@@ -2877,10 +2888,10 @@ function writeOpenCodeCommand(scope, cwd) {
2877
2888
  }
2878
2889
  async function ensureBrowserEngineInstalled() {
2879
2890
  const binary = findKuriBinary();
2880
- if (existsSync11(binary)) {
2891
+ if (existsSync12(binary)) {
2881
2892
  return { installed: true, action: "already-installed" };
2882
2893
  }
2883
- const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync11(path7.join(candidate, "build.zig")));
2894
+ const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync12(path7.join(candidate, "build.zig")));
2884
2895
  if (!sourceDir) {
2885
2896
  return {
2886
2897
  installed: false,
@@ -2902,7 +2913,7 @@ async function ensureBrowserEngineInstalled() {
2902
2913
  timeout: 300000
2903
2914
  });
2904
2915
  const builtBinary = findKuriBinary();
2905
- if (existsSync11(builtBinary)) {
2916
+ if (existsSync12(builtBinary)) {
2906
2917
  return {
2907
2918
  installed: true,
2908
2919
  action: "installed",
@@ -2927,7 +2938,7 @@ async function runSetup(options) {
2927
2938
  const browser = options?.installBrowser === false ? { installed: false, action: "skipped" } : await ensureBrowserEngineInstalled();
2928
2939
  const walletCheck = checkWalletConfigured();
2929
2940
  const skipWalletSetup = process.env.UNBROWSE_SKIP_WALLET_SETUP === "1";
2930
- const lobsterInstalled = hasBinary("lobstercash") || existsSync11(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
2941
+ const lobsterInstalled = hasBinary("lobstercash") || existsSync12(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
2931
2942
  if (!skipWalletSetup && !walletCheck.configured && lobsterInstalled) {
2932
2943
  console.log("[unbrowse] Crossmint lobster.cash detected but wallet not configured — running wallet setup...");
2933
2944
  try {
@@ -2967,7 +2978,7 @@ async function runSetup(options) {
2967
2978
 
2968
2979
  // ../../src/runtime/update-hints.ts
2969
2980
  init_paths();
2970
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
2981
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
2971
2982
  import os5 from "node:os";
2972
2983
  import path8 from "node:path";
2973
2984
  var INSTALL_SCRIPT_URL = "https://unbrowse.ai/install.sh";
@@ -2981,7 +2992,7 @@ function getConfigDir3() {
2981
2992
  return path8.join(getHomeDir2(), ".unbrowse");
2982
2993
  }
2983
2994
  function ensureDir4(dir) {
2984
- if (!existsSync12(dir))
2995
+ if (!existsSync13(dir))
2985
2996
  mkdirSync8(dir, { recursive: true });
2986
2997
  return dir;
2987
2998
  }
@@ -3007,7 +3018,7 @@ function detectRepoRoot2(start2) {
3007
3018
  let dir = path8.resolve(start2);
3008
3019
  const root = path8.parse(dir).root;
3009
3020
  while (dir !== root) {
3010
- if (existsSync12(path8.join(dir, ".git")))
3021
+ if (existsSync13(path8.join(dir, ".git")))
3011
3022
  return dir;
3012
3023
  dir = path8.dirname(dir);
3013
3024
  }
@@ -3730,6 +3741,25 @@ async function cmdFeedback(flags) {
3730
3741
  body.diagnostics = JSON.parse(flags.diagnostics);
3731
3742
  output(await api2("POST", "/v1/feedback", body), !!flags.pretty);
3732
3743
  }
3744
+ async function cmdAnnotate(flags) {
3745
+ const skillId = flags.skill;
3746
+ const endpointId = flags.endpoint;
3747
+ if (!skillId || !endpointId)
3748
+ die("--skill and --endpoint are required");
3749
+ const body = {};
3750
+ if (flags.text) {
3751
+ body.annotations = [{ text: flags.text }];
3752
+ }
3753
+ if (flags.constraint) {
3754
+ const parts = flags.constraint.split(":");
3755
+ if (parts.length >= 3) {
3756
+ body.constraints = [{ param: parts[0], rule: parts[1], message: parts.slice(2).join(":") }];
3757
+ }
3758
+ }
3759
+ if (!body.annotations && !body.constraints)
3760
+ die("--text or --constraint required");
3761
+ output(await api2("POST", `/v1/skills/${skillId}/endpoints/${endpointId}/annotate`, body), !!flags.pretty);
3762
+ }
3733
3763
  async function cmdReview(flags) {
3734
3764
  const skillId = flags.skill;
3735
3765
  if (!skillId)
@@ -3944,6 +3974,7 @@ var CLI_REFERENCE = {
3944
3974
  { name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "Search cached indexed/published routes and optionally execute the top trusted endpoint" },
3945
3975
  { name: "execute", usage: "--skill ID --endpoint ID [opts]", desc: "Execute a specific endpoint" },
3946
3976
  { name: "feedback", usage: "--skill ID --endpoint ID --rating N", desc: "Submit feedback (mandatory after resolve)" },
3977
+ { name: "annotate", usage: "--skill ID --endpoint ID --text 'tip' [--constraint 'param:rule:message']", desc: "Contribute best practices or constraints for an endpoint" },
3947
3978
  { name: "review", usage: "--skill ID --endpoints '[...]'", desc: "Push reviewed descriptions/schema metadata back to a captured skill before publish" },
3948
3979
  { name: "index", usage: "--skill ID", desc: "Recompute local graph/contracts/export from cached skill state only" },
3949
3980
  { name: "publish", usage: "--skill ID [--confirm-publish] [--endpoints '[...]']", desc: "Re-index locally, inspect publish-review metadata, then publish/share from cached skill state" },
@@ -4570,6 +4601,7 @@ async function main() {
4570
4601
  "exec",
4571
4602
  "feedback",
4572
4603
  "fb",
4604
+ "annotate",
4573
4605
  "review",
4574
4606
  "index",
4575
4607
  "publish",
@@ -4641,6 +4673,8 @@ async function main() {
4641
4673
  case "feedback":
4642
4674
  case "fb":
4643
4675
  return cmdFeedback(flags);
4676
+ case "annotate":
4677
+ return cmdAnnotate(flags);
4644
4678
  case "review":
4645
4679
  return cmdReview(flags);
4646
4680
  case "index":
package/dist/index.js CHANGED
@@ -1,16 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
- import { createRequire } from "node:module";
4
3
  import path from "node:path";
5
4
  import { fileURLToPath } from "node:url";
6
5
 
7
6
  const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
8
- const serverEntrypoint = path.join(packageRoot, "runtime-src", "index.ts");
9
- const req = createRequire(import.meta.url);
10
- const tsxPkg = req.resolve("tsx/package.json");
11
- const tsxLoader = path.join(path.dirname(tsxPkg), "dist", "loader.mjs");
7
+ const serverEntrypoint = path.join(packageRoot, "dist", "server.js");
12
8
 
13
- const child = spawn(process.execPath, ["--import", tsxLoader, serverEntrypoint, ...process.argv.slice(2)], {
9
+ const child = spawn(process.execPath, [serverEntrypoint, ...process.argv.slice(2)], {
14
10
  stdio: "inherit",
15
11
  cwd: process.cwd(),
16
12
  env: {
package/dist/mcp.js CHANGED
@@ -20,7 +20,7 @@ __export(exports_lobster_pay, {
20
20
  isLobsterAvailable: () => isLobsterAvailable
21
21
  });
22
22
  import { execFile, execFileSync } from "node:child_process";
23
- import { existsSync as existsSync5 } from "node:fs";
23
+ import { existsSync as existsSync6 } from "node:fs";
24
24
  import { homedir as homedir3 } from "node:os";
25
25
  import { join as join4 } from "node:path";
26
26
  function getLobsterCommand() {
@@ -31,7 +31,7 @@ function getLobsterCommand() {
31
31
  try {
32
32
  const npmPrefix = execFileSync("npm", ["config", "get", "prefix"], { encoding: "utf8", timeout: 5000 }).trim();
33
33
  const lobsterPath = join4(npmPrefix, "bin", "lobstercash");
34
- if (existsSync5(lobsterPath)) {
34
+ if (existsSync6(lobsterPath)) {
35
35
  execFileSync(lobsterPath, ["--version"], { stdio: "ignore", timeout: 3000 });
36
36
  return { cmd: lobsterPath, prefix: [] };
37
37
  }
@@ -45,7 +45,7 @@ function lobsterCmd() {
45
45
  }
46
46
  function isLobsterAvailable() {
47
47
  const agentsPath = join4(process.env.HOME || homedir3(), ".lobster", "agents.json");
48
- return existsSync5(agentsPath);
48
+ return existsSync6(agentsPath);
49
49
  }
50
50
  function lobsterX402Fetch(url, options) {
51
51
  return new Promise((resolve) => {
@@ -121,12 +121,12 @@ var init_lobster_pay = () => {};
121
121
  // ../../src/mcp.ts
122
122
  import { config as loadEnv } from "dotenv";
123
123
  import { createInterface } from "readline";
124
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
124
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
125
125
  import path4 from "path";
126
126
  import { fileURLToPath as fileURLToPath3 } from "url";
127
127
 
128
128
  // ../../src/runtime/local-server.ts
129
- import { openSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "node:fs";
129
+ import { existsSync as existsSync3, openSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "node:fs";
130
130
  import path2 from "node:path";
131
131
  import { spawn } from "node:child_process";
132
132
 
@@ -225,11 +225,11 @@ import { dirname, join, parse } from "path";
225
225
  import { fileURLToPath as fileURLToPath2 } from "url";
226
226
 
227
227
  // ../../src/build-info.generated.ts
228
- var BUILD_RELEASE_VERSION = "3.1.0-experiments.f9b4a71";
229
- var BUILD_GIT_SHA = "f9b4a71af04a";
228
+ var BUILD_RELEASE_VERSION = "3.2.0";
229
+ var BUILD_GIT_SHA = "c3fc3f822751";
230
230
  var BUILD_CODE_HASH = "1488fc1d92b7";
231
- var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuZjliNGE3MSIsImdpdF9zaGEiOiJmOWI0YTcxYWYwNGEiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QGY5YjRhNzFhZjA0YSIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDVUMTM6MjI6NDYuMTM0WiJ9";
232
- var BUILD_RELEASE_MANIFEST_SIGNATURE = "_s5u-H651GXE5q8GnEVaA7daM2EoV_MS-tJDHAUiJIg";
231
+ var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjAiLCJnaXRfc2hhIjoiYzNmYzNmODIyNzUxIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0BjM2ZjM2Y4MjI3NTEiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA2VDA1OjA2OjAxLjIwMFoifQ";
232
+ var BUILD_RELEASE_MANIFEST_SIGNATURE = "xCBEHEB2UsniVYLfzuTpoXlcomZHL2pXht-7Ii1e7mM";
233
233
  var BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
234
234
 
235
235
  // ../../src/version.ts
@@ -400,6 +400,10 @@ function getServerSpawnSpec(metaUrl, entrypoint = resolveSiblingEntrypoint(metaU
400
400
  recordedEntrypoint: `${process.execPath} serve`
401
401
  };
402
402
  }
403
+ const serverJs = path2.join(path2.dirname(entrypoint), "server.js");
404
+ if (existsSync3(serverJs) && path2.basename(entrypoint) !== "server.js") {
405
+ entrypoint = serverJs;
406
+ }
403
407
  return {
404
408
  command: process.execPath,
405
409
  args: runtimeArgsForEntrypoint(metaUrl, entrypoint),
@@ -540,7 +544,7 @@ function stopServer(baseUrl) {
540
544
  }
541
545
 
542
546
  // ../../src/workflow/publish.ts
543
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
547
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
544
548
  import { homedir } from "node:os";
545
549
  import { join as join2 } from "node:path";
546
550
 
@@ -567,7 +571,7 @@ function workflowPublishArtifactPathForSkill(skillId) {
567
571
  }
568
572
  function readWorkflowPublishArtifact(skillId) {
569
573
  const target = workflowPublishArtifactPathForSkill(skillId);
570
- if (!existsSync3(target))
574
+ if (!existsSync4(target))
571
575
  return null;
572
576
  try {
573
577
  return JSON.parse(readFileSync3(target, "utf-8"));
@@ -577,13 +581,13 @@ function readWorkflowPublishArtifact(skillId) {
577
581
  }
578
582
  function listWorkflowPublishArtifacts() {
579
583
  const dir = getWorkflowExportDir();
580
- if (!existsSync3(dir))
584
+ if (!existsSync4(dir))
581
585
  return [];
582
586
  return readdirSync2(dir).filter((entry) => entry.endsWith(".json")).map((entry) => join2(dir, entry));
583
587
  }
584
588
 
585
589
  // ../../src/impact-log.ts
586
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, appendFileSync, statSync, readFileSync as readFileSync4, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
590
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, appendFileSync, statSync, readFileSync as readFileSync4, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
587
591
  import { homedir as homedir2 } from "node:os";
588
592
  import { dirname as dirname2, join as join3 } from "node:path";
589
593
  var MAX_LOG_BYTES = 5 * 1024 * 1024;
@@ -599,19 +603,19 @@ function getImpactLogPath() {
599
603
  }
600
604
  function ensureDir2(path4) {
601
605
  const dir = dirname2(path4);
602
- if (!existsSync4(dir))
606
+ if (!existsSync5(dir))
603
607
  mkdirSync3(dir, { recursive: true });
604
608
  }
605
609
  function rotateIfNeeded(path4) {
606
610
  try {
607
- if (!existsSync4(path4))
611
+ if (!existsSync5(path4))
608
612
  return;
609
613
  const size = statSync(path4).size;
610
614
  if (size < MAX_LOG_BYTES)
611
615
  return;
612
616
  for (let i = MAX_ROTATIONS;i >= 1; i--) {
613
617
  const older = `${path4}.${i}`;
614
- if (!existsSync4(older))
618
+ if (!existsSync5(older))
615
619
  continue;
616
620
  if (i === MAX_ROTATIONS) {
617
621
  try {
@@ -681,10 +685,10 @@ function readImpactSummary() {
681
685
  const files = [];
682
686
  for (let i = MAX_ROTATIONS;i >= 1; i--) {
683
687
  const rotated = `${path4}.${i}`;
684
- if (existsSync4(rotated))
688
+ if (existsSync5(rotated))
685
689
  files.push(rotated);
686
690
  }
687
- if (existsSync4(path4))
691
+ if (existsSync5(path4))
688
692
  files.push(path4);
689
693
  if (files.length === 0)
690
694
  return summary;
@@ -741,9 +745,9 @@ function readImpactSummary() {
741
745
  }
742
746
 
743
747
  // ../../src/client/index.ts
744
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "fs";
748
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "fs";
745
749
  import { join as join5 } from "path";
746
- import { homedir as homedir4, hostname } from "os";
750
+ import { homedir as homedir4, hostname, release as osRelease } from "os";
747
751
 
748
752
  // ../../src/payments/cascade.ts
749
753
  import bs58 from "bs58";
@@ -792,7 +796,7 @@ function sanitizeProfileName2(value) {
792
796
  function loadConfig() {
793
797
  try {
794
798
  const configPath = getConfigPath();
795
- if (existsSync6(configPath)) {
799
+ if (existsSync7(configPath)) {
796
800
  return JSON.parse(readFileSync5(configPath, "utf-8"));
797
801
  }
798
802
  } catch {}
@@ -1312,7 +1316,7 @@ function getPackageRoot2() {
1312
1316
  let dir = path4.dirname(fileURLToPath3(import.meta.url));
1313
1317
  const root = path4.parse(dir).root;
1314
1318
  while (dir !== root) {
1315
- if (path4.basename(dir) === "src" && existsSync7(path4.join(path4.dirname(dir), "package.json"))) {
1319
+ if (path4.basename(dir) === "src" && existsSync8(path4.join(path4.dirname(dir), "package.json"))) {
1316
1320
  return path4.dirname(dir);
1317
1321
  }
1318
1322
  try {
@@ -1406,12 +1410,23 @@ function addExecuteNextStepHints(result, args) {
1406
1410
  hints.feedback_skill = skillId;
1407
1411
  if (endpointId)
1408
1412
  hints.feedback_endpoint = endpointId;
1413
+ const constraints = isPlainObject(nested) && Array.isArray(nested.constraints) ? nested.constraints : undefined;
1414
+ if (constraints?.length) {
1415
+ hints.known_constraints = constraints;
1416
+ }
1417
+ const annotations = isPlainObject(nested) && Array.isArray(nested.annotations) ? nested.annotations : undefined;
1418
+ if (annotations?.length) {
1419
+ hints.community_notes = annotations;
1420
+ }
1409
1421
  const desc = isPlainObject(nested) && typeof nested.description === "string" ? nested.description : "";
1410
1422
  const looksGeneric = !desc || desc.startsWith("Captured ") || desc.startsWith("Returns results");
1411
1423
  if (looksGeneric) {
1412
1424
  hints.first_use_review_needed = true;
1413
1425
  hints.review_step = "After feedback, call unbrowse_review to write proper endpoint descriptions, then unbrowse_publish to share to marketplace.";
1414
1426
  }
1427
+ if (skillId && endpointId) {
1428
+ hints.contribute = "If you learned something about this endpoint (required params, gotchas, best practices), call unbrowse_annotate to share it with other agents.";
1429
+ }
1415
1430
  return { ...result, _workflow_hints: hints };
1416
1431
  }
1417
1432
  function addCaptureNextStepHints(result, _args) {
@@ -2448,6 +2463,42 @@ var tools = [
2448
2463
  const withHints = addCaptureNextStepHints(result, args);
2449
2464
  return successResult(withHints, "Browse session closed. See _workflow_hints for required next steps: call unbrowse_review then unbrowse_publish.");
2450
2465
  }
2466
+ },
2467
+ {
2468
+ name: "unbrowse_annotate",
2469
+ description: "Contribute constraints or best practices for an endpoint. Call this after executing an endpoint to share what you learned (required params, gotchas, tips) with other agents.",
2470
+ parameters: {
2471
+ type: "object",
2472
+ properties: {
2473
+ skill: { type: "string", description: "Skill ID" },
2474
+ endpoint: { type: "string", description: "Endpoint ID" },
2475
+ constraints: {
2476
+ type: "array",
2477
+ description: "Learned constraints (required params, deprecated fields, format rules)",
2478
+ items: { type: "object", properties: { param: { type: "string" }, rule: { type: "string" }, message: { type: "string" } }, required: ["param", "rule", "message"] }
2479
+ },
2480
+ annotations: {
2481
+ type: "array",
2482
+ description: "Free-text best practices, tips, or gotchas",
2483
+ items: { type: "object", properties: { text: { type: "string" } }, required: ["text"] }
2484
+ }
2485
+ },
2486
+ required: ["skill", "endpoint"]
2487
+ },
2488
+ handler: async (args) => {
2489
+ await ensureServerReady();
2490
+ const skillId = args.skill;
2491
+ const endpointId = args.endpoint;
2492
+ const body = {};
2493
+ if (Array.isArray(args.constraints))
2494
+ body.constraints = args.constraints;
2495
+ if (Array.isArray(args.annotations))
2496
+ body.annotations = args.annotations;
2497
+ if (!body.constraints && !body.annotations)
2498
+ return errorResult("Provide constraints and/or annotations");
2499
+ const result = await api2("POST", `/v1/skills/${skillId}/endpoints/${endpointId}/annotate`, body);
2500
+ return successResult(result, "Annotation saved. Other agents will see your contribution when using this endpoint.");
2501
+ }
2451
2502
  }
2452
2503
  ];
2453
2504
  var toolMap = new Map(tools.map((tool) => [tool.name, tool]));