@totalreclaw/totalreclaw 3.3.12-rc.2 → 3.3.12-rc.21

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 (87) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/SKILL.md +34 -249
  3. package/api-client.ts +17 -9
  4. package/batch-gate.ts +42 -0
  5. package/billing-cache.ts +25 -20
  6. package/claims-helper.ts +7 -1
  7. package/config.ts +54 -12
  8. package/consolidation.ts +6 -13
  9. package/contradiction-sync.ts +19 -14
  10. package/credential-provider.ts +184 -0
  11. package/crypto.ts +27 -160
  12. package/dist/api-client.js +17 -9
  13. package/dist/batch-gate.js +40 -0
  14. package/dist/billing-cache.js +19 -21
  15. package/dist/claims-helper.js +7 -1
  16. package/dist/config.js +54 -12
  17. package/dist/consolidation.js +6 -15
  18. package/dist/contradiction-sync.js +15 -15
  19. package/dist/credential-provider.js +145 -0
  20. package/dist/crypto.js +17 -137
  21. package/dist/download-ux.js +11 -7
  22. package/dist/embedder-loader.js +266 -0
  23. package/dist/embedding.js +36 -3
  24. package/dist/entry.js +123 -0
  25. package/dist/extractor.js +134 -0
  26. package/dist/fs-helpers.js +116 -241
  27. package/dist/import-adapters/chatgpt-adapter.js +14 -0
  28. package/dist/import-adapters/claude-adapter.js +14 -0
  29. package/dist/import-adapters/gemini-adapter.js +43 -159
  30. package/dist/import-adapters/mcp-memory-adapter.js +14 -0
  31. package/dist/import-state-manager.js +100 -0
  32. package/dist/index.js +1130 -2520
  33. package/dist/llm-client.js +69 -1
  34. package/dist/memory-runtime.js +459 -0
  35. package/dist/native-memory.js +123 -0
  36. package/dist/onboarding-cli.js +3 -2
  37. package/dist/pair-cli.js +1 -1
  38. package/dist/pair-crypto.js +16 -358
  39. package/dist/pair-http.js +147 -4
  40. package/dist/relay.js +140 -0
  41. package/dist/reranker.js +13 -8
  42. package/dist/semantic-dedup.js +5 -7
  43. package/dist/skill-register.js +97 -0
  44. package/dist/subgraph-search.js +3 -1
  45. package/dist/subgraph-store.js +315 -282
  46. package/dist/tool-gating.js +39 -26
  47. package/dist/tools.js +367 -0
  48. package/dist/tr-cli-export-helper.js +103 -0
  49. package/dist/tr-cli.js +220 -127
  50. package/dist/trajectory-poller.js +155 -9
  51. package/dist/vault-crypto.js +551 -0
  52. package/download-ux.ts +12 -6
  53. package/embedder-loader.ts +293 -1
  54. package/embedding.ts +43 -3
  55. package/entry.ts +132 -0
  56. package/extractor.ts +167 -0
  57. package/fs-helpers.ts +166 -292
  58. package/import-adapters/chatgpt-adapter.ts +18 -0
  59. package/import-adapters/claude-adapter.ts +18 -0
  60. package/import-adapters/gemini-adapter.ts +56 -183
  61. package/import-adapters/mcp-memory-adapter.ts +18 -0
  62. package/import-adapters/types.ts +1 -1
  63. package/import-state-manager.ts +139 -0
  64. package/index.ts +1432 -3002
  65. package/llm-client.ts +74 -1
  66. package/memory-runtime.ts +723 -0
  67. package/native-memory.ts +196 -0
  68. package/onboarding-cli.ts +3 -2
  69. package/openclaw.plugin.json +5 -17
  70. package/package.json +7 -4
  71. package/pair-cli.ts +1 -1
  72. package/pair-crypto.ts +41 -483
  73. package/pair-http.ts +194 -5
  74. package/postinstall.mjs +138 -0
  75. package/relay.ts +172 -0
  76. package/reranker.ts +13 -8
  77. package/semantic-dedup.ts +5 -6
  78. package/skill-register.ts +146 -0
  79. package/skill.json +1 -1
  80. package/subgraph-search.ts +3 -1
  81. package/subgraph-store.ts +334 -299
  82. package/tool-gating.ts +39 -26
  83. package/tools.ts +499 -0
  84. package/tr-cli-export-helper.ts +138 -0
  85. package/tr-cli.ts +263 -133
  86. package/trajectory-poller.ts +162 -10
  87. package/vault-crypto.ts +705 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * native-memory — the OpenClaw-native MemoryPluginCapability wiring helper
