@rubytech/create-realagent-code 0.1.175 → 0.1.179

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 (36) hide show
  1. package/dist/index.js +13 -4
  2. package/package.json +1 -1
  3. package/payload/platform/plugins/email/skills/email-composition/SKILL.md +1 -1
  4. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +38 -3
  5. package/payload/platform/plugins/memory/mcp/dist/index.js +17 -7
  6. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  7. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.d.ts +8 -0
  8. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.d.ts.map +1 -0
  9. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.js +27 -0
  10. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.js.map +1 -0
  11. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts +15 -2
  12. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts.map +1 -1
  13. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.d.ts +2 -0
  14. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.d.ts.map +1 -0
  15. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js +95 -0
  16. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js.map +1 -0
  17. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.d.ts +2 -0
  18. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.d.ts.map +1 -0
  19. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.js +124 -0
  20. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.js.map +1 -0
  21. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.d.ts +2 -0
  22. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.d.ts.map +1 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js +104 -0
  24. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js.map +1 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts +12 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts.map +1 -1
  27. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js +38 -13
  28. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js.map +1 -1
  29. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +23 -0
  30. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  31. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +234 -20
  32. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  33. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +7 -3
  34. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +12 -3
  35. package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +1 -0
  36. package/payload/premium-plugins/real-agent/plugins/brochures/skills/property-brochure/SKILL.md +1 -1
@@ -112,7 +112,6 @@ You produce typed-section JSON yourself from the cached text and the loaded onto
112
112
  {
113
113
  "kind": "<from closed enumeration>",
114
114
  "title": "<short title>",
115
- "body": "<documentText.slice(sourceStart, sourceEnd)>",
116
115
  "summary": "<≤500 chars>",
117
116
  "sourceStart": <int>,
118
117
  "sourceEnd": <int>,
@@ -127,7 +126,7 @@ You produce typed-section JSON yourself from the cached text and the loaded onto
127
126
  }
128
127
  ```
129
128
 
130
- Use the natural-edge map below to pick `anchorEdge` per kind. `body` is the verbatim slice of the cached text between `sourceStart` (inclusive) and `sourceEnd` (exclusive). `summary` ≤ 500 chars.
129
+ Use the natural-edge map below to pick `anchorEdge` per kind. The writer derives `:Section.body` from `cachedText.slice(sourceStart, sourceEnd)` you emit offsets, not body text. Any `body` field you supply is logged and ignored. `summary` ≤ 500 chars.
131
130
 
132
131
  ```
133
132
  Section kind | Anchor edge (direction = from-anchor unless noted)
@@ -148,7 +147,7 @@ Other (label fallback) | anchorEdge: null — section-kind closure mis
148
147
 
149
148
  `orphanCandidates` is the list of entities you wanted to emit but could not natural-edge. Surface them so the ontology can grow.
150
149
 
151
- **Chat mode.** Same top-level shape with every `sections[].kind = "Conversation"` and per-chunk properties: `summary`, `keywords`, `firstMessageAt`, `lastMessageAt`, `participantNames`, `messageCount`. Cover every message in chronological order with no gaps. Drop `anchorEdge` (always null) and `related`. Use the participant elementIds from § Participant confirmation; do not invent new ones.
150
+ **Chat mode.** Same top-level shape with every `sections[].kind = "Conversation"`. `summary` is the per-section top-level field (already required by the schema) — the writer reads it from there and stamps `:Section.summary`. The `properties` bag is for non-authoritative per-chunk metadata only: `keywords`, `firstMessageAt`, `lastMessageAt`, `participantNames`, `messageCount`. The writer strips reserved writer-owned keys (`summary`, `title`, `body`, `bodyPreview`, `embedding`, provenance fields, etc.) from `properties` and logs the attempt — so duplicating an authoritative field into `properties` is a noisy no-op. Cover every message in chronological order with no gaps. Drop `anchorEdge` (always null) and `related`. Use the participant elementIds from § Participant confirmation; do not invent new ones.
152
151
 
153
152
  **Chunking — long inputs.** For inputs > ~525K chars, work through the cached text in deterministic ~525K-char windows with ~17.5K-char overlap, emit sections per window, and merge same-kind boundary straddlers yourself before calling `memory-ingest`. Inputs > 682K chars in chat mode are too large to keep within one turn — abort with `cause:"input-too-large"` and ask the operator to sessionize the export before re-dispatch.
154
153
 
@@ -247,6 +246,16 @@ The operator always sees what was attempted and where it stopped.
247
246
 
248
247
  For URL-based ingestion, run `memory-ingest-web` first with the URL and the WebFetch result. It writes the content to a temp file and routes through `memory-ingest-extract`, returning a fresh `attachmentId`. The rest of the pipeline (classify + ingest) is identical to a file upload. Pass `sourceUrl` and `sourceType: 'web'` to `memory-ingest`.
249
248
 
