@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
package/llm-client.ts CHANGED
@@ -801,6 +801,48 @@ export function isRetryable(errorMessage: string): boolean {
801
801
  // OpenAI-compatible chat completion
802
802
  // ---------------------------------------------------------------------------
803
803
 
804
+ /**
805
+ * Provider base-URL hints for OpenAI-compatible endpoints that honour the
806
+ * `response_format: {"type": "json_object"}` body field. Sending the field
807
+ * to a provider that supports it makes JSON output deterministic; sending
808
+ * it to a provider that does NOT recognise the field is a 400.
809
+ *
810
+ * Why this exists (3.3.12-rc.6, 2026-05-09):
811
+ * z.ai's GLM family (4.5-flash, 5-turbo, 5.1) silently returns EMPTY
812
+ * `message.content` for the merged-extraction prompt unless this hint
813
+ * is set. No error, no warning — the LLM just emits "" instead of the
814
+ * expected `{"topics": [], "facts": []}` JSON. Plugin's parse step
815
+ * then logs `0 raw facts` from a successful-but-empty branch.
816
+ *
817
+ * This bug was found and fixed on the Python (Hermes) side in
818
+ * 2.3.1-rc.23 (see `python/src/totalreclaw/agent/llm_client.py`
819
+ * `_supports_json_object_response_format`) but the plugin TS port did
820
+ * not carry the fix — observed in plugin 3.3.12-rc.5 auto-QA on
821
+ * 2026-05-09: hook + poller both fired correctly but extraction
822
+ * returned 0 facts on every batch despite trajectories containing
823
+ * explicit "I prefer X" / "I work at Y" statements.
824
+ *
825
+ * Mirror of Python's `_supports_json_object_response_format`. Match by
826
+ * substring on a lowercased baseUrl so cosmetic prefix differences
827
+ * (https://, /v1, etc.) don't matter.
828
+ */
829
+ const JSON_OBJECT_PROVIDER_HINTS = [
830
+ 'z.ai',
831
+ 'api.openai.com',
832
+ 'groq.com',
833
+ 'openrouter.ai',
834
+ 'deepseek.com',
835
+ 'mistral.ai',
836
+ 'x.ai',
837
+ 'together.xyz',
838
+ ] as const;
839
+
840
+ export function supportsJsonObjectResponseFormat(baseUrl: string | undefined): boolean {
841
+ if (!baseUrl) return false;
842
+ const lower = baseUrl.toLowerCase();
843
+ return JSON_OBJECT_PROVIDER_HINTS.some((h) => lower.includes(h));
844
+ }
845
+
804
846
  async function chatCompletionOpenAI(
805
847
  config: LLMClientConfig,
806
848
  messages: ChatMessage[],
@@ -817,6 +859,13 @@ async function chatCompletionOpenAI(
817
859
  max_completion_tokens: maxTokens,
818
860
  };
819
861
 
862
+ // 3.3.12-rc.6: hint the provider to return strict JSON. Critical for
863
+ // z.ai/GLM (silent-empty without it). See JSON_OBJECT_PROVIDER_HINTS
864
+ // doc above.
865
+ if (supportsJsonObjectResponseFormat(config.baseUrl)) {
866
+ body.response_format = { type: 'json_object' };
867
+ }
868
+
820
869
  try {
821
870
  const res = await fetch(url, {
822
871
  method: 'POST',
@@ -834,7 +883,31 @@ async function chatCompletionOpenAI(
834
883
  }
835
884
 
836
885
  const json = (await res.json()) as ChatCompletionResponse;
837
- return json.choices?.[0]?.message?.content ?? null;
886
+ const content = json.choices?.[0]?.message?.content ?? null;
887
+
888
+ // 3.3.12-rc.6: loud-on-empty. If the provider returned a 200 with
889
+ // empty content, this almost always means a missing response_format
890
+ // hint or a content-filter. Without this log the silent-empty
891
+ // failure mode (Python rc.23 / plugin rc.5) is invisible to ops.
892
+ if (content === '' || content === null) {
893
+ // Lazy import to avoid circular dep with the registered logger.
894
+ // Fall back to console.warn if logger unavailable.
895
+ const warn = (msg: string) => {
896
+ try {
897
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
898
+ (globalThis as any)?.console?.warn?.(msg);
899
+ } catch {
900
+ /* noop */
901
+ }
902
+ };
903
+ warn(
904
+ `[totalreclaw][llm-client] provider=${config.baseUrl} model=${config.model} ` +
905
+ `returned empty content (status=200). ` +
906
+ `If using z.ai/GLM/OpenAI-compat, check response_format hint is being sent.`,
907
+ );
908
+ }
909
+
910
+ return content;
838
911
  } catch (err) {
839
912
  const msg = err instanceof Error ? err.message : String(err);
840
913
  throw new Error(`LLM call failed: ${msg}`);