3
+ * for the TR plugin (Task 2.7 of the OpenClaw native integration plan,
4
+ * docs/plans/2026-06-21-openclaw-native-integration-plan.md, 2026-06-21).
5
+ *
6
+ * WHY THIS FILE EXISTS:
7
+ * This is THE integration point. OpenClaw 2026.6.8 exposes a
8
+ * `api.registerMemoryCapability({ promptBuilder, flushPlanResolver, runtime })`
9
+ * host surface plus the conventional `api.registerTool(...)` calls for the
10
+ * `memory_search` / `memory_get` tools the active-memory sub-agent already
11
+ * knows how to drive. For TR to BE the memory backend (not just a tool
12
+ * plugin), it must register all four against TR's own pipeline.
13
+ *
14
+ * This helper takes a single `deps` object (built by `buildRecallDeps` in
15
+ * index.ts) carrying:
16
+ * - recall / getById (the real subgraph-search + decrypt + reranker
17
+ * pipeline, parameterized by the paired account — wired in index.ts
18
+ * because that is where the unexported pipeline helpers + the
19
+ * module-level auth/encryption/owner state live)
20
+ * - quota / pinned (prompt-builder inputs; default to "no warning" /
21
+ * "no pinned" when the caller does not supply them — see TODO markers
22
+ * in index.ts's buildRecallDeps for the H1 QA gate)
23
+ *
24
+ * and performs the canonical four-call registration in the order memory-core
25
+ * itself registers them (verified at
26
+ * /tmp/tr-openclaw-probe/node_modules/openclaw/dist/extensions/memory-core/
27
+ * index.js, 2026.6.8):
28
+ * 1. api.registerMemoryCapability({ promptBuilder, flushPlanResolver, runtime })
29
+ * 2. api.registerTool(() => createMemorySearchTool(runtime), { names: ['memory_search'] })
30
+ * 3. api.registerTool(() => createMemoryGetTool(runtime), { names: ['memory_get'] })
31
+ *
32
+ * The SAME `runtime` instance is handed to the capability AND both tool
33
+ * factories. This is load-bearing: the host calls
34
+ * `runtime.getMemorySearchManager(...)` to obtain a manager, and the
35
+ * tools obtain a manager via the same surface. A distinct runtime per
36
+ * registration would still work today (each construction is a pure
37
+ * closure capture) but would violate the memory-core invariant and
38
+ * break the day runtime owns real per-manager resources (embedder pool,
39
+ * connection cache). register-native.test.ts asserts the identity.
40
+ *
41
+ * DESIGN: WHY THE DEPS OBJECT IS PRE-BUILT, NOT BUILT HERE.
42
+ * `buildRecallDeps` lives in index.ts (not here) on purpose:
43
+ * 1. The real recall/getById closures capture unexported index.ts helpers
44
+ * (ensureInitialized, generateBlindIndices, generateEmbedding,
45
+ * getLSHHasher, computeCandidatePool, isDigestBlob, readClaimFromBlob,
46
+ * searchSubgraph, fetchFactById) AND module-level state
47
+ * (authKeyHex / encryptionKey / userId / subgraphOwner). Moving them
48
+ * here would force either (a) exporting all of those from index.ts
49
+ * (high blast-radius refactor with scanner-trap risk) or (b)
50
+ * re-plumbing them through this file's signature. Neither is in scope
51
+ * for Task 2.7.
52
+ * 2. The paired-account context is resolved LAZILY by `ensureInitialized()`
53
+ * on the first tool/hook call, NOT synchronously at register() time.
54
+ * So the closures must call `ensureInitialized(logger)` internally —
55
+ * they cannot be resolved at register() time even if we wanted to.
56
+ * 3. Keeping this file scanner-trivial: it touches NO host environment
57
+ * state and performs NO outbound network I/O. The closures live in
58
+ * index.ts alongside the rest of the plugin's network surface.
59
+ *
60
+ * SCANNER-CLEAN HARD CONTRACT (env=N net=N):
61
+ * This file is pure orchestration. It contains no environment-variable
62
+ * read token and no outbound network primitive, so neither the
63
+ * env-harvesting pair nor the disk-exfiltration pair can ever co-occur
64
+ * here. It also contains no dynamic-code-evaluation primitive (no
65
+ * runtime `eval` call, no `new Function` constructor). `npm run
66
+ * check-scanner` MUST remain 0 flags; this docstring itself avoids the
67
+ * literal trigger tokens for that reason.
68
+ *
69
+ * `npm run build` uses `--noCheck`, so the loose typing (the api object
70
+ * is typed against a minimal local interface) is safe — the plugin does
71
+ * not import OpenClaw's type package.
72
+ */
73
+
74
+ import {
75
+ createTrMemoryPluginRuntime,
76
+ buildPromptSection,
77
+ buildFlushPlan,
78
+ type TrRecallFn,
79
+ type TrGetFn,
80
+ type TrQuotaState,
81
+ type TrPinnedFact,
82
+ } from './memory-runtime.js';
83
+ import { createMemorySearchTool, createMemoryGetTool } from './tools.js';
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Types — the combined deps shape. Reconciles the two dep shapes the
87
+ // underlying factories expect (createTrMemoryPluginRuntime + the search
88
+ // manager need {recall, getById}; buildPromptSection needs {quota?, pinned?})
89
+ // into ONE object the caller in index.ts builds once and passes here.
90
+ // ---------------------------------------------------------------------------
91
+
92
+ /**
93
+ * The combined deps object `buildRecallDeps` in index.ts produces. Carries
94
+ * everything `registerNativeMemory` needs to wire the capability + both
95
+ * tools + the prompt builder.
96
+ *
97
+ * `recall` / `getById` are the load-bearing closures — they bind the real
98
+ * subgraph-search + decrypt + reranker pipeline (Task 2.7's `buildRecallDeps`
99
+ * in index.ts). `quota` / `pinned` are prompt-builder inputs; they are
100
+ * optional and default to no-warning / no-pinned when the caller omits them
101
+ * (documented as TODO(task 2.7b / H1 gate) at the build site).
102
+ */
103
+ export interface TrNativeMemoryDeps {
104
+ /** Recall closure: blind-index → subgraph → decrypt → rerank → TrFact[]. */
105
+ recall: TrRecallFn;
106
+ /** getById closure: fetchFactById → decrypt → {id, plaintext} | null. */
107
+ getById: TrGetFn;
108
+ /** Optional quota state for the prompt builder's warning path. */
109
+ quota?: TrQuotaState;
110
+ /** Optional pinned-facts block surfaced by the prompt builder. */
111
+ pinned?: TrPinnedFact[];
112
+ }
113
+
114
+ /**
115
+ * Minimal subset of OpenClawPluginApi this helper needs. Kept local (not
116
+ * imported) so this file compiles without the SDK type package. The runtime
117
+ * shape is structurally identical to OpenClawPluginApi's
118
+ * registerMemoryCapability / registerTool surface.
119
+ *
120
+ * `registerMemoryCapability` is OpenClaw 2026.6.8's host surface for a
121
+ * memory-kind plugin. The shape of the capability object is exactly:
122
+ * { promptBuilder, flushPlanResolver, runtime }
123
+ * — verified at /tmp/tr-openclaw-probe/node_modules/openclaw/dist/extensions/
124
+ * memory-core/index.js (l.270-273).
125
+ */
126
+ export interface NativeMemoryApiSurface {
127
+ registerMemoryCapability(capability: {
128
+ promptBuilder: (params: {
129
+ availableTools: Set<string>;
130
+ citationsMode?: unknown;
131
+ }) => string[];
132
+ flushPlanResolver: (params: { cfg?: unknown; nowMs?: number }) => unknown;
133
+ runtime: ReturnType<typeof createTrMemoryPluginRuntime>;
134
+ }): void;
135
+ registerTool(
136
+ factory: () => unknown,
137
+ opts?: { name?: string; names?: string[] },
138
+ ): void;
139
+ }
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // registerNativeMemory — the canonical four-call wiring
143
+ // ---------------------------------------------------------------------------
144
+
145
+ /**
146
+ * Wire TR's native MemoryPluginCapability + the two memory tools into the
147
+ * OpenClaw host. Performs the canonical registration in the order memory-core
148
+ * itself registers them (capability first, then both tools).
149
+ *
150
+ * The SAME `runtime` instance is passed to:
151
+ * - api.registerMemoryCapability({ ..., runtime }) (host surface)
152
+ * - createMemorySearchTool(runtime) (memory_search factory)
153
+ * - createMemoryGetTool(runtime) (memory_get factory)
154
+ *
155
+ * Identity is load-bearing — see file header. register-native.test.ts
156
+ * asserts the same `runtime` reaches all three.
157
+ *
158
+ * @param api the OpenClaw plugin api (registerMemoryCapability + registerTool)
159
+ * @param deps the combined deps object from buildRecallDeps in index.ts
160
+ * @returns the runtime that was registered (for callers that want to
161
+ * hold a reference, e.g. for close on plugin stop)
162
+ */
163
+ export function registerNativeMemory(
164
+ api: NativeMemoryApiSurface,
165
+ deps: TrNativeMemoryDeps,
166
+ ): ReturnType<typeof createTrMemoryPluginRuntime> {
167
+ // Build the runtime ONCE. This is the single object that flows into the
168
+ // capability AND both tool factories below — identity is asserted by
169
+ // register-native.test.ts.
170
+ const runtime = createTrMemoryPluginRuntime({
171
+ recall: deps.recall,
172
+ getById: deps.getById,
173
+ });
174
+
175
+ // (1) Register the capability. The prompt builder closes over deps.quota
176
+ // and deps.pinned directly (it needs no runtime state — it's pure string
177
+ // rendering, see memory-runtime.ts buildPromptSection). flushPlanResolver
178
+ // is stateless (it returns TR's canonical extraction plan; capture is not
179
+ // required, we hand the function reference verbatim).
180
+ api.registerMemoryCapability({
181
+ promptBuilder: (params) => buildPromptSection(params, { quota: deps.quota, pinned: deps.pinned }),
182
+ flushPlanResolver: buildFlushPlan,
183
+ runtime,
184
+ });
185
+
186
+ // (2) + (3) Register the two tools. The factories capture the SAME runtime
187
+ // (the captured-runtime design — see tools.ts header). The conventional
188
+ // tool names survive the tool-policy strip in OC 2026.5.x (issue #223):
189
+ // they are passed via the `names` opts so the SDK's name bookkeeping sees
190
+ // them even though the tool object's own `name` field is the visible
191
+ // registration name.
192
+ api.registerTool(() => createMemorySearchTool(runtime), { names: ['memory_search'] });
193
+ api.registerTool(() => createMemoryGetTool(runtime), { names: ['memory_get'] });
194
+
195
+ return runtime;
196
+ }
package/onboarding-cli.ts CHANGED
@@ -66,7 +66,8 @@ import {
66
66
  *
67
67
  * The phrase-print branch will be REMOVED in the next RC after rc.18.
68
68
  * Users running on a TTY can still complete the flow in rc.18; agents
69
- * MUST use `--pair-only` or the `totalreclaw_pair` tool today.
69
+ * MUST use `--pair-only` (or `tr pair --url-pin`) the legacy
70
+ * `totalreclaw_pair` agent tool was retired in Phase 3.2.
70
71
  */
71
72
  export const PHRASE_PRINT_DEPRECATION_WARNING =
72
73
  '\nDEPRECATION (issue #95): the interactive `openclaw totalreclaw onboard` flow\n' +
@@ -133,7 +134,7 @@ export const COPY = {
133
134
  '\nDone. Your phrase is saved at ~/.totalreclaw/credentials.json (mode 0600).\n' +
134
135
  'Memory tools are now active.\n\n' +
135
136
  ' Next: run `openclaw chat` to start. Existing memories tied to this\n' +
136
- ' phrase will be available via totalreclaw_recall.\n',
137
+ ' phrase are recalled automatically by the agent via memory_search.\n',
137
138
  skipped:
138
139
  '\nSkipped. Run `openclaw totalreclaw onboard` anytime to resume.\n' +
139
140
  'Memory tools remain disabled until you do.\n',
@@ -3,25 +3,13 @@
3
3
  "name": "TotalReclaw",
4
4
  "description": "End-to-end encrypted memory vault for AI agents",
5
5
  "kind": "memory",
6
+ "activation": {
7
+ "onStartup": false
8
+ },
6
9
  "contracts": {
7
10
  "tools": [
8
- "totalreclaw_remember",
9
- "totalreclaw_recall",
10
- "totalreclaw_forget",
11
- "totalreclaw_export",
12
- "totalreclaw_status",
13
- "totalreclaw_preload_embedder",
14
- "totalreclaw_consolidate",
15
- "totalreclaw_pin",
16
- "totalreclaw_unpin",
17
- "totalreclaw_retype",
18
- "totalreclaw_set_scope",
19
- "totalreclaw_import_from",
20
- "totalreclaw_import_batch",
21
- "totalreclaw_upgrade",
22
- "totalreclaw_migrate",
23
- "totalreclaw_pair",
24
- "totalreclaw_report_qa_bug"
11
+ "memory_search",
12
+ "memory_get"
25
13
  ]
26
14
  },
27
15
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totalreclaw/totalreclaw",
3
- "version": "3.3.12-rc.2",
3
+ "version": "3.3.12-rc.21",
4
4
  "description": "End-to-end encrypted, agent-portable memory for OpenClaw and any LLM-agent runtime. XChaCha20-Poly1305 with protobuf v4 + on-chain Memory Taxonomy v1 (claim / preference / directive / commitment / episode / summary).",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -32,8 +32,8 @@
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
34
  "@scure/bip39": "^2.2.0",
35
- "@totalreclaw/client": "^1.2.0",
36
- "@totalreclaw/core": "^2.1.1",
35
+ "@totalreclaw/client": "^1.3.0",
36
+ "@totalreclaw/core": "^2.5.3",
37
37
  "@types/qrcode": "^1.5.6",
38
38
  "@types/ws": "^8.5.12",
39
39
  "qrcode": "^1.5.4",
@@ -58,6 +58,7 @@
58
58
  "!**/*.test.ts",
59
59
  "!pocv2-e2e-test.ts",
60
60
  "openclaw.plugin.json",
61
+ "postinstall.mjs",
61
62
  "SKILL.md",
62
63
  "README.md",
63
64
  "CHANGELOG.md",
@@ -67,12 +68,14 @@
67
68
  "scripts": {
68
69
  "build": "rm -rf dist && tsc -p tsconfig.json --noCheck",
69
70
  "verify-tarball": "node ../scripts/verify-tarball.mjs",
70
- "test": "npx tsx manifest-shape.test.ts && npx tsx config-schema.test.ts && npx tsx config.test.ts && npx tsx relay-headers.test.ts && npx tsx scope-address-visible.test.ts && npx tsx llm-profile-reader.test.ts && npx tsx llm-client.test.ts && npx tsx llm-client-retry.test.ts && npx tsx gateway-url.test.ts && npx tsx retype-setscope.test.ts && npx tsx tool-gating.test.ts && npx tsx onboarding-noninteractive.test.ts && npx tsx pair-cli-json.test.ts && npx tsx pair-qr.test.ts && npx tsx pair-remote-client.test.ts && npx tsx qa-bug-report.test.ts && npx tsx nonce-serialization.test.ts && npx tsx phrase-safety-registry.test.ts && npx tsx test_issue_92_onnx_download_ux.test.ts && npx tsx onboard-pair-only.test.ts && npx tsx import-time-smoke.test.ts && npx tsx install-staging-cleanup.test.ts && npx tsx partial-install-detection.test.ts && npx tsx install-reload-idempotency.test.ts && npx tsx json-stdout-cleanliness.test.ts && npx tsx load-manifest.test.ts && npx tsx url-binding.test.ts && npx tsx fs-helpers.test.ts && npx tsx pair-cli-default-mode.test.ts && npx tsx embedding-fallback-tag.test.ts && npx tsx staging-banner-gate.test.ts && npx tsx restart-auth.test.ts && npx tsx inbound-user-tracker.test.ts && npx tsx register-command-name.test.ts && npx tsx skill-md-hybrid-primary.test.ts && npx tsx tr-cli-json-output.test.ts",
71
+ "test": "npx tsx batch-gate.test.ts && npx tsx manifest-shape.test.ts && npx tsx config-schema.test.ts && npx tsx config.test.ts && npx tsx relay-headers.test.ts && npx tsx scope-address-visible.test.ts && npx tsx llm-profile-reader.test.ts && npx tsx llm-client.test.ts && npx tsx llm-client-retry.test.ts && npx tsx llm-client-json-mode.test.ts && npx tsx gateway-url.test.ts && npx tsx retype-setscope.test.ts && npx tsx tool-gating.test.ts && npx tsx onboarding-noninteractive.test.ts && npx tsx pair-cli-json.test.ts && npx tsx pair-qr.test.ts && npx tsx pair-remote-client.test.ts && npx tsx pair-http.test.ts && npx tsx pair-http-route-registration.test.ts && npx tsx pair-http-init.test.ts && npx tsx qa-bug-report.test.ts && npx tsx nonce-serialization.test.ts && npx tsx initcode-lifecycle.test.ts && npx tsx phrase-safety-registry.test.ts && npx tsx test_issue_92_onnx_download_ux.test.ts && npx tsx onboard-pair-only.test.ts && npx tsx import-state.test.ts && npx tsx import-time-smoke.test.ts && npx tsx install-staging-cleanup.test.ts && npx tsx partial-install-detection.test.ts && npx tsx install-reload-idempotency.test.ts && npx tsx skill-register.test.ts && npx tsx json-stdout-cleanliness.test.ts && npx tsx url-binding.test.ts && npx tsx fs-helpers.test.ts && npx tsx pair-cli-default-mode.test.ts && npx tsx embedding-fallback-tag.test.ts && npx tsx staging-banner-gate.test.ts && npx tsx restart-auth.test.ts && npx tsx inbound-user-tracker.test.ts && npx tsx register-command-name.test.ts && npx tsx skill-md-native.test.ts &&npx tsx tr-cli-json-output.test.ts && npx tsx import-upgrade-cli.test.ts && npx tsx cli-registercli-scope.test.ts && npx tsx postinstall-validate.test.ts && npx tsx credential-provider.test.ts && npx tsx memory-runtime.test.ts && npx tsx tools.test.ts && npx tsx register-native.test.ts && npx tsx relay.test.ts && npx tsx vault-crypto.test.ts && npx tsx entry-env.test.ts && npx tsx trajectory-poller.test.ts && npx tsx billing-cache.test.ts",
71
72
  "smoke:dist": "npx tsx dist-esm-smoke.test.ts",
72
73
  "check-scanner": "node ../scripts/check-scanner.mjs",
73
74
  "check-version-drift": "node ../scripts/check-version-drift.mjs",
74
75
  "check-url-binding": "node ../scripts/check-url-binding.mjs",
75
76
  "sync-version": "node ../scripts/sync-version.mjs",
77
+ "preinstall": "node -e \"try{require('fs').writeFileSync('.tr-partial-install','');}catch{}\"",
78
+ "postinstall": "node ./postinstall.mjs",
76
79
  "prepack": "npm run build",
77
80
  "prepublishOnly": "node ../scripts/check-scanner.mjs && node ../scripts/check-version-drift.mjs && node ../scripts/check-url-binding.mjs --release-type=${TOTALRECLAW_RELEASE_TYPE:-rc}"
78
81
  },
package/pair-cli.ts CHANGED
@@ -487,7 +487,7 @@ export function registerPairCli(
487
487
  'gateway-loopback URLs for air-gapped setups.',
488
488
  )
489
489
  .option('--json', 'Emit a single JSON payload (url/pin/qr_ascii) instead of the human-readable banner. Enables agent-driven pairing.')
490
- .option('--url-pin-only', 'Emit ONLY {v,url,pin,expires_at_ms} — no QR ASCII, no SID, no mode echo. Headless fallback for container-based agents where the totalreclaw_pair tool is not injected (issue #87). Zero phrase exposure on stdout.')
490
+ .option('--url-pin-only', 'Emit ONLY {v,url,pin,expires_at_ms} — no QR ASCII, no SID, no mode echo. Headless fallback for container-based agents (issue #87; the legacy totalreclaw_pair agent tool was retired in Phase 3.2, so this is now the canonical agent-driven pair surface). Zero phrase exposure on stdout.')
491
491
  .option('--local', '(3.3.4-rc.1) Use the loopback / LAN URL flow instead of the relay. URLs point at this gateway\'s bound interface (e.g. http://localhost:18789/…) and require the user\'s browser to be on a reachable network. Default since rc.6 was relay; this flag preserves the air-gapped path.')
492
492
  .option('--timeout <sec>', 'Session TTL in seconds (default: 900 = 15 min, matches pair-session-store default)')
493
493
  .action(async (...args: unknown[]) => {