249
+ **Page title is mandatory input — parse it before the call.** `KnowledgeDocument.name` is what shows up everywhere the document is referenced (`pickShortLabel` / `pickDisplayName` check `name` first), so it must be an editorial title, never body prose. Before calling `memory-ingest-web`, scan the WebFetch result for the page's title and pass it as the `title` parameter:
250
+
251
+ - The readability output often starts with the page's `<title>` or `og:title` as a literal H1 (e.g. `# I am no longer the Official Representative for Reform UK`). Use that line verbatim — strip the leading `# ` and any trailing site name suffix (e.g. ` - Substack`, ` | Medium`).
252
+ - When the readability output strips the title entirely (rare; some publishers prefix the body with marketing chrome), look at the URL's final path segment — `…/i-am-no-longer-the-official-representative` is the slug the publisher chose for the article.
253
+ - Never invent a title from the article's opening paragraph. Body prose is exactly what this parameter exists to avoid.
254
+
255
+ The server's fallback chain when `title` is absent: markdown H1 in the cached text → URL-derived slug → the URL itself. The librarian's job is to make the `caller` branch the common case; the server-side fallbacks are last-resort, not the default path. The response includes `titleSource` (`caller` / `h1` / `url-slug` / `url`) so the operator can see which path fired.
256
+
257
+ **WebFetch limitation — subscriber-gated and JS-rendered pages (Substack, Medium-locked, paywalled news, gated PDFs, single-page apps).** `WebFetch` retrieves only the public, server-rendered surface. For gated articles the return is the meta-description / preview blurb / third-person summary that the publisher exposes to scrapers — not the author's original prose. Verbatim text is unrecoverable through this path. **Detect this before ingesting:** if the WebFetch result looks like a third-person synopsis of an article whose author would have written in first person, or carries publisher-summary framing (`# <Title> - Full Article Text` headers, marketing blurbs, "Subscribe to read"), abort the ingest and surface the gap to the dispatching agent in one line: *"WebFetch for `<url>` returned a publisher summary, not the article body. Ask the operator for a different input shape — for a Substack URL on the operator's own publication, use the `substack-import` plugin's export-archive flow (Substack admin → 'Export your data' → drop the ZIP); otherwise paste the article text into chat or attach a PDF / .txt export."* The dispatching agent relays this to the operator and re-dispatches with the pasted text, uploaded file, or Substack archive as the input. Do NOT proceed to `memory-ingest-web` with the summary — `:Section.body` will faithfully reproduce that summary downstream, defeating the verbatim-prose contract.
258
+
250
259
  ## Attachment metadata format
251
260
 
252
261
  Your task description contains attachment metadata in this format:
@@ -179,4 +179,5 @@ Audit query: `MATCH ()-[r]->() WHERE r.createdByAgent='substack-import' RETURN D
179
179
  - **Comments.** Substack export does not include reader comments.
180
180
  - **Inbound newsletter ingestion.** A Substack post landing in the operator's Gmail goes through `conversation-archive --source email` (Task 426), not this plugin.
181
181
  - **Live API or RSS poll.** Operator-dropped export only.
182
+ - **Single-URL ingestion via `WebFetch`.** A Substack URL passed to `document-ingest` via `memory-ingest-web` will NOT recover the article's verbatim prose for gated / paywalled / subscriber-only posts — `WebFetch` retrieves only the public, server-rendered surface (meta-description, preview blurb, third-person publisher summary). Even for free public posts, JS-rendering on Substack means the WebFetch return is unreliable for verbatim capture. **If the operator hands over a Substack URL, advise:** (a) for the operator's own publication — run the export-archive flow this skill owns (Substack admin → "Export your data" → drop the ZIP); (b) for a third-party publication — paste the article text into chat, or save the page as PDF / `.txt` and attach the file. Then re-dispatch the librarian on the pasted text or attached file. Do not attempt URL-based ingestion as a fallback — the prose will be lost. (Recurring failure surfaced on session `d725df52` 2026-05-27; see `.tasks/465-memory-ingest-body-server-sliced-from-offsets.md` and the WebFetch-limitation note in `document-ingest/SKILL.md`.)
182
183
  - **Audio / video enclosures.** Substack podcast / video posts use additional file shapes the v1 plugin does not handle; only images referenced from post-body `<img>` tags are enclosed.
@@ -95,7 +95,7 @@ If the operator did **not** request socials, do not run the step; adding 1 to 3
95
95
 
96
96
  ## Step 4 — populate, render, deliver
97
97
 
98
- **Before writing any copy, call `voice-retrieve-conditioning`** with `format: "long"` and `topic` set to the property's `addressLocality` or the marketing angle (e.g. "period townhouse in Hackney"). When the tool returns a non-null `styleCard`, condition the long-form narrative copy on it — the style card + exemplars feed the editorial copy register; brand-required structure rules still override. When the tool returns `{styleCard: null, exemplars: []}` (no operator voice profile yet, or `writer-craft` not enabled), fall back to the unconditioned register documented in `references/copy.md`. Emit narration `[property-brochure] voice-mirror styleCardBytes=<n> exemplars=<k>` when conditioning was applied.
98
+ **Before writing any copy, call `voice-retrieve-conditioning`** with `format: "article"`, `length: "long"`, and `topic` set to the property's `addressLocality` or the marketing angle (e.g. "period townhouse in Hackney"). When the tool returns a non-null `styleCard`, condition the long-form narrative copy on it — the style card + exemplars feed the editorial copy register; brand-required structure rules still override. When the tool returns `{styleCard: null, exemplars: []}` (no operator voice profile yet, or `writer-craft` not enabled), fall back to the unconditioned register documented in `references/copy.md`. Emit narration `[property-brochure] voice-mirror styleCardBytes=<n> exemplars=<k>` when conditioning was applied.
99
99
 
100
100
  Read the seller brief once before writing any copy. Then walk the `{{ placeholder }}` tokens in both files top-to-bottom, substituting from the brief and `property.json`. The complete substitution contract — every token, its source field, its expected format and voice — lives in [references/placeholders.md](references/placeholders.md). Each token also has an inline `<!-- REPLACE: ... -->` comment in the template giving the short version.
101
101