@reclaimprotocol/agent 0.1.29 → 0.1.31

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 (89) hide show
  1. package/README.md +28 -9
  2. package/lib/consts.d.ts +5 -5
  3. package/lib/consts.js +7 -7
  4. package/lib/live/tunnel-watchdog.d.ts +1 -0
  5. package/lib/live/tunnel-watchdog.js +66 -0
  6. package/lib/live/tunnel.d.ts +30 -7
  7. package/lib/live/tunnel.js +217 -40
  8. package/lib/mcp/api/operations.gen.d.ts +12 -0
  9. package/lib/mcp/api/operations.gen.js +537 -59
  10. package/lib/mcp/api/tools.js +27 -5
  11. package/lib/mcp/instructions.js +23 -32
  12. package/lib/mcp/remap_errors.js +6 -0
  13. package/lib/mcp/server.js +2 -8
  14. package/lib/mcp/start.js +3 -5
  15. package/lib/mcp/success-response.d.ts +5 -0
  16. package/lib/mcp/success-response.js +21 -0
  17. package/lib/mcp/tools/agent/attach.js +9 -66
  18. package/lib/mcp/tools/agent/build.d.ts +0 -3
  19. package/lib/mcp/tools/agent/build.js +3 -4
  20. package/lib/mcp/tools/agent/how-it-works.d.ts +5 -15
  21. package/lib/mcp/tools/agent/how-it-works.js +9 -22
  22. package/lib/mcp/tools/agent/live-view.d.ts +1 -15
  23. package/lib/mcp/tools/agent/live-view.js +71 -96
  24. package/lib/mcp/tools/agent/propose.js +7 -23
  25. package/lib/mcp/tools/agent/publish.js +80 -18
  26. package/lib/mcp/tools/agent/session.d.ts +4 -7
  27. package/lib/mcp/tools/agent/session.js +8 -23
  28. package/lib/mcp/tools/verification/result.d.ts +2 -1
  29. package/lib/mcp/tools/verification/result.js +29 -28
  30. package/lib/old/client.d.ts +2 -2
  31. package/lib/old/flag.d.ts +2 -5
  32. package/lib/old/flag.js +2 -5
  33. package/lib/old/instructions.d.ts +1 -1
  34. package/lib/old/instructions.js +1 -1
  35. package/lib/old/to-register.d.ts +4 -3
  36. package/lib/old/tools/publish.js +11 -14
  37. package/lib/proof/attestor-auth.d.ts +9 -1
  38. package/lib/proof/attestor-auth.js +15 -2
  39. package/lib/proof/attestor.d.ts +2 -2
  40. package/lib/proof/attestor.js +20 -2
  41. package/lib/proof/pod-prover.js +1 -1
  42. package/lib/proof/proof-core.d.ts +4 -0
  43. package/lib/proof/proof-core.js +20 -0
  44. package/lib/provider/injection-doc.d.ts +3 -2
  45. package/lib/provider/injection-doc.js +12 -10
  46. package/lib/provider/matcher.js +19 -15
  47. package/lib/provider/to-version.d.ts +3 -0
  48. package/lib/provider/to-version.js +2 -1
  49. package/package.json +3 -3
  50. package/skill/SKILL.builder.md +30 -83
  51. package/skill/SKILL.common.md +38 -22
  52. package/skill/SKILL.old.md +17 -3
  53. package/skill/builder-mode.md +37 -4
  54. package/skill/community-contributions.md +12 -3
  55. package/skill/hash-validation.md +9 -9
  56. package/skill/interception.md +39 -46
  57. package/skill/old-mode.md +13 -0
  58. package/skill/user-script.md +24 -8
  59. package/skill/verification-client-integration.md +130 -0
  60. package/src/consts.ts +9 -7
  61. package/src/mcp/api/operations.gen.ts +539 -59
  62. package/src/mcp/api/tools.ts +38 -7
  63. package/src/mcp/instructions.ts +33 -32
  64. package/src/mcp/remap_errors.ts +6 -0
  65. package/src/mcp/server.ts +2 -8
  66. package/src/mcp/start.ts +3 -5
  67. package/src/mcp/success-response.ts +26 -0
  68. package/src/mcp/tools/agent/attach.ts +9 -71
  69. package/src/mcp/tools/agent/build.ts +4 -7
  70. package/src/mcp/tools/agent/how-it-works.ts +9 -22
  71. package/src/mcp/tools/agent/live-view.ts +76 -116
  72. package/src/mcp/tools/agent/propose.ts +7 -23
  73. package/src/mcp/tools/agent/publish.ts +89 -18
  74. package/src/mcp/tools/agent/session.ts +6 -23
  75. package/src/mcp/tools/verification/result.ts +30 -29
  76. package/src/old/client.ts +2 -2
  77. package/src/old/flag.ts +2 -5
  78. package/src/old/instructions.ts +1 -1
  79. package/src/old/to-register.ts +4 -3
  80. package/src/old/tools/publish.ts +16 -14
  81. package/src/proof/attestor-auth.ts +18 -1
  82. package/src/proof/attestor.ts +24 -3
  83. package/src/proof/pod-prover.ts +1 -1
  84. package/src/proof/proof-core.ts +28 -0
  85. package/src/provider/injection-doc.ts +12 -10
  86. package/src/provider/matcher.ts +25 -17
  87. package/src/provider/to-version.ts +7 -1
  88. package/src/live/cloudflared.ts +0 -348
  89. package/src/live/tunnel.ts +0 -254
