@rubytech/create-realagent-code 0.1.614 → 0.1.616

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 (197) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/aeo-llms-txt-writer/dist/__tests__/write-llms-txt.test.d.ts +2 -0
  3. package/payload/platform/lib/aeo-llms-txt-writer/dist/__tests__/write-llms-txt.test.d.ts.map +1 -0
  4. package/payload/platform/lib/aeo-llms-txt-writer/dist/__tests__/write-llms-txt.test.js +291 -0
  5. package/payload/platform/lib/aeo-llms-txt-writer/dist/__tests__/write-llms-txt.test.js.map +1 -0
  6. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.d.ts +13 -1
  7. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.d.ts.map +1 -1
  8. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.js +127 -18
  9. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.js.map +1 -1
  10. package/payload/platform/lib/aeo-llms-txt-writer/src/__tests__/write-llms-txt.test.ts +391 -0
  11. package/payload/platform/lib/aeo-llms-txt-writer/src/index.ts +156 -19
  12. package/payload/platform/lib/aeo-llms-txt-writer/vitest.config.ts +10 -0
  13. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +15 -1
  14. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts.map +1 -1
  15. package/payload/platform/lib/mcp-spawn-tee/dist/index.js +129 -7
  16. package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -1
  17. package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +227 -0
  18. package/payload/platform/lib/mcp-spawn-tee/src/index.ts +133 -6
  19. package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +129 -7
  20. package/payload/platform/plugins/admin/mcp/dist/index.js +11 -3
  21. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  22. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.d.ts +3 -0
  23. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.d.ts.map +1 -1
  24. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.js +38 -20
  25. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.js.map +1 -1
  26. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +6 -5
  27. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +3 -0
  28. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +13 -0
  29. package/payload/platform/plugins/aeo/PLUGIN.md +1 -1
  30. package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +129 -7
  31. package/payload/platform/plugins/aeo/mcp/dist/index.js +17 -10
  32. package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -1
  33. package/payload/platform/plugins/browser/PLUGIN.md +1 -1
  34. package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +129 -7
  35. package/payload/platform/plugins/connector/lib/mcp-spawn-tee/index.js +129 -7
  36. package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +129 -7
  37. package/payload/platform/plugins/dispatch/lib/mcp-spawn-tee/index.js +129 -7
  38. package/payload/platform/plugins/docs/references/aeo.md +4 -3
  39. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  40. package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +129 -7
  41. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +129 -7
  42. package/payload/platform/plugins/google/lib/mcp-spawn-tee/index.js +129 -7
  43. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +129 -7
  44. package/payload/platform/plugins/inventory/lib/mcp-spawn-tee/index.js +129 -7
  45. package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/index.js +129 -7
  46. package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +129 -7
  47. package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +129 -7
  48. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +129 -7
  49. package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +129 -7
  50. package/payload/platform/plugins/scheduling/PLUGIN.md +3 -4
  51. package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +129 -7
  52. package/payload/platform/plugins/scheduling/mcp/dist/index.js +0 -6
  53. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  54. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +65 -20
  55. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -1
  56. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-classification.test.js +20 -0
  57. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-classification.test.js.map +1 -1
  58. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +12 -4
  59. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -1
  60. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -1
  61. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
  62. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +39 -6
  63. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
  64. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js +21 -6
  65. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js.map +1 -1
  66. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +26 -5
  67. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  68. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-waiver.test.js +17 -51
  69. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-waiver.test.js.map +1 -1
  70. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +6 -4
  71. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
  72. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +113 -1
  73. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
  74. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +8 -18
  75. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  76. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +14 -27
  77. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  78. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
  79. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +125 -56
  80. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
  81. package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +129 -7
  82. package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +129 -7
  83. package/payload/platform/plugins/voice-mirror/lib/mcp-spawn-tee/index.js +129 -7
  84. package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +129 -7
  85. package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +129 -7
  86. package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +129 -7
  87. package/payload/platform/scripts/__tests__/vnc-cdp-liveness.test.sh +270 -19
  88. package/payload/platform/scripts/vnc.sh +174 -71
  89. package/payload/platform/services/claude-session-manager/dist/index.js +17 -0
  90. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/mcp-helper-census.d.ts +80 -0
  92. package/payload/platform/services/claude-session-manager/dist/mcp-helper-census.d.ts.map +1 -0
  93. package/payload/platform/services/claude-session-manager/dist/mcp-helper-census.js +201 -0
  94. package/payload/platform/services/claude-session-manager/dist/mcp-helper-census.js.map +1 -0
  95. package/payload/premium-plugins/real-agent/plugins/buyers/lib/mcp-spawn-tee/index.js +129 -7
  96. package/payload/premium-plugins/real-agent/plugins/estate-sales/lib/mcp-spawn-tee/index.js +129 -7
  97. package/payload/premium-plugins/real-agent/plugins/loop/PLUGIN.md +12 -0
  98. package/payload/premium-plugins/real-agent/plugins/loop/lib/mcp-spawn-tee/index.js +129 -7
  99. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/index.js +13 -0
  100. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/index.js.map +1 -1
  101. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/field-census.d.ts +22 -0
  102. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/field-census.d.ts.map +1 -0
  103. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/field-census.js +81 -0
  104. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/field-census.js.map +1 -0
  105. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.d.ts +17 -5
  106. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.d.ts.map +1 -1
  107. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.js +90 -17
  108. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.js.map +1 -1
  109. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-schema-keys.d.ts +2 -0
  110. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-schema-keys.d.ts.map +1 -0
  111. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-schema-keys.js +1010 -0
  112. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-schema-keys.js.map +1 -0
  113. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/auto-responder.js +2 -2
  114. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/auto-responder.js.map +1 -1
  115. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/customer-preferences.js +3 -3
  116. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/customer-preferences.js.map +1 -1
  117. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/feedback.d.ts +1 -0
  118. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/feedback.d.ts.map +1 -1
  119. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/feedback.js +5 -5
  120. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/feedback.js.map +1 -1
  121. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts +1 -0
  122. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts.map +1 -1
  123. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match-batch.js +2 -2
  124. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match-batch.js.map +1 -1
  125. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match.d.ts +1 -0
  126. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match.d.ts.map +1 -1
  127. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match.js +4 -4
  128. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-match.js.map +1 -1
  129. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-detail.d.ts +1 -0
  130. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-detail.d.ts.map +1 -1
  131. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-detail.js +3 -3
  132. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-detail.js.map +1 -1
  133. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-search.d.ts +1 -0
  134. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-search.d.ts.map +1 -1
  135. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-search.js +3 -3
  136. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/people-search.js.map +1 -1
  137. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-detail.d.ts +1 -0
  138. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-detail.d.ts.map +1 -1
  139. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-detail.js +13 -6
  140. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-detail.js.map +1 -1
  141. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.d.ts +1 -0
  142. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.d.ts.map +1 -1
  143. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.js +3 -3
  144. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.js.map +1 -1
  145. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-search.d.ts +1 -0
  146. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-search.d.ts.map +1 -1
  147. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-search.js +3 -3
  148. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-search.js.map +1 -1
  149. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.d.ts +1 -0
  150. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.d.ts.map +1 -1
  151. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.js +3 -3
  152. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.js.map +1 -1
  153. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/supplier.js +4 -4
  154. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/supplier.js.map +1 -1
  155. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-availability.d.ts +1 -0
  156. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-availability.d.ts.map +1 -1
  157. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-availability.js +3 -3
  158. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-availability.js.map +1 -1
  159. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-info.d.ts +1 -0
  160. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-info.d.ts.map +1 -1
  161. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-info.js +3 -3
  162. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/team-info.js.map +1 -1
  163. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-detail.d.ts +1 -0
  164. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-detail.d.ts.map +1 -1
  165. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-detail.js +3 -3
  166. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-detail.js.map +1 -1
  167. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-search.d.ts +1 -0
  168. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-search.d.ts.map +1 -1
  169. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-search.js +3 -3
  170. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/viewing-search.js.map +1 -1
  171. package/payload/premium-plugins/real-agent/plugins/loop/mcp/package.json +2 -1
  172. package/payload/premium-plugins/real-agent/plugins/loop/mcp/scripts/generate-loop-schema-keys.mjs +87 -0
  173. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/__tests__/new-tool-shape.test.ts +1 -1
  174. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/__tests__/read-fidelity.test.ts +303 -0
  175. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/__tests__/swagger-write-coverage.test.ts +6 -6
  176. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/index.ts +13 -0
  177. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/lib/field-census.ts +86 -0
  178. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/lib/loop-api.ts +116 -17
  179. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/lib/loop-schema-keys.ts +1010 -0
  180. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/auto-responder.ts +2 -2
  181. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/customer-preferences.ts +3 -3
  182. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/feedback.ts +6 -4
  183. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/marketing-match-batch.ts +4 -2
  184. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/marketing-match.ts +6 -4
  185. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/people-detail.ts +5 -3
  186. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/people-search.ts +5 -3
  187. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-detail.ts +17 -7
  188. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-listed.ts +5 -3
  189. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-search.ts +5 -3
  190. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-sold.ts +5 -3
  191. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/supplier.ts +4 -4
  192. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/team-availability.ts +5 -3
  193. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/team-info.ts +6 -3
  194. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/viewing-detail.ts +5 -3
  195. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/viewing-search.ts +5 -3
  196. package/payload/premium-plugins/real-agent/plugins/property-data/lib/mcp-spawn-tee/index.js +129 -7
  197. package/payload/server/server.js +422 -251