@@ -1,4 +1,5 @@
1
1
  import { remapErrorAsResponse } from "../remap_errors.js";
2
+ import { successResponse } from "../success-response.js";
2
3
  import { MCP_TOOLS } from "./operations.gen.js";
3
4
  /**
4
5
  * The generated `MCP_TOOLS` are keyed by OpenAPI `operationId` (PascalCase,
@@ -15,18 +16,20 @@ function toSnakeCase(name) {
15
16
  }
16
17
  export function registerTools(server, client) {
17
18
  for (const [opId, shape] of Object.entries(MCP_TOOLS)) {
19
+ if (!isOperationId(opId)) {
20
+ throw new Error(`Generated MCP operation is not in the API client: ${opId}`);
21
+ }
18
22
  // snake_case to match the hand-written tools. A name that collides with
19
23
  // a hand-written tool throws here at startup — intentional: hand-written
20
24
  // and generated names must stay disjoint (see start.ts), so a clash is a
21
25
  // bug to surface, not silently shadow.
22
26
  server.registerTool(toSnakeCase(opId), shape, async (args) => {
23
27
  try {
28
+ if (!isOperationArgs(opId, args)) {
29
+ throw new Error(`Invalid generated MCP arguments for ${opId}`);
30
+ }
24
31
  const result = await client.call(opId, args);
25
- return {
26
- content: [{
27
- type: 'text', text: JSON.stringify(result.data, null, 2)
28
- }],
29
- };
32
+ return successResponse(result.data);
30
33
  }
31
34
  catch (err) {
32
35
  return remapErrorAsResponse(err);
@@ -34,3 +37,22 @@ export function registerTools(server, client) {
34
37
  });
35
38
  }
36
39
  }
40
+ /**
41
+ * Always true in practice — `value` comes from `Object.entries(MCP_TOOLS)`
42
+ * itself — but the check still earns its keep: it narrows `opId` to
43
+ * `IOperationId` for `client.call` below without an unchecked cast, and it
44
+ * turns "the generator and the API client drifted apart" into a startup
45
+ * error instead of a silent `as`.
46
+ */
47
+ function isOperationId(value) {
48
+ return value in MCP_TOOLS;
49
+ }
50
+ /**
51
+ * The MCP SDK parses `args` through the generated Zod schema before invoking
52
+ * this callback. Keep a runtime object check at this TypeScript boundary so
53
+ * generated operation arguments retain their operation-specific type without
54
+ * an unchecked cast.
55
+ */
56
+ function isOperationArgs(_operationId, value) {
57
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
58
+ }
@@ -1,29 +1,10 @@
1
1
  /**
2
- * The authoring documentation, assembled from `skill/*.md` at module load.
2
+ * Loads concise, always-on mode instructions and topic guides served on
3
+ * demand by `how_it_works`. Keeping detailed guidance out of tool schemas
4
+ * avoids adding it to every request's context.
3
5
  *
4
- * Two things are served from here:
5
- * - the always-on MCP server `instructions` (one file per backend mode) —
6
- * deliberately small, since every client pays for them on every turn;
7
- * - the deep authoring guide, split into TOPICS and served on demand by the
8
- * `how_it_works` tool. Nothing here is inlined into a tool schema: a doc
9
- * embedded in an input-schema description is paid for on every request
10
- * whether or not it's ever relevant.
11
- *
12
- * The prose lives in markdown (published via package.json's `files`) so it
13
- * can be edited without touching code. Paths resolve identically from
14
- * `src/mcp` (dev) and `lib/mcp` (published) — both are two levels below the
15
- * package root.
16
- *
17
- * Assembly has three steps:
18
- * 1. `<!-- common: name -->` splices a shared block out of `SKILL.common.md`
19
- * (delimited there by `<!-- section: name -->`), so the two mode guides
20
- * don't drift on the ~60% of prose that is identical.
21
- * 2. `%CTX%` expands to the mode's consumer-param namespace — `context.` on
22
- * the builder, `context_` on old-devtools. That is the only textual
23
- * difference across most shared blocks, so tokenizing it keeps them
24
- * shared.
25
- * 3. `<!-- topic: name -->` splits the result into the units `how_it_works`
26
- * serves. Backend-independent topics come from their own files instead.
6
+ * `<!-- common: name -->` includes shared sections, `%CTX%` selects the
7
+ * mode-specific context prefix, and `<!-- topic: name -->` creates a topic.
27
8
  */