@@ -1,4 +1,11 @@
1
1
  import { selectLoopKeyStore } from "./key-store.js";
2
+ import { LOOP_SCHEMA_KEYS } from "./loop-schema-keys.js";
3
+ import {
4
+ censusRecords,
5
+ falsyPresentKeys,
6
+ matchPathPattern,
7
+ wireKeys,
8
+ } from "./field-census.js";
2
9
 
3
10
  // ─────────────────────────────────────────────────────────────────
4
11
  // Loop API V2 HTTP Client + Cross-Team Aggregation Engine
@@ -17,6 +24,12 @@ import { selectLoopKeyStore } from "./key-store.js";
17
24
  // ─────────────────────────────────────────────────────────────────
18
25
 
19
26
  const LOOP_BASE_URL = "https://api.loop.software";
27
+
28
+ // The Loop API version every path below targets. Declared once so tool output
29
+ // and log lines can both state which version a read actually reached. An agent
30
+ // reading either must never have to infer it from the shape of the paths: a
31
+ // capability audit did exactly that on 2026-08-18 and recorded v1.
32
+ export const LOOP_API_VERSION = "v2";
20
33
  const REQUEST_TIMEOUT_MS = 10_000;
21
34
  const TIMEOUT_RETRY_BACKOFF_MS = 2_000;