28
9
  import assert from 'node:assert';
29
10
  import { readFileSync } from 'node:fs';
@@ -31,7 +12,7 @@ import { fileURLToPath } from 'node:url';
31
12
  function readSkill(file) {
32
13
  const url = new URL(`../../skill/${file}`, import.meta.url);
33
14
  return readFileSync(fileURLToPath(url), 'utf8')
34
- // Strip any leading frontmatter defensively.
15
+ // These files are internal topic fragments, but tolerate skill frontmatter.
35
16
  .replace(/^---\n[\s\S]*?\n---\n/, '')
36
17
  .trim();
37
18
  }
@@ -42,7 +23,7 @@ function splitBlocks(text, marker) {
42
23
  const blocks = new Map();
43
24
  const re = new RegExp(`<!-- ${marker}: (\\S+) -->\\n([\\s\\S]*?)(?=\\n<!-- ${marker}: |$)`, 'g');
44
25
  for (const match of text.matchAll(re)) {
45
- // Trim blank LINES only, not inline indentation — some blocks are
26
+ // Trim blank lines only, not inline indentation. Some blocks are
46
27
  // list-continuation text that's intentionally indented on its first line.
47
28
  blocks.set(match[1], match[2].replace(/^\n+/, '').replace(/\n+$/, ''));
48
29
  }
@@ -55,6 +36,11 @@ const SHARED_TOPIC_FILES = {
55
36
  'interception': 'interception.md',
56
37
  'hash-validation': 'hash-validation.md',
57
38
  };
39
+ /** Detailed topics that exist only in Builder mode. */
40
+ const BUILDER_TOPIC_FILES = {
41
+ 'contributions': 'community-contributions.md',
42
+ 'integration': 'verification-client-integration.md',
43
+ };
58
44
  /** One line per topic, shown in the `how_it_works` index so a caller can pick
59
45
  * the right one without fetching several. Keyed across both modes; only the
60
46
  * topics a mode actually has are listed back to it. */
@@ -80,21 +66,22 @@ const TOPIC_SUMMARIES = {
80
66
  + 'and going live.',
81
67
  'discover': 'Finding providers, resolving them, and creating a verification '
82
68
  + 'session.',
83
- 'integration': 'Builder Verification Client integration: the `api=2` bridge, '
84
- + 'result verification, and optional TEE binding.',
69
+ 'integration': 'Set up a Builder token, callback, frontend and backend '
70
+ + 'with @reclaimprotocol/client; direct `api=2` API and result '
71
+ + 'verification.',
85
72
  'user-script': 'Authoring jsUserScripts / customInjection to drive '
86
73
  + 'login-gated pages.',
87
74
  'interception': 'injectionType / interceptorType, live interception, the '
88
75
  + 'document-replay flag, and what to do when a site won\'t load or '
89
76
  + 'won\'t let the user sign in.',
90
- 'hash-validation': 'bodySniff, and how verifyProof({ providerId }) gates on '
91
- + 'the declared spec hash.',
77
+ 'hash-validation': 'bodySniff, and how verifyRequestProof({ providerId }) '
78
+ + 'gates on the declared spec hash.',
92
79
  'credentials': 'The eth proof-owner key run_proof signs with.',
93
80
  'never-do': 'The short list of things that always break a provider.',
94
81
  'troubleshooting': 'Expired drafts, browser problems, and where to look '
95
82
  + 'next.',
96
83
  };
97
- function buildGuide(file, ctxPrefix) {
84
+ function buildGuide(file, ctxPrefix, modeTopicFiles = {}) {
98
85
  const raw = readSkill(file)
99
86
  .replace(/<!-- common: (\S+) -->/g, (_whole, name) => {
100
87
  const section = COMMON_SECTIONS.get(name);
@@ -107,6 +94,10 @@ function buildGuide(file, ctxPrefix) {
107
94
  for (const [name, path] of Object.entries(SHARED_TOPIC_FILES)) {
108
95
  topics.set(name, readSkill(path));
109
96
  }
97
+ for (const [name, path] of Object.entries(modeTopicFiles)) {
98
+ assert(topics.has(name), `${file}: no topic marker named "${name}"`);
99
+ topics.set(name, readSkill(path));
100
+ }
110
101
  const index = [...topics.keys()].map((topic) => {
111
102
  const summary = TOPIC_SUMMARIES[topic];
112
103
  assert(summary, `${file}: no TOPIC_SUMMARIES entry for "${topic}"`);
@@ -114,7 +105,7 @@ function buildGuide(file, ctxPrefix) {
114
105
  });
115
106
  return { topics, index, full: [...topics.values()].join('\n\n') };
116
107
  }
117
- export const GUIDE_BUILDER = buildGuide('SKILL.builder.md', 'context.');
108
+ export const GUIDE_BUILDER = buildGuide('SKILL.builder.md', 'context.', BUILDER_TOPIC_FILES);
118
109
  export const GUIDE_OLD = buildGuide('SKILL.old.md', 'context_');
119
110
  export function guideFor(oldMode) {
120
111
  return oldMode ? GUIDE_OLD : GUIDE_BUILDER;
@@ -5,6 +5,12 @@ import { errorAsToolText } from "../tools/registry.js";
5
5
  // (shared with the cloud loop); Boom problems also attach structuredContent.
6
6
  export const remapErrorAsResponse = (err) => {
7
7
  const text = errorAsToolText(err);
8
+ // Two unrelated things can carry structured data here: the client's own
9
+ // ProblemError (an RFC 9457 response from a Builder API call), and a
10
+ // hand-thrown Boom error whose second argument is arbitrary tool context,
11
+ // for example `badRequest('invalid provider', { field: 'providerId' })`.
12
+ // `isProblemDetails` only rejects a wrongly typed KNOWN field, so it lets
13
+ // that arbitrary shape through too — this isn't a strict RFC 9457 check.
8
14
  const data = isProblemError(err)
9
15
  ? err.data
10
16
  : isBoom(err) && err.output?.statusCode
package/lib/mcp/server.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { toZod } from "./api/to-zod.js";
2
2
  import { remapErrorAsResponse } from "./remap_errors.js";
3
+ import { successResponse } from "./success-response.js";
3
4
  // The tool primitive lives in `../tools/registry.ts` (SDK-free, so the cloud
4
5
  // loop can use it too). Re-exported here so the MCP-side tool files can keep
5
6
  // importing `defineTool`/`RegisteredTool` from the server module.
@@ -16,14 +17,7 @@ export function registerToolList(server, tools) {
16
17
  inputSchema: toZod(tool.definition.inputSchema),
17
18
  }, async (args) => {
18
19
  try {
19
- return {
20
- content: [
21
- {
22
- type: 'text',
23
- text: JSON.stringify(await tool.handler(args), null, 2),
24
- },
25
- ],
26
- };
20
+ return successResponse(await tool.handler(args));
27
21
  }
28
22
  catch (err) {
29
23
  return remapErrorAsResponse(err);
package/lib/mcp/start.js CHANGED
@@ -15,7 +15,7 @@ import { buildAgentState, buildAgentTools, buildBuilderAuthenticationTools, buil
15
15
  import { BUILDER_MODE_INSTRUCTIONS } from "./instructions.js";
16
16
  import { registerToolList } from "./server.js";
17
17
  export async function startMcpServer() {
18
- // Mode precedence: explicit USE_OLD_DEVTOOLS env → persisted config → old.
18
+ // Mode precedence: explicit env → persisted config → builder.
19
19
  // The same store instance backs the set_devtools_mode toggle below.
20
20
  const config = new ConfigStore();
21
21
  const oldMode = resolveOldMode(config.read()?.mode);
@@ -42,8 +42,7 @@ export async function startMcpServer() {
42
42
  // the agent tools are enabled, so a user can always switch either direction.
43
43
  registerToolList(server, buildModeTools(config, oldMode));
44
44
  if (oldMode) {
45
- // OLD-devtools mode (the default): the legacy backend is still in
46
- // production while builder is built. All auto-generated builder tools
45
+ // Explicit OLD-devtools mode. All auto-generated builder tools
47
46
  // are disabled; a small ReclaimOldClient serves authenticate / publish /
48
47
  // get_me_providers instead.
49
48
  const oldClient = new ReclaimOldClient({
@@ -89,8 +88,7 @@ export async function startMcpServer() {
89
88
  // Anything the session holds has to be released when the session ends, and
90
89
  // the session can end without a single tool call: the developer closes their
91
90
  // agent, the client kills the server, stdin drops. Nothing ran here before,
92
- // which left a container up and — worse — a public tunnel still pointing at a
93
- // live browser after the session that made it was gone.
91
+ // which left a container up after the session that made it was gone.
94
92
  if (agentToolsEnabled) {
95
93
  installShutdownHooks(() => agentState);
96
94
  }
@@ -0,0 +1,5 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ /** Return successful tool data in both MCP representations. Older clients read
3
+ * the JSON text; newer clients can consume `structuredContent` without asking
4
+ * the model to parse or reconstruct identifiers, URLs, and state. */
5
+ export declare function successResponse(value: unknown): CallToolResult;
@@ -0,0 +1,21 @@
1
+ /** Return successful tool data in both MCP representations. Older clients read
2
+ * the JSON text; newer clients can consume `structuredContent` without asking
3
+ * the model to parse or reconstruct identifiers, URLs, and state. */
4
+ export function successResponse(value) {
5
+ return {
6
+ structuredContent: structured(value),
7
+ content: [{
8
+ type: 'text',
9
+ text: JSON.stringify(value, null, 2) ?? 'null',
10
+ }],
11
+ };
12
+ }
13
+ function structured(value) {
14
+ if (isRecord(value)) {
15
+ return value;
16
+ }
17
+ return { result: value ?? null };
18
+ }
19
+ function isRecord(value) {
20
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
21
+ }
@@ -16,22 +16,8 @@ import { ensureLiveViewUrl } from "./live-view-url.js";
16
16
  * show — one on this machine is watched by looking at it.
17
17
  */
18
18
  export const SHAREABLE_MODES = new Set(['container']);
19
- /**
20
- * The browser sources, written once and used twice: rendered into this tool's
21
- * DESCRIPTION (so an agent can put the choice to the developer straight from
22
- * the tool list, without a discovery round-trip) and returned verbatim as
23
- * structured `options` when the tool is called without a `mode` anyway.
24
- *
25
- * Being the single source for both is the point — an option list that a model
26
- * reads in the description but that disagrees with what the tool returns is
27
- * worse than either alone.
28
- *
29
- * Ordered and labelled BY USE CASE, because that is the question a developer
30
- * can actually answer. "Which CDP transport?" is not; "does anyone else need to
31
- * get into this browser?" is, and it decides two of the four. `useCase` is the
32
- * grouping key, so the rendered list reads as two pairs rather than four
33
- * unrelated transports.
34
- */
19
+ /** Browser sources returned when the caller omits `mode`. Keep selection detail
20
+ * here instead of repeating it in the always-loaded tool description. */
35
21
  const BROWSER_OPTIONS = [
36
22
  {
37
23
  mode: 'dedicated',
@@ -103,28 +89,6 @@ const BROWSER_OPTIONS = [
103
89
  liveView: 'None, unless you pass your own liveViewUrl alongside the cdpUrl.',
104
90
  },
105
91
  ];
106
- /**
107
- * The options as prose, grouped by use case.
108
- *
109
- * The grouping is the useful part: it turns "pick one of five transports" into
110
- * "answer one question, then pick one of two".
111
- */
112
- function renderBrowserOptions() {
113
- const seen = new Set();
114
- const lines = [];
115
- for (const option of BROWSER_OPTIONS) {
116
- if (!seen.has(option.useCase)) {
117
- seen.add(option.useCase);
118
- lines.push(`— ${option.useCase.toUpperCase()} —`);
119
- }
120
- const needs = option.requires.length
121
- ? ` Needs: ${option.requires.join('; ')}.`
122
- : '';
123
- lines.push(`(${option.mode}) ${option.label}. ${option.detail}${needs}`
124
- + ` Live view: ${option.liveView}`);
125
- }
126
- return lines.join(' ');
127
- }
128
92
  const DEFAULT_DEPENDENCIES = {
129
93
  connectLocal: connectCdp,
130
94
  connectRemote: connectCdpUrl,
@@ -140,34 +104,13 @@ dependencies = {}) {
140
104
  };
141
105
  return defineTool({
142
106
  name: 'attach_browser',
143
- description: 'Connect the provider-authoring tools to a browser. Never choose '
144
- + 'for them, never assume a previous session\'s choice still '
145
- + 'applies, and call this ONCE with the `mode` they pick. '
146
- + 'ASK THIS FIRST, before listing anything: "Will you be doing this '
147
- + 'yourself, or does someone else need to sign in — for example the '
148
- + 'person whose account it is?" That answer decides half the list, '
149
- + 'and a developer can answer it without knowing what CDP is. '
150
- + 'THEMSELVES → offer `dedicated` or `attach`. SOMEONE ELSE, or the '
151
- + 'site only works on a phone → offer `container` or `builder`, '
152
- + 'which are the only two that produce a link another person can '
153
- + 'open. Say which of those two costs money: `container` is free '
154
- + 'but needs Docker, `builder` needs no install but is billed '
155
- + 'against the org\'s quota. '
156
- + 'Then relay the matching options with their live-view lines. You '
157
- + 'do not need a discovery call — the full list is here, and calling '
158
- + 'with no `mode` returns it as structured `options` if you would '
159
- + 'rather read it back verbatim. '
160
- + 'OPTIONS — ' + renderBrowserOptions() + ' '
161
- + 'A `container` browser returns a local `viewerUrl`; relay it. When '
162
- + 'someone remote needs in, call share_browser_view and relay the '
163
- + 'FULL public url verbatim, on its own line. `builder` returns a '
164
- + 'hosted `liveViewUrl` — relay that the same way. `dedicated`, '
165
- + '`attach` and `custom` cannot be shared at all: if they ask for a '
166
- + 'link after picking one, say so and offer to re-attach with '
167
- + '`container`. '
168
- + 'Builder and custom CDP credentials are held in memory and are '
169
- + 'never returned. '
170
- + 'More: how_it_works({ topic: "browser" }).',
107
+ description: 'Connects provider-authoring tools to a browser. Call without '
108
+ + '`mode` to receive the current options, then ask the developer to '
109
+ + 'choose; `builder` consumes quota and only `container` and `builder` '
110
+ + 'support a shareable live view. Call again once with the chosen '
111
+ + 'mode. Relay returned live-view URLs verbatim. For selection, '
112
+ + 'geolocation, and lifecycle guidance, call '
113
+ + 'how_it_works({ topic: "browser" }).',
171
114
  inputSchema: {
172
115
  type: 'object',
173
116
  properties: {
@@ -14,15 +14,12 @@ import { ConfigStore } from '../../../config/store.ts';
14
14
  import { type RegisteredTool } from '../../server.ts';
15
15
  import { type AttachState } from './attach.ts';
16
16
  import { type CaptureStore } from './capture.ts';
17
- import { type LiveShareRef } from './live-view.ts';
18
17
  export interface AgentState {
19
18
  attach: {
20
19
  current?: AttachState;
21
20
  config: ConfigStore;
22
21
  };
23
22
  captures: CaptureStore;
24
- /** The one public live view, when `share_browser_view` has opened it. */
25
- liveShare: LiveShareRef;
26
23
  }
27
24
  /** Exported so a mode (for example, old-devtools) that supplies its own
28
25
  * publish tool can build the attach/capture state up front and share the SAME
@@ -30,7 +30,6 @@ export function buildAgentState() {
30
30
  return {
31
31
  attach: { config: new ConfigStore() },
32
32
  captures: { sessions: new Map() },
33
- liveShare: {},
34
33
  };
35
34
  }
36
35
  /** Build the flat list of `RegisteredTool` entries, ready for
@@ -54,11 +53,11 @@ export function buildAgentTools(client, builderClient = client, externalState) {
54
53
  for (const t of captureTools(state.attach, state.captures)) {
55
54
  tools.push(t);
56
55
  }
57
- for (const t of sessionTools(state.attach, state.captures, state.liveShare)) {
56
+ for (const t of sessionTools(state.attach, state.captures)) {
58
57
  tools.push(t);
59
58
  }
60
59
  // Local-CDP only: a builder-mode browser already has a hosted live view.
61
- for (const t of liveViewTools(state.attach, state.liveShare)) {
60
+ for (const t of liveViewTools(state.attach)) {
62
61
  tools.push(t);
63
62
  }
64
63
  for (const t of podProofTools(state.attach, state.captures)) {
@@ -81,5 +80,5 @@ export function buildAgentTools(client, builderClient = client, externalState) {
81
80
  * safe when nothing was ever attached.
82
81
  */
83
82
  export async function disposeAgentState(state) {
84
- await resetAll(state.attach, state.captures, state.liveShare);
83
+ await resetAll(state.attach, state.captures);
85
84
  }
@@ -1,19 +1,9 @@
1
1
  import { type RegisteredTool } from '../../server.ts';
2
2
  /**
3
- * Serves the provider-authoring guide on demand, ONE TOPIC AT A TIME. The
4
- * server's always-on `instructions` are a terse tool-flow reference; this tool
5
- * is the deep version, so any MCP client — not just Claude Code, which alone
6
- * auto-loads skills — can pull it.
7
- *
8
- * Topic-scoped rather than all-at-once because the full guide is ~7k tokens:
9
- * paying that to answer "how do I mark a header secret?" crowds out the actual
10
- * task, which is exactly what makes an agent slow and prone to inventing
11
- * details. Every response carries the topic index, so a caller that guessed
12
- * wrong (or called with no topic at all) always learns what else is available
13
- * without a second round-trip.
14
- *
15
- * `oldMode` selects the backend-specific guide, so the agent is never told to
16
- * call auth/publish tools that aren't registered in its mode. Stateless:
17
- * callable before `attach_browser`.
3
+ * Serve one mode-specific guide topic without loading the full guide (~7k
4
+ * tokens) — paying that cost to answer one question would crowd out the
5
+ * actual task. Every response includes the topic index, so a caller that
6
+ * guessed the topic name wrong still learns what else exists in the same
7
+ * round-trip.
18
8
  */
19
9
  export declare function howItWorksTool(oldMode: boolean): RegisteredTool;
@@ -1,34 +1,21 @@
1
1
  import { guideFor } from "../../instructions.js";
2
2
  import { defineTool } from "../../server.js";
3
3
  /**
4
- * Serves the provider-authoring guide on demand, ONE TOPIC AT A TIME. The
5
- * server's always-on `instructions` are a terse tool-flow reference; this tool
6
- * is the deep version, so any MCP client — not just Claude Code, which alone
7
- * auto-loads skills — can pull it.
8
- *
9
- * Topic-scoped rather than all-at-once because the full guide is ~7k tokens:
10
- * paying that to answer "how do I mark a header secret?" crowds out the actual
11
- * task, which is exactly what makes an agent slow and prone to inventing
12
- * details. Every response carries the topic index, so a caller that guessed
13
- * wrong (or called with no topic at all) always learns what else is available
14
- * without a second round-trip.
15
- *
16
- * `oldMode` selects the backend-specific guide, so the agent is never told to
17
- * call auth/publish tools that aren't registered in its mode. Stateless:
18
- * callable before `attach_browser`.
4
+ * Serve one mode-specific guide topic without loading the full guide (~7k
5
+ * tokens) — paying that cost to answer one question would crowd out the
6
+ * actual task. Every response includes the topic index, so a caller that
7
+ * guessed the topic name wrong still learns what else exists in the same
8
+ * round-trip.
19
9
  */
20
10
  export function howItWorksTool(oldMode) {
21
11
  const guide = guideFor(oldMode);
22
12
  const names = [...guide.topics.keys()];
23
13
  return defineTool({
24
14
  name: 'how_it_works',
25
- description: 'Reclaim provider-authoring guide for THIS server, by topic. Call it '
26
- + 'with no arguments for the overview plus the topic index, then again '
27
- + 'for whichever topic you need. Topics: '
28
- + names.join(', ')
29
- + '. Use `all` for the entire guide at once (large — prefer a topic). '
30
- + 'Read the relevant topic BEFORE acting unless you are already fluent '
31
- + 'in that part of the flow.',
15
+ description: 'Returns topic-specific provider-authoring guidance for the active '
16
+ + 'backend. Call without a topic for the overview and index. Before an '
17
+ + 'unfamiliar or sensitive step, request that topic. Prefer one topic; '
18
+ + '`all` returns the much larger complete guide.',
32
19
  inputSchema: {
33
20
  type: 'object',
34
21
  properties: {
@@ -1,19 +1,5 @@
1
- import { startTunnel, type Tunnel } from '../../../live/tunnel.ts';
2
1
  import { type RegisteredTool } from '../../server.ts';
3
2
  import { type AttachState } from './attach.ts';
4
- export interface LiveShare {
5
- tunnel: Tunnel;
6
- /** The public URL handed to whoever is watching. */
7
- url: string;
8
- }
9
- export interface LiveShareRef {
10
- current?: LiveShare;
11
- }
12
- type TunnelStarter = typeof startTunnel;
13
- /** Tear down whatever share is running. Safe when none is. Exported so the
14
- * session tools can call it from `dispose_browser` / `reset_session`. */
15
- export declare function stopLiveShare(ref: LiveShareRef): Promise<boolean>;
16
3
  export declare function liveViewTools(attachRef: {
17
4
  current?: AttachState;
18
- }, shareRef: LiveShareRef, openTunnel?: TunnelStarter): RegisteredTool[];
19
- export {};
5
+ }): RegisteredTool[];