22
35
  const DEFAULT_LIMIT_PER_TEAM = 50;
@@ -139,14 +152,58 @@ function logErrorBody(
139
152
  );
140
153
  }
141
154
 
142
- /**
143
- * Make a GET request to the Loop API V2.
144
- */
155
+ // Two standing signals, both blind spots before this.
156
+ //
157
+ // field-census: a read that returned falsy-present keys while raw was off gave
158
+ // its caller a lossy answer. It fires on every read whether or not raw was
159
+ // asked for, because "nobody asked for raw" is exactly the failure being
160
+ // watched and it produces no event of its own. A log hung on the raw path
161
+ // would only ever record the reads that were already correct.
162
+ //
163
+ // schema-drift: Loop's own deploys change what arrives, which never reproduces
164
+ // on demand here. schemaOnly means Loop stopped sending documented fields;
165
+ // wireOnly means Loop added fields we have never seen. An unmatched path says
166
+ // so rather than skipping silently.
167
+ function emitReadSignals(toolName: string, path: string, data: unknown, raw: boolean): void {
168
+ const records = censusRecords(data);
169
+ const keys = wireKeys(data);
170
+ const falsy = falsyPresentKeys(data);
171
+ // falsyKeys unions across records, so it alone cannot separate one lossy row
172
+ // in two hundred from all two hundred. falsyRecords counts the rows carrying
173
+ // at least one, which is the difference between an edge case and a systemic
174
+ // gap in the operator's data.
175
+ const falsyRecords = records.filter((r) => falsyPresentKeys(r).length > 0).length;
176
+
177
+ console.error(
178
+ `[loop] op=field-census tool=${toolName} apiVersion=${LOOP_API_VERSION} ` +
179
+ `raw=${raw} records=${records.length} falsyRecords=${falsyRecords} ` +
180
+ `wireKeys=${keys.length} falsyKeys=${falsy.length ? falsy.join(",") : "-"}`
181
+ );
182
+
183
+ const patterns = Object.keys(LOOP_SCHEMA_KEYS);
184
+ const pattern = matchPathPattern(path, patterns);
185
+ if (!pattern) {
186
+ console.error(
187
+ `[loop] op=schema-drift tool=${toolName} apiVersion=${LOOP_API_VERSION} path=unmatched`
188
+ );
189
+ return;
190
+ }
191
+ const documented = LOOP_SCHEMA_KEYS[pattern];
192
+ const schemaOnly = documented.filter((k) => !keys.includes(k));
193
+ const wireOnly = keys.filter((k) => !documented.includes(k));
194
+ console.error(
195
+ `[loop] op=schema-drift tool=${toolName} apiVersion=${LOOP_API_VERSION} path=${pattern} ` +
196
+ `schemaOnly=${schemaOnly.length ? schemaOnly.join(",") : "-"} ` +
197
+ `wireOnly=${wireOnly.length ? wireOnly.join(",") : "-"}`
198
+ );
199
+ }
200
+
145
201
  export async function loopGet<T>(
146
202
  apiKey: string,
147
203
  path: string,
148
204
  toolName: string,
149
- teamName: string
205
+ teamName: string,
206
+ opts: { raw?: boolean } = {}
150
207
  ): Promise<T> {
151
208
  const url = `${LOOP_BASE_URL}${path}`;
152
209
  const start = Date.now();
@@ -158,7 +215,8 @@ export async function loopGet<T>(
158
215
 
159
216
  const duration = Date.now() - start;
160
217
  console.error(
161
- `[loop] ${toolName} team=${teamName} endpoint=GET ${path} status=${response.status} duration=${duration}ms`
218
+ `[loop] op=request tool=${toolName} apiVersion=${LOOP_API_VERSION} team=${teamName} ` +
219
+ `method=GET path=${path} status=${response.status} duration=${duration}ms`
162
220
  );
163
221
 
164
222
  if (response.status === 204) {
@@ -172,9 +230,10 @@ export async function loopGet<T>(
172
230
  }
173
231
 
174
232
  const text = await response.text();
233
+ let data: T;
175
234
  try {
176
235
  const parsed = JSON.parse(text);
177
- return unwrapEnvelope<T>(parsed, toolName, teamName);
236
+ data = unwrapEnvelope<T>(parsed, toolName, teamName);
178
237
  } catch (err) {
179
238
  if (err instanceof SyntaxError) {
180
239
  throw new Error(
@@ -184,6 +243,13 @@ export async function loopGet<T>(
184
243
  }
185
244
  throw err;
186
245
  }
246
+
247
+ // Outside the parse guard on purpose. Inside it, any throw from
248
+ // instrumentation was caught by a handler written for parse failures and
249
+ // re-thrown, which drops the whole team from the aggregate and blames Loop
250
+ // for a telemetry bug. Observability must never fail the read it observes.
251
+ emitReadSignals(toolName, path, data, opts.raw === true);
252
+ return data;
187
253
  }
188
254
 
189
255
  /**
@@ -214,7 +280,8 @@ export async function loopPost<T>(
214
280
 
215
281
  const duration = Date.now() - start;
216
282
  console.error(
217
- `[loop] WRITE ${toolName} team=${teamName} endpoint=POST ${path} status=${response.status} duration=${duration}ms`
283
+ `[loop] op=request kind=write tool=${toolName} apiVersion=${LOOP_API_VERSION} team=${teamName} ` +
284
+ `method=POST path=${path} status=${response.status} duration=${duration}ms`
218
285
  );
219
286
 
220
287
  if (response.status === 204) {
@@ -269,7 +336,8 @@ export async function loopPut<T>(
269
336
 
270
337
  const duration = Date.now() - start;
271
338
  console.error(
272
- `[loop] WRITE ${toolName} team=${teamName} endpoint=PUT ${path} status=${response.status} duration=${duration}ms`
339
+ `[loop] op=request kind=write tool=${toolName} apiVersion=${LOOP_API_VERSION} team=${teamName} ` +
340
+ `method=PUT path=${path} status=${response.status} duration=${duration}ms`
273
341
  );
274
342
 
275
343
  if (response.status === 204) {
@@ -435,11 +503,11 @@ export async function aggregateAcrossTeams<T>(
435
503
  const erroredCount = failures.length - timedOutCount;
436
504
 
437
505
  console.error(
438
- `[loop] aggregate tool=${toolName} teams=${keys.length} ` +
506
+ `[loop] aggregate tool=${toolName} apiVersion=${LOOP_API_VERSION} teams=${keys.length} ` +
439
507
  `succeeded=${results.length} failed=${failures.length} skipped=${skipped.length}`
440
508
  );
441
509
  console.error(
442
- `[loop] aggregate-coverage tool=${toolName} requested=${keys.length} ` +
510
+ `[loop] aggregate-coverage tool=${toolName} apiVersion=${LOOP_API_VERSION} requested=${keys.length} ` +
443
511
  `answered=${results.length} timed_out=${timedOutCount} errored=${erroredCount}`
444
512
  );
445
513
 
@@ -482,13 +550,37 @@ export async function withTeamKey<T>(
482
550
  /**
483
551
  * Format an aggregation result into a human-readable text response.
484
552
  */
553
+ /**
554
+ * Version header and raw rendering for the reads that do NOT aggregate across
555
+ * teams. `withTeamKey` tools format their own prose, so without this they were
556
+ * the four Loop reads that named no version and offered no way to see the
557
+ * field set — the exact defect this module exists to close, surviving in the
558
+ * corner the aggregation path does not cover.
559
+ */
560
+ export function formatSingleTeamRead(
561
+ data: unknown,
562
+ prose: string,
563
+ opts: { raw?: boolean } = {}
564
+ ): string {
565
+ const version = `Loop API ${LOOP_API_VERSION}`;
566
+ if (opts.raw === true) {
567
+ return `[${version}]\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\``;
568
+ }
569
+ return `[${version}] ${prose}`;
570
+ }
571
+
485
572
  export function formatAggregationResult<T>(
486
573
  result: AggregationResult<T>,
487
574
  formatItem: (item: T, teamName: string) => string,
488
- entityName: string
575
+ entityName: string,
576
+ opts: { raw?: boolean } = {}
489
577
  ): string {
578
+ // Every branch states the version the read reached. An agent must be able to
579
+ // answer "which version did I just read" from the output alone.
580
+ const version = `Loop API ${LOOP_API_VERSION}`;
581
+
490
582
  if (result.totalTeams === 0) {
491
- return "No Loop teams registered. Use loop-key-register to add team API keys.";
583
+ return `[${version}] No Loop teams registered. Use loop-key-register to add team API keys.`;
492
584
  }
493
585
 
494
586
  const allSkippedOrFailed =
@@ -499,7 +591,7 @@ export function formatAggregationResult<T>(
499
591
  ...result.failures.map((f) => `${f.teamName}: ${f.reason}`),
500
592
  ...result.skipped.map((s) => `${s.teamName}: ${s.reason}`),
501
593
  ];
502
- return `All teams failed or were skipped:\n${reasons.join("\n")}`;
594
+ return `[${version}] All teams failed or were skipped:\n${reasons.join("\n")}`;
503
595
  }
504
596
 
505
597
  const lines: string[] = [];
@@ -510,7 +602,14 @@ export function formatAggregationResult<T>(
510
602
  totalItems += teamResult.data.length;
511
603
  lines.push(`\n## ${teamResult.teamName}`);
512
604
  for (const item of teamResult.data) {
513
- lines.push(formatItem(item, teamResult.teamName));
605
+ // Raw serialises the wire payload, not the interface, so a field no tool
606
+ // declares still appears — and 0, false and "" survive, which is the
607
+ // whole point: prose drops them and makes a zero look like an absence.
608
+ lines.push(
609
+ opts.raw === true
610
+ ? "```json\n" + JSON.stringify(item, null, 2) + "\n```"
611
+ : formatItem(item, teamResult.teamName)
612
+ );
514
613
  }
515
614
  }
516
615
 
@@ -523,18 +622,18 @@ export function formatAggregationResult<T>(
523
622
 
524
623
  if (totalItems === 0) {
525
624
  if (failed > 0) {
526
- return `No ${entityName} found across ${answered} of ${totalCovered} teams (${coverageNote}).`;
625
+ return `[${version}] No ${entityName} found across ${answered} of ${totalCovered} teams (${coverageNote}).`;
527
626
  }
528
627
  const teamCount =
529
628
  result.results.length === 1
530
629
  ? result.results[0].teamName
531
630
  : `${result.results.length} teams`;
532
- return `No ${entityName} found across ${teamCount}.`;
631
+ return `[${version}] No ${entityName} found across ${teamCount}.`;
533
632
  }
534
633
 
535
634
  const teamCountFragment =
536
635
  failed > 0 ? `${answered} of ${totalCovered} team(s)` : `${answered} team(s)`;
537
- let header = `${totalItems} ${entityName} across ${teamCountFragment}:`;
636
+ let header = `[${version}] ${totalItems} ${entityName} across ${teamCountFragment}:`;
538
637
  const parts: string[] = [header, ...lines];
539
638
 
540
639
  if (failed > 0) {